@tldraw/editor 5.3.0-next.d7d8ced023d5 → 5.3.0-next.dec36c5c2930

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 (47) hide show
  1. package/dist-cjs/index.d.ts +57 -3
  2. package/dist-cjs/index.js +4 -3
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +24 -19
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/EditorPortal.js +44 -0
  7. package/dist-cjs/lib/components/EditorPortal.js.map +7 -0
  8. package/dist-cjs/lib/components/LiveCollaborators.js +7 -3
  9. package/dist-cjs/lib/components/LiveCollaborators.js.map +2 -2
  10. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +4 -4
  11. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
  12. package/dist-cjs/lib/license/LicenseManager.js +3 -0
  13. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  14. package/dist-cjs/version.js +3 -3
  15. package/dist-cjs/version.js.map +1 -1
  16. package/dist-esm/index.d.mts +57 -3
  17. package/dist-esm/index.mjs +7 -3
  18. package/dist-esm/index.mjs.map +2 -2
  19. package/dist-esm/lib/TldrawEditor.mjs +23 -18
  20. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  21. package/dist-esm/lib/components/EditorPortal.mjs +24 -0
  22. package/dist-esm/lib/components/EditorPortal.mjs.map +7 -0
  23. package/dist-esm/lib/components/LiveCollaborators.mjs +8 -4
  24. package/dist-esm/lib/components/LiveCollaborators.mjs.map +2 -2
  25. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +5 -5
  26. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
  27. package/dist-esm/lib/license/LicenseManager.mjs +3 -0
  28. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  29. package/dist-esm/version.mjs +3 -3
  30. package/dist-esm/version.mjs.map +1 -1
  31. package/editor.css +6 -0
  32. package/package.json +7 -7
  33. package/src/index.ts +5 -1
  34. package/src/lib/TldrawEditor.tsx +21 -13
  35. package/src/lib/components/EditorPortal.tsx +77 -0
  36. package/src/lib/components/LiveCollaborators.tsx +20 -6
  37. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.test.ts +46 -1
  38. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +10 -5
  39. package/src/lib/license/LicenseManager.test.ts +25 -0
  40. package/src/lib/license/LicenseManager.ts +7 -0
  41. package/src/lib/test/EditorPortal.test.tsx +48 -0
  42. package/src/version.ts +3 -3
  43. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js +0 -51
  44. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +0 -7
  45. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs +0 -31
  46. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +0 -7
  47. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +0 -32
@@ -0,0 +1,48 @@
1
+ import { act, render, screen } from '@testing-library/react'
2
+ import { EditorPortal } from '../components/EditorPortal'
3
+ import { createTLStore } from '../config/createTLStore'
4
+ import { TL_CONTAINER_CLASS, TldrawEditor } from '../TldrawEditor'
5
+
6
+ /** Mounted in a canvas slot — the deep-in-the-tree case the portal exists for. */
7
+ function Portaled() {
8
+ return (
9
+ <EditorPortal>
10
+ <div data-testid="portaled" />
11
+ </EditorPortal>
12
+ )
13
+ }
14
+
15
+ describe('EditorPortal', () => {
16
+ async function renderEditor() {
17
+ const store = createTLStore({ shapeUtils: [], bindingUtils: [] })
18
+ await act(async () => {
19
+ render(<TldrawEditor store={store} components={{ InFrontOfTheCanvas: Portaled }} />)
20
+ })
21
+ return document.querySelector(`.${TL_CONTAINER_CLASS}`)!
22
+ }
23
+
24
+ it('renders its children into the editor container', async () => {
25
+ const container = await renderEditor()
26
+ expect(container.contains(screen.getByTestId('portaled'))).toBe(true)
27
+ })
28
+
29
+ it('renders into a host that comes after everything the editor renders', async () => {
30
+ const container = await renderEditor()
31
+ const host = container.querySelector('.tl-portal-host')!
32
+ expect(host.contains(screen.getByTestId('portaled'))).toBe(true)
33
+ // Last of the container's rendered children. Managers append their own nodes after this one
34
+ // (the text measurement element, say) but those are `tabIndex = -1` and out of the tab order.
35
+ const rendered = [...container.children].filter((el) => !el.matches('.tl-text-measure'))
36
+ expect(rendered.at(-1)).toBe(host)
37
+ })
38
+
39
+ // The tab order guarantee: mounted in a canvas slot, the portaled content still lands after
40
+ // everything the editor renders itself — including (in tldraw) the UI's skip link, which only
41
+ // works while nothing precedes it. Portaling straight into the container lands it ahead of them.
42
+ it('places its children after the rest of the editor in document order', async () => {
43
+ await renderEditor()
44
+ const portaled = screen.getByTestId('portaled')
45
+ const canvas = screen.getByTestId('canvas')
46
+ expect(canvas.compareDocumentPosition(portaled) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy()
47
+ })
48
+ })
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.3.0-next.d7d8ced023d5'
4
+ export const version = '5.3.0-next.dec36c5c2930'
5
5
  export const publishDates = {
6
6
  major: '2026-05-06T16:28:18.473Z',
7
- minor: '2026-08-03T09:27:05.718Z',
8
- patch: '2026-08-03T09:27:05.718Z',
7
+ minor: '2026-08-05T10:19:16.457Z',
8
+ patch: '2026-08-05T10:19:16.457Z',
9
9
  }
@@ -1,51 +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 usePassThroughMouseOverEvents_exports = {};
20
- __export(usePassThroughMouseOverEvents_exports, {
21
- usePassThroughMouseOverEvents: () => usePassThroughMouseOverEvents
22
- });
23
- module.exports = __toCommonJS(usePassThroughMouseOverEvents_exports);
24
- var import_react = require("react");
25
- var import_dom = require("../utils/dom");
26
- var import_useContainer = require("./useContainer");
27
- var import_useEditor = require("./useEditor");
28
- function usePassThroughMouseOverEvents(ref) {
29
- if (!ref) throw Error("usePassThroughWheelEvents must be passed a ref");
30
- const container = (0, import_useContainer.useContainer)();
31
- const editor = (0, import_useEditor.useMaybeEditor)();
32
- (0, import_react.useEffect)(() => {
33
- function onMouseOver(e) {
34
- if (!editor?.getInstanceState().isFocused) return;
35
- if (e.isSpecialRedispatchedEvent) return;
36
- (0, import_dom.preventDefault)(e);
37
- const cvs = container.querySelector(".tl-canvas");
38
- if (!cvs) return;
39
- const newEvent = new PointerEvent(e.type, e);
40
- newEvent.isSpecialRedispatchedEvent = true;
41
- cvs.dispatchEvent(newEvent);
42
- }
43
- const elm = ref.current;
44
- if (!elm) return;
45
- elm.addEventListener("mouseover", onMouseOver, { passive: false });
46
- return () => {
47
- elm.removeEventListener("mouseover", onMouseOver);
48
- };
49
- }, [container, editor, ref]);
50
- }
51
- //# sourceMappingURL=usePassThroughMouseOverEvents.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/lib/hooks/usePassThroughMouseOverEvents.ts"],
4
- "sourcesContent": ["import { RefObject, useEffect } from 'react'\nimport { preventDefault } from '../utils/dom'\nimport { useContainer } from './useContainer'\nimport { useMaybeEditor } from './useEditor'\n\n/** @public */\nexport function usePassThroughMouseOverEvents(ref: RefObject<HTMLElement | null>) {\n\tif (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')\n\tconst container = useContainer()\n\tconst editor = useMaybeEditor()\n\n\tuseEffect(() => {\n\t\tfunction onMouseOver(e: MouseEvent) {\n\t\t\tif (!editor?.getInstanceState().isFocused) return\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\t\t\tpreventDefault(e)\n\t\t\tconst cvs = container.querySelector('.tl-canvas')\n\t\t\tif (!cvs) return\n\t\t\tconst newEvent = new PointerEvent(e.type, e as any)\n\t\t\t;(newEvent as any).isSpecialRedispatchedEvent = true\n\t\t\tcvs.dispatchEvent(newEvent)\n\t\t}\n\n\t\tconst elm = ref.current\n\t\tif (!elm) return\n\n\t\telm.addEventListener('mouseover', onMouseOver, { passive: false })\n\t\treturn () => {\n\t\t\telm.removeEventListener('mouseover', onMouseOver)\n\t\t}\n\t}, [container, editor, ref])\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAqC;AACrC,iBAA+B;AAC/B,0BAA6B;AAC7B,uBAA+B;AAGxB,SAAS,8BAA8B,KAAoC;AACjF,MAAI,CAAC,IAAK,OAAM,MAAM,gDAAgD;AACtE,QAAM,gBAAY,kCAAa;AAC/B,QAAM,aAAS,iCAAe;AAE9B,8BAAU,MAAM;AACf,aAAS,YAAY,GAAe;AACnC,UAAI,CAAC,QAAQ,iBAAiB,EAAE,UAAW;AAC3C,UAAK,EAAU,2BAA4B;AAC3C,qCAAe,CAAC;AAChB,YAAM,MAAM,UAAU,cAAc,YAAY;AAChD,UAAI,CAAC,IAAK;AACV,YAAM,WAAW,IAAI,aAAa,EAAE,MAAM,CAAQ;AACjD,MAAC,SAAiB,6BAA6B;AAChD,UAAI,cAAc,QAAQ;AAAA,IAC3B;AAEA,UAAM,MAAM,IAAI;AAChB,QAAI,CAAC,IAAK;AAEV,QAAI,iBAAiB,aAAa,aAAa,EAAE,SAAS,MAAM,CAAC;AACjE,WAAO,MAAM;AACZ,UAAI,oBAAoB,aAAa,WAAW;AAAA,IACjD;AAAA,EACD,GAAG,CAAC,WAAW,QAAQ,GAAG,CAAC;AAC5B;",
6
- "names": []
7
- }
@@ -1,31 +0,0 @@
1
- import { useEffect } from "react";
2
- import { preventDefault } from "../utils/dom.mjs";
3
- import { useContainer } from "./useContainer.mjs";
4
- import { useMaybeEditor } from "./useEditor.mjs";
5
- function usePassThroughMouseOverEvents(ref) {
6
- if (!ref) throw Error("usePassThroughWheelEvents must be passed a ref");
7
- const container = useContainer();
8
- const editor = useMaybeEditor();
9
- useEffect(() => {
10
- function onMouseOver(e) {
11
- if (!editor?.getInstanceState().isFocused) return;
12
- if (e.isSpecialRedispatchedEvent) return;
13
- preventDefault(e);
14
- const cvs = container.querySelector(".tl-canvas");
15
- if (!cvs) return;
16
- const newEvent = new PointerEvent(e.type, e);
17
- newEvent.isSpecialRedispatchedEvent = true;
18
- cvs.dispatchEvent(newEvent);
19
- }
20
- const elm = ref.current;
21
- if (!elm) return;
22
- elm.addEventListener("mouseover", onMouseOver, { passive: false });
23
- return () => {
24
- elm.removeEventListener("mouseover", onMouseOver);
25
- };
26
- }, [container, editor, ref]);
27
- }
28
- export {
29
- usePassThroughMouseOverEvents
30
- };
31
- //# sourceMappingURL=usePassThroughMouseOverEvents.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/lib/hooks/usePassThroughMouseOverEvents.ts"],
4
- "sourcesContent": ["import { RefObject, useEffect } from 'react'\nimport { preventDefault } from '../utils/dom'\nimport { useContainer } from './useContainer'\nimport { useMaybeEditor } from './useEditor'\n\n/** @public */\nexport function usePassThroughMouseOverEvents(ref: RefObject<HTMLElement | null>) {\n\tif (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')\n\tconst container = useContainer()\n\tconst editor = useMaybeEditor()\n\n\tuseEffect(() => {\n\t\tfunction onMouseOver(e: MouseEvent) {\n\t\t\tif (!editor?.getInstanceState().isFocused) return\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\t\t\tpreventDefault(e)\n\t\t\tconst cvs = container.querySelector('.tl-canvas')\n\t\t\tif (!cvs) return\n\t\t\tconst newEvent = new PointerEvent(e.type, e as any)\n\t\t\t;(newEvent as any).isSpecialRedispatchedEvent = true\n\t\t\tcvs.dispatchEvent(newEvent)\n\t\t}\n\n\t\tconst elm = ref.current\n\t\tif (!elm) return\n\n\t\telm.addEventListener('mouseover', onMouseOver, { passive: false })\n\t\treturn () => {\n\t\t\telm.removeEventListener('mouseover', onMouseOver)\n\t\t}\n\t}, [container, editor, ref])\n}\n"],
5
- "mappings": "AAAA,SAAoB,iBAAiB;AACrC,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAGxB,SAAS,8BAA8B,KAAoC;AACjF,MAAI,CAAC,IAAK,OAAM,MAAM,gDAAgD;AACtE,QAAM,YAAY,aAAa;AAC/B,QAAM,SAAS,eAAe;AAE9B,YAAU,MAAM;AACf,aAAS,YAAY,GAAe;AACnC,UAAI,CAAC,QAAQ,iBAAiB,EAAE,UAAW;AAC3C,UAAK,EAAU,2BAA4B;AAC3C,qBAAe,CAAC;AAChB,YAAM,MAAM,UAAU,cAAc,YAAY;AAChD,UAAI,CAAC,IAAK;AACV,YAAM,WAAW,IAAI,aAAa,EAAE,MAAM,CAAQ;AACjD,MAAC,SAAiB,6BAA6B;AAChD,UAAI,cAAc,QAAQ;AAAA,IAC3B;AAEA,UAAM,MAAM,IAAI;AAChB,QAAI,CAAC,IAAK;AAEV,QAAI,iBAAiB,aAAa,aAAa,EAAE,SAAS,MAAM,CAAC;AACjE,WAAO,MAAM;AACZ,UAAI,oBAAoB,aAAa,WAAW;AAAA,IACjD;AAAA,EACD,GAAG,CAAC,WAAW,QAAQ,GAAG,CAAC;AAC5B;",
6
- "names": []
7
- }
@@ -1,32 +0,0 @@
1
- import { RefObject, useEffect } from 'react'
2
- import { preventDefault } from '../utils/dom'
3
- import { useContainer } from './useContainer'
4
- import { useMaybeEditor } from './useEditor'
5
-
6
- /** @public */
7
- export function usePassThroughMouseOverEvents(ref: RefObject<HTMLElement | null>) {
8
- if (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')
9
- const container = useContainer()
10
- const editor = useMaybeEditor()
11
-
12
- useEffect(() => {
13
- function onMouseOver(e: MouseEvent) {
14
- if (!editor?.getInstanceState().isFocused) return
15
- if ((e as any).isSpecialRedispatchedEvent) return
16
- preventDefault(e)
17
- const cvs = container.querySelector('.tl-canvas')
18
- if (!cvs) return
19
- const newEvent = new PointerEvent(e.type, e as any)
20
- ;(newEvent as any).isSpecialRedispatchedEvent = true
21
- cvs.dispatchEvent(newEvent)
22
- }
23
-
24
- const elm = ref.current
25
- if (!elm) return
26
-
27
- elm.addEventListener('mouseover', onMouseOver, { passive: false })
28
- return () => {
29
- elm.removeEventListener('mouseover', onMouseOver)
30
- }
31
- }, [container, editor, ref])
32
- }