@tldraw/editor 4.3.0-canary.d8da2a99f394 → 4.3.0-canary.e52fa5385f86

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 (71) hide show
  1. package/dist-cjs/index.d.ts +70 -35
  2. package/dist-cjs/index.js +2 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  5. package/dist-cjs/lib/editor/Editor.js +24 -12
  6. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  7. package/dist-cjs/lib/editor/bindings/BindingUtil.js.map +2 -2
  8. package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
  9. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js.map +2 -2
  10. package/dist-cjs/lib/editor/shapes/BaseBoxShapeUtil.js.map +1 -1
  11. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  12. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  13. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.js.map +2 -2
  14. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  15. package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
  16. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  17. package/dist-cjs/lib/globals/environment.js +45 -9
  18. package/dist-cjs/lib/globals/environment.js.map +2 -2
  19. package/dist-cjs/lib/globals/menus.js +1 -1
  20. package/dist-cjs/lib/globals/menus.js.map +2 -2
  21. package/dist-cjs/lib/hooks/useCoarsePointer.js +14 -29
  22. package/dist-cjs/lib/hooks/useCoarsePointer.js.map +2 -2
  23. package/dist-cjs/lib/utils/reparenting.js.map +2 -2
  24. package/dist-cjs/version.js +3 -3
  25. package/dist-cjs/version.js.map +1 -1
  26. package/dist-esm/index.d.mts +70 -35
  27. package/dist-esm/index.mjs +3 -2
  28. package/dist-esm/index.mjs.map +2 -2
  29. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  30. package/dist-esm/lib/editor/Editor.mjs +24 -12
  31. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  32. package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +2 -2
  33. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
  34. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs.map +2 -2
  35. package/dist-esm/lib/editor/shapes/BaseBoxShapeUtil.mjs.map +1 -1
  36. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  37. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  38. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.mjs.map +2 -2
  39. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  40. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  41. package/dist-esm/lib/globals/environment.mjs +45 -9
  42. package/dist-esm/lib/globals/environment.mjs.map +2 -2
  43. package/dist-esm/lib/globals/menus.mjs +1 -1
  44. package/dist-esm/lib/globals/menus.mjs.map +2 -2
  45. package/dist-esm/lib/hooks/useCoarsePointer.mjs +15 -30
  46. package/dist-esm/lib/hooks/useCoarsePointer.mjs.map +2 -2
  47. package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
  48. package/dist-esm/version.mjs +3 -3
  49. package/dist-esm/version.mjs.map +1 -1
  50. package/package.json +10 -10
  51. package/src/index.ts +1 -1
  52. package/src/lib/components/default-components/DefaultCanvas.tsx +1 -0
  53. package/src/lib/editor/Editor.test.ts +10 -10
  54. package/src/lib/editor/Editor.ts +110 -59
  55. package/src/lib/editor/bindings/BindingUtil.ts +15 -9
  56. package/src/lib/editor/derivations/bindingsIndex.ts +2 -2
  57. package/src/lib/editor/managers/FontManager/FontManager.test.ts +14 -4
  58. package/src/lib/editor/managers/SnapManager/SnapManager.ts +3 -3
  59. package/src/lib/editor/shapes/BaseBoxShapeUtil.tsx +2 -2
  60. package/src/lib/editor/shapes/ShapeUtil.ts +5 -8
  61. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -3
  62. package/src/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.ts +2 -1
  63. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +3 -3
  64. package/src/lib/editor/types/emit-types.ts +3 -1
  65. package/src/lib/exports/getSvgJsx.test.ts +10 -19
  66. package/src/lib/exports/getSvgJsx.tsx +2 -5
  67. package/src/lib/globals/environment.ts +65 -10
  68. package/src/lib/globals/menus.ts +1 -1
  69. package/src/lib/hooks/useCoarsePointer.ts +16 -59
  70. package/src/lib/utils/reparenting.ts +5 -5
  71. package/src/version.ts +3 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/globals/environment.ts"],
4
- "sourcesContent": ["/**\n * An object that contains information about the current device and environment.\n *\n * @public\n */\nconst tlenv = {\n\tisSafari: false,\n\tisIos: false,\n\tisChromeForIos: false,\n\tisFirefox: false,\n\tisAndroid: false,\n\tisWebview: false,\n\tisDarwin: false,\n\thasCanvasSupport: false,\n}\n\nif (typeof window !== 'undefined' && 'navigator' in window) {\n\ttlenv.isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)\n\ttlenv.isIos = !!navigator.userAgent.match(/iPad/i) || !!navigator.userAgent.match(/iPhone/i)\n\ttlenv.isChromeForIos = /crios.*safari/i.test(navigator.userAgent)\n\ttlenv.isFirefox = /firefox/i.test(navigator.userAgent)\n\ttlenv.isAndroid = /android/i.test(navigator.userAgent)\n\ttlenv.isDarwin = window.navigator.userAgent.toLowerCase().indexOf('mac') > -1\n\ttlenv.hasCanvasSupport =\n\t\ttypeof window !== 'undefined' && 'Promise' in window && 'HTMLCanvasElement' in window\n}\n\nexport { tlenv }\n"],
5
- "mappings": "AAKA,MAAM,QAAQ;AAAA,EACb,UAAU;AAAA,EACV,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,UAAU;AAAA,EACV,kBAAkB;AACnB;AAEA,IAAI,OAAO,WAAW,eAAe,eAAe,QAAQ;AAC3D,QAAM,WAAW,iCAAiC,KAAK,UAAU,SAAS;AAC1E,QAAM,QAAQ,CAAC,CAAC,UAAU,UAAU,MAAM,OAAO,KAAK,CAAC,CAAC,UAAU,UAAU,MAAM,SAAS;AAC3F,QAAM,iBAAiB,iBAAiB,KAAK,UAAU,SAAS;AAChE,QAAM,YAAY,WAAW,KAAK,UAAU,SAAS;AACrD,QAAM,YAAY,WAAW,KAAK,UAAU,SAAS;AACrD,QAAM,WAAW,OAAO,UAAU,UAAU,YAAY,EAAE,QAAQ,KAAK,IAAI;AAC3E,QAAM,mBACL,OAAO,WAAW,eAAe,aAAa,UAAU,uBAAuB;AACjF;",
4
+ "sourcesContent": ["import { atom } from '@tldraw/state'\n\n/**\n * An object that contains information about the current device and environment.\n * This object is not reactive and will not update automatically when the environment changes,\n * so only include values that are fixed, such as the user's browser and operating system.\n *\n * @public\n */\nconst tlenv = {\n\tisSafari: false,\n\tisIos: false,\n\tisChromeForIos: false,\n\tisFirefox: false,\n\tisAndroid: false,\n\tisWebview: false,\n\tisDarwin: false,\n\thasCanvasSupport: false,\n}\n\nlet isForcedFinePointer = false\n\nif (typeof window !== 'undefined') {\n\tif ('navigator' in window) {\n\t\ttlenv.isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)\n\t\ttlenv.isIos = !!navigator.userAgent.match(/iPad/i) || !!navigator.userAgent.match(/iPhone/i)\n\t\ttlenv.isChromeForIos = /crios.*safari/i.test(navigator.userAgent)\n\t\ttlenv.isFirefox = /firefox/i.test(navigator.userAgent)\n\t\ttlenv.isAndroid = /android/i.test(navigator.userAgent)\n\t\ttlenv.isDarwin = window.navigator.userAgent.toLowerCase().indexOf('mac') > -1\n\t}\n\ttlenv.hasCanvasSupport = 'Promise' in window && 'HTMLCanvasElement' in window\n\tisForcedFinePointer = tlenv.isFirefox && !tlenv.isAndroid && !tlenv.isIos\n}\n\n/**\n * An atom that contains information about the current device and environment.\n * This object is reactive and will update automatically when the environment changes.\n * Use it for values that may change over time, such as the pointer type.\n *\n * @public\n */\nconst tlenvReactive = atom('tlenvReactive', {\n\t// Whether the user's device has a coarse pointer. This is dynamic on many systems, especially\n\t// on touch-screen laptops, which will become \"coarse\" if the user touches the screen.\n\t// See https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/pointer#coarse\n\tisCoarsePointer: false,\n})\n\nif (typeof window !== 'undefined' && !isForcedFinePointer) {\n\tconst mql = window.matchMedia && window.matchMedia('(any-pointer: coarse)')\n\n\tconst isCurrentCoarsePointer = () => tlenvReactive.__unsafe__getWithoutCapture().isCoarsePointer\n\n\tif (mql) {\n\t\t// 1. Update the coarse pointer automatically when the media query changes\n\t\tconst updateIsCoarsePointer = () => {\n\t\t\tconst isCoarsePointer = mql.matches\n\t\t\tif (isCoarsePointer !== isCurrentCoarsePointer()) {\n\t\t\t\ttlenvReactive.update((prev) => ({ ...prev, isCoarsePointer: isCoarsePointer }))\n\t\t\t}\n\t\t}\n\t\tupdateIsCoarsePointer()\n\t\tmql.addEventListener('change', updateIsCoarsePointer)\n\t}\n\n\t// 2. Also update the coarse pointer state when a pointer down event occurs. We need `capture: true`\n\t// here because the tldraw component itself stops propagation on pointer events it receives.\n\twindow.addEventListener(\n\t\t'pointerdown',\n\t\t(e: PointerEvent) => {\n\t\t\t// when the user interacts with a mouse, we assume they have a fine pointer.\n\t\t\t// otherwise, we assume they have a coarse pointer.\n\t\t\tconst isCoarseEvent = e.pointerType !== 'mouse'\n\t\t\tif (isCoarseEvent !== isCurrentCoarsePointer()) {\n\t\t\t\ttlenvReactive.update((prev) => ({ ...prev, isCoarsePointer: isCoarseEvent }))\n\t\t\t}\n\t\t},\n\t\t{ capture: true }\n\t)\n}\n\nexport { tlenv, tlenvReactive }\n"],
5
+ "mappings": "AAAA,SAAS,YAAY;AASrB,MAAM,QAAQ;AAAA,EACb,UAAU;AAAA,EACV,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,UAAU;AAAA,EACV,kBAAkB;AACnB;AAEA,IAAI,sBAAsB;AAE1B,IAAI,OAAO,WAAW,aAAa;AAClC,MAAI,eAAe,QAAQ;AAC1B,UAAM,WAAW,iCAAiC,KAAK,UAAU,SAAS;AAC1E,UAAM,QAAQ,CAAC,CAAC,UAAU,UAAU,MAAM,OAAO,KAAK,CAAC,CAAC,UAAU,UAAU,MAAM,SAAS;AAC3F,UAAM,iBAAiB,iBAAiB,KAAK,UAAU,SAAS;AAChE,UAAM,YAAY,WAAW,KAAK,UAAU,SAAS;AACrD,UAAM,YAAY,WAAW,KAAK,UAAU,SAAS;AACrD,UAAM,WAAW,OAAO,UAAU,UAAU,YAAY,EAAE,QAAQ,KAAK,IAAI;AAAA,EAC5E;AACA,QAAM,mBAAmB,aAAa,UAAU,uBAAuB;AACvE,wBAAsB,MAAM,aAAa,CAAC,MAAM,aAAa,CAAC,MAAM;AACrE;AASA,MAAM,gBAAgB,KAAK,iBAAiB;AAAA;AAAA;AAAA;AAAA,EAI3C,iBAAiB;AAClB,CAAC;AAED,IAAI,OAAO,WAAW,eAAe,CAAC,qBAAqB;AAC1D,QAAM,MAAM,OAAO,cAAc,OAAO,WAAW,uBAAuB;AAE1E,QAAM,yBAAyB,MAAM,cAAc,4BAA4B,EAAE;AAEjF,MAAI,KAAK;AAER,UAAM,wBAAwB,MAAM;AACnC,YAAM,kBAAkB,IAAI;AAC5B,UAAI,oBAAoB,uBAAuB,GAAG;AACjD,sBAAc,OAAO,CAAC,UAAU,EAAE,GAAG,MAAM,gBAAiC,EAAE;AAAA,MAC/E;AAAA,IACD;AACA,0BAAsB;AACtB,QAAI,iBAAiB,UAAU,qBAAqB;AAAA,EACrD;AAIA,SAAO;AAAA,IACN;AAAA,IACA,CAAC,MAAoB;AAGpB,YAAM,gBAAgB,EAAE,gBAAgB;AACxC,UAAI,kBAAkB,uBAAuB,GAAG;AAC/C,sBAAc,OAAO,CAAC,UAAU,EAAE,GAAG,MAAM,iBAAiB,cAAc,EAAE;AAAA,MAC7E;AAAA,IACD;AAAA,IACA,EAAE,SAAS,KAAK;AAAA,EACjB;AACD;",
6
6
  "names": []
7
7
  }
@@ -138,7 +138,7 @@ const tlmenus = {
138
138
  * @public
139
139
  */
140
140
  isMenuOpen(id, contextId) {
141
- return this.getOpenMenus(contextId).includes(id);
141
+ return this.getOpenMenus(contextId).includes(`${id}-${contextId}`);
142
142
  },
143
143
  /**
144
144
  * Get whether any menus are open for a given context.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/globals/menus.ts"],
4
- "sourcesContent": ["import { atom } from '@tldraw/state'\n\n/** @public */\nexport const tlmenus = {\n\t/**\n\t * A set of strings representing any open menus. When menus are open,\n\t * certain interactions will behave differently; for example, when a\n\t * draw tool is selected and a menu is open, a pointer-down will not\n\t * create a dot (because the user is probably trying to close the menu)\n\t * however a pointer-down event followed by a drag will begin drawing\n\t * a line (because the user is BOTH trying to close the menu AND start\n\t * drawing a line).\n\t *\n\t * @public\n\t */\n\tmenus: atom<string[]>('open menus', []),\n\n\t/**\n\t * Get the current open menus.\n\t *\n\t * @param contextId - An optional context to get menus for.\n\t *\n\t * @public\n\t */\n\tgetOpenMenus(contextId?: string) {\n\t\tif (contextId) return this.menus.get().filter((m) => m.endsWith('-' + contextId))\n\t\treturn this.menus.get()\n\t},\n\n\t/**\n\t * Add an open menu.\n\t *\n\t * @example\n\t * ```ts\n\t * addOpenMenu('menu-id')\n\t * addOpenMenu('menu-id', myEditorId)\n\t * ```\n\t *\n\t * @param id - The id of the menu to add.\n\t * @param contextId - An optional context to add the menu to.\n\t *\n\t * @public\n\t */\n\taddOpenMenu(id: string, contextId = '') {\n\t\tconst idWithContext = contextId ? `${id}-${contextId}` : id\n\t\tconst menus = new Set(this.menus.get())\n\t\tif (!menus.has(idWithContext)) {\n\t\t\tmenus.add(idWithContext)\n\t\t\tthis.menus.set([...menus])\n\t\t}\n\t},\n\n\t/**\n\t * Delete an open menu.\n\t *\n\t * @example\n\t * ```ts\n\t * deleteOpenMenu('menu-id')\n\t * deleteOpenMenu('menu-id', myEditorId)\n\t * ```\n\t *\n\t * @param id - The id of the menu to delete.\n\t * @param contextId - An optional context to delete the menu from.\n\t *\n\t * @public\n\t */\n\tdeleteOpenMenu(id: string, contextId = '') {\n\t\tconst idWithContext = contextId ? `${id}-${contextId}` : id\n\t\tconst menus = new Set(this.menus.get())\n\t\tif (menus.has(idWithContext)) {\n\t\t\tmenus.delete(idWithContext)\n\t\t\tthis.menus.set([...menus])\n\t\t}\n\t},\n\n\t/**\n\t * Clear all open menus.\n\t *\n\t * @example\n\t * ```ts\n\t * clearOpenMenus()\n\t * clearOpenMenus(myEditorId)\n\t * ```\n\t *\n\t * @param contextId - An optional context to clear menus for.\n\t *\n\t * @public\n\t */\n\tclearOpenMenus(contextId?: string) {\n\t\tthis.menus.set(contextId ? this.menus.get().filter((m) => !m.endsWith('-' + contextId)) : [])\n\t},\n\n\t_hiddenMenus: [] as string[],\n\n\t/**\n\t * Hide all open menus. Restore them with the `showOpenMenus` method.\n\t *\n\t * @example\n\t * ```ts\n\t * hideOpenMenus()\n\t * hideOpenMenus(myEditorId)\n\t * ```\n\t *\n\t * @param contextId - An optional context to hide menus for.\n\t *\n\t * @public\n\t */\n\thideOpenMenus(contextId?: string) {\n\t\tthis._hiddenMenus = [...this.getOpenMenus(contextId)]\n\t\tif (this._hiddenMenus.length === 0) return\n\t\tfor (const menu of this._hiddenMenus) {\n\t\t\tthis.deleteOpenMenu(menu, contextId)\n\t\t}\n\t},\n\n\t/**\n\t * Show all hidden menus.\n\t *\n\t * @example\n\t * ```ts\n\t * showOpenMenus()\n\t * showOpenMenus(myEditorId)\n\t * ```\n\t *\n\t * @param contextId - An optional context to show menus for.\n\t *\n\t * @public\n\t */\n\tshowOpenMenus(contextId?: string) {\n\t\tif (this._hiddenMenus.length === 0) return\n\t\tfor (const menu of this._hiddenMenus) {\n\t\t\tthis.addOpenMenu(menu, contextId)\n\t\t}\n\t\tthis._hiddenMenus = []\n\t},\n\n\t/**\n\t * Get whether a menu is open for a given context.\n\t *\n\t * @example\n\t * ```ts\n\t * isMenuOpem(id, myEditorId)\n\t * ```\n\t *\n\t * @param id - The id of the menu to check.\n\t * @param contextId - An optional context to check menus for.\n\t *\n\t * @public\n\t */\n\tisMenuOpen(id: string, contextId?: string): boolean {\n\t\treturn this.getOpenMenus(contextId).includes(id)\n\t},\n\n\t/**\n\t * Get whether any menus are open for a given context.\n\t *\n\t * @example\n\t * ```ts\n\t * hasOpenMenus(myEditorId)\n\t * ```\n\t *\n\t * @param contextId - A context to check menus for.\n\t *\n\t * @public\n\t */\n\thasOpenMenus(contextId: string): boolean {\n\t\treturn this.getOpenMenus(contextId).length > 0\n\t},\n\n\t/**\n\t * Get whether any menus are open for any context.\n\t *\n\t * @example\n\t * ```ts\n\t * hasAnyOpenMenus()\n\t * ```\n\t *\n\t * @public\n\t */\n\thasAnyOpenMenus(): boolean {\n\t\treturn this.getOpenMenus().length > 0\n\t},\n\n\tforContext(contextId: string) {\n\t\treturn {\n\t\t\tgetOpenMenus: () => this.getOpenMenus(contextId),\n\t\t\taddOpenMenu: (id: string) => this.addOpenMenu(id, contextId),\n\t\t\tdeleteOpenMenu: (id: string) => this.deleteOpenMenu(id, contextId),\n\t\t\tclearOpenMenus: () => this.clearOpenMenus(contextId),\n\t\t\t// Gets whether any menus are open\n\t\t\tisMenuOpen: (id: string) => this.isMenuOpen(id, contextId),\n\t\t\thasOpenMenus: () => this.hasOpenMenus(contextId),\n\t\t\thasAnyOpenMenus: () => this.hasAnyOpenMenus(),\n\t\t}\n\t},\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { atom } from '@tldraw/state'\n\n/** @public */\nexport const tlmenus = {\n\t/**\n\t * A set of strings representing any open menus. When menus are open,\n\t * certain interactions will behave differently; for example, when a\n\t * draw tool is selected and a menu is open, a pointer-down will not\n\t * create a dot (because the user is probably trying to close the menu)\n\t * however a pointer-down event followed by a drag will begin drawing\n\t * a line (because the user is BOTH trying to close the menu AND start\n\t * drawing a line).\n\t *\n\t * @public\n\t */\n\tmenus: atom<string[]>('open menus', []),\n\n\t/**\n\t * Get the current open menus.\n\t *\n\t * @param contextId - An optional context to get menus for.\n\t *\n\t * @public\n\t */\n\tgetOpenMenus(contextId?: string) {\n\t\tif (contextId) return this.menus.get().filter((m) => m.endsWith('-' + contextId))\n\t\treturn this.menus.get()\n\t},\n\n\t/**\n\t * Add an open menu.\n\t *\n\t * @example\n\t * ```ts\n\t * addOpenMenu('menu-id')\n\t * addOpenMenu('menu-id', myEditorId)\n\t * ```\n\t *\n\t * @param id - The id of the menu to add.\n\t * @param contextId - An optional context to add the menu to.\n\t *\n\t * @public\n\t */\n\taddOpenMenu(id: string, contextId = '') {\n\t\tconst idWithContext = contextId ? `${id}-${contextId}` : id\n\t\tconst menus = new Set(this.menus.get())\n\t\tif (!menus.has(idWithContext)) {\n\t\t\tmenus.add(idWithContext)\n\t\t\tthis.menus.set([...menus])\n\t\t}\n\t},\n\n\t/**\n\t * Delete an open menu.\n\t *\n\t * @example\n\t * ```ts\n\t * deleteOpenMenu('menu-id')\n\t * deleteOpenMenu('menu-id', myEditorId)\n\t * ```\n\t *\n\t * @param id - The id of the menu to delete.\n\t * @param contextId - An optional context to delete the menu from.\n\t *\n\t * @public\n\t */\n\tdeleteOpenMenu(id: string, contextId = '') {\n\t\tconst idWithContext = contextId ? `${id}-${contextId}` : id\n\t\tconst menus = new Set(this.menus.get())\n\t\tif (menus.has(idWithContext)) {\n\t\t\tmenus.delete(idWithContext)\n\t\t\tthis.menus.set([...menus])\n\t\t}\n\t},\n\n\t/**\n\t * Clear all open menus.\n\t *\n\t * @example\n\t * ```ts\n\t * clearOpenMenus()\n\t * clearOpenMenus(myEditorId)\n\t * ```\n\t *\n\t * @param contextId - An optional context to clear menus for.\n\t *\n\t * @public\n\t */\n\tclearOpenMenus(contextId?: string) {\n\t\tthis.menus.set(contextId ? this.menus.get().filter((m) => !m.endsWith('-' + contextId)) : [])\n\t},\n\n\t_hiddenMenus: [] as string[],\n\n\t/**\n\t * Hide all open menus. Restore them with the `showOpenMenus` method.\n\t *\n\t * @example\n\t * ```ts\n\t * hideOpenMenus()\n\t * hideOpenMenus(myEditorId)\n\t * ```\n\t *\n\t * @param contextId - An optional context to hide menus for.\n\t *\n\t * @public\n\t */\n\thideOpenMenus(contextId?: string) {\n\t\tthis._hiddenMenus = [...this.getOpenMenus(contextId)]\n\t\tif (this._hiddenMenus.length === 0) return\n\t\tfor (const menu of this._hiddenMenus) {\n\t\t\tthis.deleteOpenMenu(menu, contextId)\n\t\t}\n\t},\n\n\t/**\n\t * Show all hidden menus.\n\t *\n\t * @example\n\t * ```ts\n\t * showOpenMenus()\n\t * showOpenMenus(myEditorId)\n\t * ```\n\t *\n\t * @param contextId - An optional context to show menus for.\n\t *\n\t * @public\n\t */\n\tshowOpenMenus(contextId?: string) {\n\t\tif (this._hiddenMenus.length === 0) return\n\t\tfor (const menu of this._hiddenMenus) {\n\t\t\tthis.addOpenMenu(menu, contextId)\n\t\t}\n\t\tthis._hiddenMenus = []\n\t},\n\n\t/**\n\t * Get whether a menu is open for a given context.\n\t *\n\t * @example\n\t * ```ts\n\t * isMenuOpem(id, myEditorId)\n\t * ```\n\t *\n\t * @param id - The id of the menu to check.\n\t * @param contextId - An optional context to check menus for.\n\t *\n\t * @public\n\t */\n\tisMenuOpen(id: string, contextId?: string): boolean {\n\t\treturn this.getOpenMenus(contextId).includes(`${id}-${contextId}`)\n\t},\n\n\t/**\n\t * Get whether any menus are open for a given context.\n\t *\n\t * @example\n\t * ```ts\n\t * hasOpenMenus(myEditorId)\n\t * ```\n\t *\n\t * @param contextId - A context to check menus for.\n\t *\n\t * @public\n\t */\n\thasOpenMenus(contextId: string): boolean {\n\t\treturn this.getOpenMenus(contextId).length > 0\n\t},\n\n\t/**\n\t * Get whether any menus are open for any context.\n\t *\n\t * @example\n\t * ```ts\n\t * hasAnyOpenMenus()\n\t * ```\n\t *\n\t * @public\n\t */\n\thasAnyOpenMenus(): boolean {\n\t\treturn this.getOpenMenus().length > 0\n\t},\n\n\tforContext(contextId: string) {\n\t\treturn {\n\t\t\tgetOpenMenus: () => this.getOpenMenus(contextId),\n\t\t\taddOpenMenu: (id: string) => this.addOpenMenu(id, contextId),\n\t\t\tdeleteOpenMenu: (id: string) => this.deleteOpenMenu(id, contextId),\n\t\t\tclearOpenMenus: () => this.clearOpenMenus(contextId),\n\t\t\t// Gets whether any menus are open\n\t\t\tisMenuOpen: (id: string) => this.isMenuOpen(id, contextId),\n\t\t\thasOpenMenus: () => this.hasOpenMenus(contextId),\n\t\t\thasAnyOpenMenus: () => this.hasAnyOpenMenus(),\n\t\t}\n\t},\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,36 +1,21 @@
1
- import { useEffect } from "react";
2
- import { tlenv } from "../globals/environment.mjs";
1
+ import { unsafe__withoutCapture } from "@tldraw/state";
2
+ import { useReactor } from "@tldraw/state-react";
3
+ import { tlenvReactive } from "../globals/environment.mjs";
3
4
  import { useEditor } from "./useEditor.mjs";
4
5
  function useCoarsePointer() {
5
6
  const editor = useEditor();
6
- useEffect(() => {
7
- let isCoarse = editor.getInstanceState().isCoarsePointer;
8
- const handlePointerDown = (e) => {
9
- const isCoarseEvent = e.pointerType !== "mouse";
10
- if (isCoarse === isCoarseEvent) return;
11
- isCoarse = isCoarseEvent;
12
- editor.updateInstanceState({ isCoarsePointer: isCoarseEvent });
13
- };
14
- window.addEventListener("pointerdown", handlePointerDown, { capture: true });
15
- const mql = window.matchMedia && window.matchMedia("(any-pointer: coarse)");
16
- const isForcedFinePointer = tlenv.isFirefox && !tlenv.isAndroid && !tlenv.isIos;
17
- const handleMediaQueryChange = () => {
18
- const next = isForcedFinePointer ? false : mql.matches;
19
- if (isCoarse !== next) return;
20
- isCoarse = next;
21
- editor.updateInstanceState({ isCoarsePointer: next });
22
- };
23
- if (mql) {
24
- mql.addEventListener("change", handleMediaQueryChange);
25
- handleMediaQueryChange();
26
- }
27
- return () => {
28
- window.removeEventListener("pointerdown", handlePointerDown, { capture: true });
29
- if (mql) {
30
- mql.removeEventListener("change", handleMediaQueryChange);
31
- }
32
- };
33
- }, [editor]);
7
+ useReactor(
8
+ "coarse pointer change",
9
+ () => {
10
+ const isCoarsePointer = tlenvReactive.get().isCoarsePointer;
11
+ const isInstanceStateCoarsePointer = unsafe__withoutCapture(
12
+ () => editor.getInstanceState().isCoarsePointer
13
+ );
14
+ if (isCoarsePointer === isInstanceStateCoarsePointer) return;
15
+ editor.updateInstanceState({ isCoarsePointer });
16
+ },
17
+ [editor]
18
+ );
34
19
  }
35
20
  export {
36
21
  useCoarsePointer
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/hooks/useCoarsePointer.ts"],
4
- "sourcesContent": ["import { useEffect } from 'react'\nimport { tlenv } from '../globals/environment'\nimport { useEditor } from './useEditor'\n\n/** @internal */\nexport function useCoarsePointer() {\n\tconst editor = useEditor()\n\n\tuseEffect(() => {\n\t\t// We'll track our own state for the pointer type\n\t\tlet isCoarse = editor.getInstanceState().isCoarsePointer\n\n\t\t// 1.\n\t\t// We'll use pointer events to detect coarse pointer.\n\n\t\tconst handlePointerDown = (e: PointerEvent) => {\n\t\t\t// when the user interacts with a mouse, we assume they have a fine pointer.\n\t\t\t// otherwise, we assume they have a coarse pointer.\n\t\t\tconst isCoarseEvent = e.pointerType !== 'mouse'\n\t\t\tif (isCoarse === isCoarseEvent) return\n\t\t\tisCoarse = isCoarseEvent\n\t\t\teditor.updateInstanceState({ isCoarsePointer: isCoarseEvent })\n\t\t}\n\n\t\t// we need `capture: true` here because the tldraw component itself stops propagation on\n\t\t// pointer events it receives.\n\t\twindow.addEventListener('pointerdown', handlePointerDown, { capture: true })\n\n\t\t// 2.\n\t\t// We can also use the media query to detect / set the initial pointer type\n\t\t// and update the state if the pointer type changes.\n\n\t\t// We want the touch / mouse events to run even if the browser does not\n\t\t// support matchMedia. We'll have to handle the media query changes\n\t\t// conditionally in the code below.\n\t\tconst mql = window.matchMedia && window.matchMedia('(any-pointer: coarse)')\n\n\t\t// This is a workaround for a Firefox bug where we don't correctly\n\t\t// detect coarse VS fine pointer. For now, let's assume that you have a fine\n\t\t// pointer if you're on Firefox on desktop.\n\t\tconst isForcedFinePointer = tlenv.isFirefox && !tlenv.isAndroid && !tlenv.isIos\n\n\t\tconst handleMediaQueryChange = () => {\n\t\t\tconst next = isForcedFinePointer ? false : mql.matches // get the value from the media query\n\t\t\tif (isCoarse !== next) return // bail if the value hasn't changed\n\t\t\tisCoarse = next // update the local value\n\t\t\teditor.updateInstanceState({ isCoarsePointer: next }) // update the value in state\n\t\t}\n\n\t\tif (mql) {\n\t\t\t// set up the listener\n\t\t\tmql.addEventListener('change', handleMediaQueryChange)\n\n\t\t\t// and run the handler once to set the initial value\n\t\t\thandleMediaQueryChange()\n\t\t}\n\n\t\treturn () => {\n\t\t\twindow.removeEventListener('pointerdown', handlePointerDown, { capture: true })\n\n\t\t\tif (mql) {\n\t\t\t\tmql.removeEventListener('change', handleMediaQueryChange)\n\t\t\t}\n\t\t}\n\t}, [editor])\n}\n"],
5
- "mappings": "AAAA,SAAS,iBAAiB;AAC1B,SAAS,aAAa;AACtB,SAAS,iBAAiB;AAGnB,SAAS,mBAAmB;AAClC,QAAM,SAAS,UAAU;AAEzB,YAAU,MAAM;AAEf,QAAI,WAAW,OAAO,iBAAiB,EAAE;AAKzC,UAAM,oBAAoB,CAAC,MAAoB;AAG9C,YAAM,gBAAgB,EAAE,gBAAgB;AACxC,UAAI,aAAa,cAAe;AAChC,iBAAW;AACX,aAAO,oBAAoB,EAAE,iBAAiB,cAAc,CAAC;AAAA,IAC9D;AAIA,WAAO,iBAAiB,eAAe,mBAAmB,EAAE,SAAS,KAAK,CAAC;AAS3E,UAAM,MAAM,OAAO,cAAc,OAAO,WAAW,uBAAuB;AAK1E,UAAM,sBAAsB,MAAM,aAAa,CAAC,MAAM,aAAa,CAAC,MAAM;AAE1E,UAAM,yBAAyB,MAAM;AACpC,YAAM,OAAO,sBAAsB,QAAQ,IAAI;AAC/C,UAAI,aAAa,KAAM;AACvB,iBAAW;AACX,aAAO,oBAAoB,EAAE,iBAAiB,KAAK,CAAC;AAAA,IACrD;AAEA,QAAI,KAAK;AAER,UAAI,iBAAiB,UAAU,sBAAsB;AAGrD,6BAAuB;AAAA,IACxB;AAEA,WAAO,MAAM;AACZ,aAAO,oBAAoB,eAAe,mBAAmB,EAAE,SAAS,KAAK,CAAC;AAE9E,UAAI,KAAK;AACR,YAAI,oBAAoB,UAAU,sBAAsB;AAAA,MACzD;AAAA,IACD;AAAA,EACD,GAAG,CAAC,MAAM,CAAC;AACZ;",
4
+ "sourcesContent": ["import { unsafe__withoutCapture } from '@tldraw/state'\nimport { useReactor } from '@tldraw/state-react'\nimport { tlenvReactive } from '../globals/environment'\nimport { useEditor } from './useEditor'\n\n/** @internal */\nexport function useCoarsePointer() {\n\tconst editor = useEditor()\n\n\t// When the coarse pointer state changes, update the instance state\n\tuseReactor(\n\t\t'coarse pointer change',\n\t\t() => {\n\t\t\tconst isCoarsePointer = tlenvReactive.get().isCoarsePointer\n\t\t\tconst isInstanceStateCoarsePointer = unsafe__withoutCapture(\n\t\t\t\t() => editor.getInstanceState().isCoarsePointer\n\t\t\t)\n\t\t\tif (isCoarsePointer === isInstanceStateCoarsePointer) return\n\t\t\teditor.updateInstanceState({ isCoarsePointer: isCoarsePointer })\n\t\t},\n\t\t[editor]\n\t)\n}\n"],
5
+ "mappings": "AAAA,SAAS,8BAA8B;AACvC,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAGnB,SAAS,mBAAmB;AAClC,QAAM,SAAS,UAAU;AAGzB;AAAA,IACC;AAAA,IACA,MAAM;AACL,YAAM,kBAAkB,cAAc,IAAI,EAAE;AAC5C,YAAM,+BAA+B;AAAA,QACpC,MAAM,OAAO,iBAAiB,EAAE;AAAA,MACjC;AACA,UAAI,oBAAoB,6BAA8B;AACtD,aAAO,oBAAoB,EAAE,gBAAiC,CAAC;AAAA,IAChE;AAAA,IACA,CAAC,MAAM;AAAA,EACR;AACD;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/utils/reparenting.ts"],
4
- "sourcesContent": ["import { EMPTY_ARRAY } from '@tldraw/state'\nimport { TLGroupShape, TLParentId, TLShape, TLShapeId } from '@tldraw/tlschema'\nimport { IndexKey, compact, getIndexAbove, getIndexBetween } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\nimport { intersectPolygonPolygon } from '../primitives/intersect'\n\n/**\n * Reparents shapes that are no longer contained within their parent shapes.\n * todo: rename me to something more descriptive, like `reparentOccludedShapes` or `reparentAutoDroppedShapes`\n *\n * @param editor - The editor instance.\n * @param shapeIds - The IDs of the shapes to reparent.\n * @param opts - Optional options, including a callback to filter out certain parents, such as when removing a frame.\n *\n * @public\n */\nexport function kickoutOccludedShapes(\n\teditor: Editor,\n\tshapeIds: TLShapeId[],\n\topts?: { filter?(parent: TLShape): boolean }\n) {\n\tconst parentsToCheck = new Set<TLShape>()\n\n\tfor (const id of shapeIds) {\n\t\tconst shape = editor.getShape(id)\n\n\t\tif (!shape) continue\n\t\tparentsToCheck.add(shape)\n\n\t\tconst parent = editor.getShape(shape.parentId)\n\t\tif (!parent) continue\n\t\tparentsToCheck.add(parent)\n\t}\n\n\t// Check all of the parents and gather up parents who have lost children\n\tconst parentsToLostChildren = new Map<TLShape, TLShapeId[]>()\n\n\tfor (const parent of parentsToCheck) {\n\t\tconst childIds = editor.getSortedChildIdsForParent(parent)\n\t\tif (opts?.filter && !opts.filter(parent)) {\n\t\t\t// If the shape is filtered out, we kick out all of its children\n\t\t\tparentsToLostChildren.set(parent, childIds)\n\t\t} else {\n\t\t\tconst overlappingChildren = getOverlappingShapes(editor, parent.id, childIds)\n\t\t\tif (overlappingChildren.length < childIds.length) {\n\t\t\t\tparentsToLostChildren.set(\n\t\t\t\t\tparent,\n\t\t\t\t\tchildIds.filter((id) => !overlappingChildren.includes(id))\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Get all of the shapes on the current page, sorted by their index\n\tconst sortedShapeIds = editor.getCurrentPageShapesSorted().map((s) => s.id)\n\n\tconst parentsToNewChildren: Record<\n\t\tTLParentId,\n\t\t{ parentId: TLParentId; shapeIds: TLShapeId[]; index?: IndexKey }\n\t> = {}\n\n\tfor (const [prevParent, lostChildrenIds] of parentsToLostChildren) {\n\t\tconst lostChildren = compact(lostChildrenIds.map((id) => editor.getShape(id)))\n\n\t\t// Don't fall \"up\" into frames in front of the shape\n\t\t// if (pageShapes.indexOf(shape) < frameSortPosition) continue shapeCheck\n\n\t\t// Otherwise, we have no next dropping shape under the cursor, so go find\n\t\t// all the frames on the page where the moving shapes will fall into\n\t\tconst { reparenting, remainingShapesToReparent } = getDroppedShapesToNewParents(\n\t\t\teditor,\n\t\t\tlostChildren,\n\t\t\t(shape, maybeNewParent) => {\n\t\t\t\t// If we're filtering out a potential parent, don't reparent shapes to the filtered out shape\n\t\t\t\tif (opts?.filter && !opts.filter(maybeNewParent)) return false\n\t\t\t\treturn (\n\t\t\t\t\tmaybeNewParent.id !== prevParent.id &&\n\t\t\t\t\tsortedShapeIds.indexOf(maybeNewParent.id) < sortedShapeIds.indexOf(shape.id)\n\t\t\t\t)\n\t\t\t}\n\t\t)\n\n\t\treparenting.forEach((childrenToReparent, newParentId) => {\n\t\t\tif (childrenToReparent.length === 0) return\n\t\t\tif (!parentsToNewChildren[newParentId]) {\n\t\t\t\tparentsToNewChildren[newParentId] = {\n\t\t\t\t\tparentId: newParentId,\n\t\t\t\t\tshapeIds: [],\n\t\t\t\t}\n\t\t\t}\n\t\t\tparentsToNewChildren[newParentId].shapeIds.push(...childrenToReparent.map((s) => s.id))\n\t\t})\n\n\t\t// Reparent the rest to the page (or containing group)\n\t\tif (remainingShapesToReparent.size > 0) {\n\t\t\t// The remaining shapes are going to be reparented to the old parent's containing group, if there was one, or else to the page\n\t\t\tconst newParentId =\n\t\t\t\teditor.findShapeAncestor(prevParent, (s) => editor.isShapeOfType<TLGroupShape>(s, 'group'))\n\t\t\t\t\t?.id ?? editor.getCurrentPageId()\n\n\t\t\tremainingShapesToReparent.forEach((shape) => {\n\t\t\t\tif (!parentsToNewChildren[newParentId]) {\n\t\t\t\t\tlet insertIndexKey: IndexKey | undefined\n\n\t\t\t\t\tconst oldParentSiblingIds = editor.getSortedChildIdsForParent(newParentId)\n\t\t\t\t\tconst oldParentIndex = oldParentSiblingIds.indexOf(prevParent.id)\n\t\t\t\t\tif (oldParentIndex > -1) {\n\t\t\t\t\t\t// If the old parent is a direct child of the new parent, then we'll add them above the old parent but below the next sibling.\n\t\t\t\t\t\tconst siblingsIndexAbove = oldParentSiblingIds[oldParentIndex + 1]\n\t\t\t\t\t\tconst indexKeyAbove = siblingsIndexAbove\n\t\t\t\t\t\t\t? editor.getShape(siblingsIndexAbove)!.index\n\t\t\t\t\t\t\t: getIndexAbove(prevParent.index)\n\t\t\t\t\t\tinsertIndexKey = getIndexBetween(prevParent.index, indexKeyAbove)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If the old parent is not a direct child of the new parent, then we'll add them to the \"top\" of the new parent's children.\n\t\t\t\t\t\t// This is done automatically if we leave the index undefined, so let's do that.\n\t\t\t\t\t}\n\n\t\t\t\t\tparentsToNewChildren[newParentId] = {\n\t\t\t\t\t\tparentId: newParentId,\n\t\t\t\t\t\tshapeIds: [],\n\t\t\t\t\t\tindex: insertIndexKey,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tparentsToNewChildren[newParentId].shapeIds.push(shape.id)\n\t\t\t})\n\t\t}\n\t}\n\n\teditor.run(() => {\n\t\tObject.values(parentsToNewChildren).forEach(({ parentId, shapeIds, index }) => {\n\t\t\tif (shapeIds.length === 0) return\n\t\t\t// Before we reparent, sort the new shape ids by their place in the original absolute order on the page\n\t\t\tshapeIds.sort((a, b) => (sortedShapeIds.indexOf(a) < sortedShapeIds.indexOf(b) ? -1 : 1))\n\t\t\teditor.reparentShapes(shapeIds, parentId, index)\n\t\t})\n\t})\n}\n\n/**\n * Get the shapes that overlap with a given shape.\n *\n * @param editor - The editor instance.\n * @param shape - The shapes or shape IDs to check against.\n * @param otherShapes - The shapes or shape IDs to check for overlap.\n * @returns An array of shapes or shape IDs that overlap with the given shape.\n */\nfunction getOverlappingShapes<T extends TLShape[] | TLShapeId[]>(\n\teditor: Editor,\n\tshape: T[number],\n\totherShapes: T\n) {\n\tif (otherShapes.length === 0) {\n\t\treturn EMPTY_ARRAY\n\t}\n\n\tconst parentPageBounds = editor.getShapePageBounds(shape)\n\tif (!parentPageBounds) return EMPTY_ARRAY\n\n\tconst parentGeometry = editor.getShapeGeometry(shape)\n\tconst parentPageTransform = editor.getShapePageTransform(shape)\n\tconst parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices)\n\n\tconst _shape = editor.getShape(shape)\n\tif (!_shape) return EMPTY_ARRAY\n\n\tconst pageTransform = editor.getShapePageTransform(shape)\n\tconst clipPath = editor.getShapeUtil(_shape.type).getClipPath?.(_shape)\n\n\tconst parentPageMaskVertices = clipPath ? pageTransform.applyToPoints(clipPath) : undefined\n\tconst parentPagePolygon = parentPageMaskVertices\n\t\t? intersectPolygonPolygon(parentPageMaskVertices, parentPageCorners)\n\t\t: parentPageCorners\n\n\tif (!parentPagePolygon) return EMPTY_ARRAY\n\n\treturn otherShapes.filter((childId) => {\n\t\tconst shapePageBounds = editor.getShapePageBounds(childId)\n\t\tif (!shapePageBounds || !parentPageBounds.includes(shapePageBounds)) return false\n\n\t\tconst parentPolygonInShapeShape = editor\n\t\t\t.getShapePageTransform(childId)\n\t\t\t.clone()\n\t\t\t.invert()\n\t\t\t.applyToPoints(parentPagePolygon)\n\n\t\tconst geometry = editor.getShapeGeometry(childId)\n\n\t\treturn geometry.overlapsPolygon(parentPolygonInShapeShape)\n\t})\n}\n\n/**\n * Get the shapes that will be reparented to new parents when the shapes are dropped.\n *\n * @param editor - The editor instance.\n * @param shapes - The shapes to check.\n * @param cb - A callback to filter out certain shapes.\n * @returns An object with the shapes that will be reparented to new parents and the shapes that will be reparented to the page or their ancestral group.\n *\n * @public\n */\nexport function getDroppedShapesToNewParents(\n\teditor: Editor,\n\tshapes: Set<TLShape> | TLShape[],\n\tcb?: (shape: TLShape, parent: TLShape) => boolean\n) {\n\tconst shapesToActuallyCheck = new Set<TLShape>(shapes)\n\tconst movingGroups = new Set<TLGroupShape>()\n\n\tfor (const shape of shapes) {\n\t\tconst parent = editor.getShapeParent(shape)\n\t\tif (parent && editor.isShapeOfType<TLGroupShape>(parent, 'group')) {\n\t\t\tif (!movingGroups.has(parent)) {\n\t\t\t\tmovingGroups.add(parent)\n\t\t\t}\n\t\t}\n\t}\n\n\t// If all of a group's children are moving, then move the group instead\n\tfor (const movingGroup of movingGroups) {\n\t\tconst children = compact(\n\t\t\teditor.getSortedChildIdsForParent(movingGroup).map((id) => editor.getShape(id))\n\t\t)\n\t\tfor (const child of children) {\n\t\t\tshapesToActuallyCheck.delete(child)\n\t\t}\n\t\tshapesToActuallyCheck.add(movingGroup)\n\t}\n\n\t// this could be cached and passed in\n\tconst shapeGroupIds = new Map<TLShapeId, TLShapeId | undefined>()\n\n\tconst reparenting = new Map<TLShapeId, TLShape[]>()\n\n\tconst remainingShapesToReparent = new Set(shapesToActuallyCheck)\n\n\tconst potentialParentShapes = editor\n\t\t.getCurrentPageShapesSorted()\n\t\t// filter out any shapes that aren't frames or that are included among the provided shapes\n\t\t.filter(\n\t\t\t(s) =>\n\t\t\t\teditor.getShapeUtil(s).canReceiveNewChildrenOfType?.(s, s.type) &&\n\t\t\t\t!remainingShapesToReparent.has(s)\n\t\t)\n\n\tparentCheck: for (let i = potentialParentShapes.length - 1; i >= 0; i--) {\n\t\tconst parentShape = potentialParentShapes[i]\n\t\tconst parentShapeContainingGroupId = editor.findShapeAncestor(parentShape, (s) =>\n\t\t\teditor.isShapeOfType<TLGroupShape>(s, 'group')\n\t\t)?.id\n\n\t\tconst parentGeometry = editor.getShapeGeometry(parentShape)\n\t\tconst parentPageTransform = editor.getShapePageTransform(parentShape)\n\t\tconst parentPageMaskVertices = editor.getShapeMask(parentShape)\n\t\tconst parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices)\n\t\tconst parentPagePolygon = parentPageMaskVertices\n\t\t\t? intersectPolygonPolygon(parentPageMaskVertices, parentPageCorners)\n\t\t\t: parentPageCorners\n\n\t\tif (!parentPagePolygon) continue parentCheck\n\n\t\tconst childrenToReparent = []\n\n\t\t// For each of the dropping shapes...\n\t\tshapeCheck: for (const shape of remainingShapesToReparent) {\n\t\t\t// Don't reparent a frame to itself\n\t\t\tif (parentShape.id === shape.id) continue shapeCheck\n\n\t\t\t// Use the callback to filter out certain shapes\n\t\t\tif (cb && !cb(shape, parentShape)) continue shapeCheck\n\n\t\t\tif (!shapeGroupIds.has(shape.id)) {\n\t\t\t\tshapeGroupIds.set(\n\t\t\t\t\tshape.id,\n\t\t\t\t\teditor.findShapeAncestor(shape, (s) => editor.isShapeOfType<TLGroupShape>(s, 'group'))?.id\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst shapeGroupId = shapeGroupIds.get(shape.id)\n\n\t\t\t// Are the shape and the parent part of different groups?\n\t\t\tif (shapeGroupId !== parentShapeContainingGroupId) continue shapeCheck\n\n\t\t\t// Is the shape is actually the ancestor of the parent?\n\t\t\tif (editor.findShapeAncestor(parentShape, (s) => shape.id === s.id)) continue shapeCheck\n\n\t\t\t// Convert the parent polygon to the shape's space\n\t\t\tconst parentPolygonInShapeSpace = editor\n\t\t\t\t.getShapePageTransform(shape)\n\t\t\t\t.clone()\n\t\t\t\t.invert()\n\t\t\t\t.applyToPoints(parentPagePolygon)\n\n\t\t\t// If the shape overlaps the parent polygon, reparent it to that parent\n\t\t\tif (editor.getShapeGeometry(shape).overlapsPolygon(parentPolygonInShapeSpace)) {\n\t\t\t\t// Use the util to check if the shape can be reparented to the parent\n\t\t\t\tif (\n\t\t\t\t\t!editor.getShapeUtil(parentShape).canReceiveNewChildrenOfType?.(parentShape, shape.type)\n\t\t\t\t)\n\t\t\t\t\tcontinue shapeCheck\n\n\t\t\t\tif (shape.parentId !== parentShape.id) {\n\t\t\t\t\tchildrenToReparent.push(shape)\n\t\t\t\t}\n\t\t\t\tremainingShapesToReparent.delete(shape)\n\t\t\t\tcontinue shapeCheck\n\t\t\t}\n\t\t}\n\n\t\tif (childrenToReparent.length) {\n\t\t\treparenting.set(parentShape.id, childrenToReparent)\n\t\t}\n\t}\n\n\treturn {\n\t\t// these are the shapes that will be reparented to new parents\n\t\treparenting,\n\t\t// these are the shapes that will be reparented to the page or their ancestral group\n\t\tremainingShapesToReparent,\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { EMPTY_ARRAY } from '@tldraw/state'\nimport { TLGroupShape, TLParentId, TLShape, TLShapeId } from '@tldraw/tlschema'\nimport { IndexKey, compact, getIndexAbove, getIndexBetween } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\nimport { intersectPolygonPolygon } from '../primitives/intersect'\n\n/**\n * Reparents shapes that are no longer contained within their parent shapes.\n * todo: rename me to something more descriptive, like `reparentOccludedShapes` or `reparentAutoDroppedShapes`\n *\n * @param editor - The editor instance.\n * @param shapeIds - The IDs of the shapes to reparent.\n * @param opts - Optional options, including a callback to filter out certain parents, such as when removing a frame.\n *\n * @public\n */\nexport function kickoutOccludedShapes(\n\teditor: Editor,\n\tshapeIds: TLShapeId[],\n\topts?: { filter?(parent: TLShape): boolean }\n) {\n\tconst parentsToCheck = new Set<TLShape>()\n\n\tfor (const id of shapeIds) {\n\t\tconst shape = editor.getShape(id)\n\n\t\tif (!shape) continue\n\t\tparentsToCheck.add(shape)\n\n\t\tconst parent = editor.getShape(shape.parentId)\n\t\tif (!parent) continue\n\t\tparentsToCheck.add(parent)\n\t}\n\n\t// Check all of the parents and gather up parents who have lost children\n\tconst parentsToLostChildren = new Map<TLShape, TLShapeId[]>()\n\n\tfor (const parent of parentsToCheck) {\n\t\tconst childIds = editor.getSortedChildIdsForParent(parent)\n\t\tif (opts?.filter && !opts.filter(parent)) {\n\t\t\t// If the shape is filtered out, we kick out all of its children\n\t\t\tparentsToLostChildren.set(parent, childIds)\n\t\t} else {\n\t\t\tconst overlappingChildren = getOverlappingShapes(editor, parent.id, childIds)\n\t\t\tif (overlappingChildren.length < childIds.length) {\n\t\t\t\tparentsToLostChildren.set(\n\t\t\t\t\tparent,\n\t\t\t\t\tchildIds.filter((id) => !overlappingChildren.includes(id))\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Get all of the shapes on the current page, sorted by their index\n\tconst sortedShapeIds = editor.getCurrentPageShapesSorted().map((s) => s.id)\n\n\tconst parentsToNewChildren: Record<\n\t\tTLParentId,\n\t\t{ parentId: TLParentId; shapeIds: TLShapeId[]; index?: IndexKey }\n\t> = {}\n\n\tfor (const [prevParent, lostChildrenIds] of parentsToLostChildren) {\n\t\tconst lostChildren = compact(lostChildrenIds.map((id) => editor.getShape(id)))\n\n\t\t// Don't fall \"up\" into frames in front of the shape\n\t\t// if (pageShapes.indexOf(shape) < frameSortPosition) continue shapeCheck\n\n\t\t// Otherwise, we have no next dropping shape under the cursor, so go find\n\t\t// all the frames on the page where the moving shapes will fall into\n\t\tconst { reparenting, remainingShapesToReparent } = getDroppedShapesToNewParents(\n\t\t\teditor,\n\t\t\tlostChildren,\n\t\t\t(shape, maybeNewParent) => {\n\t\t\t\t// If we're filtering out a potential parent, don't reparent shapes to the filtered out shape\n\t\t\t\tif (opts?.filter && !opts.filter(maybeNewParent)) return false\n\t\t\t\treturn (\n\t\t\t\t\tmaybeNewParent.id !== prevParent.id &&\n\t\t\t\t\tsortedShapeIds.indexOf(maybeNewParent.id) < sortedShapeIds.indexOf(shape.id)\n\t\t\t\t)\n\t\t\t}\n\t\t)\n\n\t\treparenting.forEach((childrenToReparent, newParentId) => {\n\t\t\tif (childrenToReparent.length === 0) return\n\t\t\tif (!parentsToNewChildren[newParentId]) {\n\t\t\t\tparentsToNewChildren[newParentId] = {\n\t\t\t\t\tparentId: newParentId,\n\t\t\t\t\tshapeIds: [],\n\t\t\t\t}\n\t\t\t}\n\t\t\tparentsToNewChildren[newParentId].shapeIds.push(...childrenToReparent.map((s) => s.id))\n\t\t})\n\n\t\t// Reparent the rest to the page (or containing group)\n\t\tif (remainingShapesToReparent.size > 0) {\n\t\t\t// The remaining shapes are going to be reparented to the old parent's containing group, if there was one, or else to the page\n\t\t\tconst newParentId =\n\t\t\t\teditor.findShapeAncestor(prevParent, (s) => editor.isShapeOfType(s, 'group'))?.id ??\n\t\t\t\teditor.getCurrentPageId()\n\n\t\t\tremainingShapesToReparent.forEach((shape) => {\n\t\t\t\tif (!parentsToNewChildren[newParentId]) {\n\t\t\t\t\tlet insertIndexKey: IndexKey | undefined\n\n\t\t\t\t\tconst oldParentSiblingIds = editor.getSortedChildIdsForParent(newParentId)\n\t\t\t\t\tconst oldParentIndex = oldParentSiblingIds.indexOf(prevParent.id)\n\t\t\t\t\tif (oldParentIndex > -1) {\n\t\t\t\t\t\t// If the old parent is a direct child of the new parent, then we'll add them above the old parent but below the next sibling.\n\t\t\t\t\t\tconst siblingsIndexAbove = oldParentSiblingIds[oldParentIndex + 1]\n\t\t\t\t\t\tconst indexKeyAbove = siblingsIndexAbove\n\t\t\t\t\t\t\t? editor.getShape(siblingsIndexAbove)!.index\n\t\t\t\t\t\t\t: getIndexAbove(prevParent.index)\n\t\t\t\t\t\tinsertIndexKey = getIndexBetween(prevParent.index, indexKeyAbove)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If the old parent is not a direct child of the new parent, then we'll add them to the \"top\" of the new parent's children.\n\t\t\t\t\t\t// This is done automatically if we leave the index undefined, so let's do that.\n\t\t\t\t\t}\n\n\t\t\t\t\tparentsToNewChildren[newParentId] = {\n\t\t\t\t\t\tparentId: newParentId,\n\t\t\t\t\t\tshapeIds: [],\n\t\t\t\t\t\tindex: insertIndexKey,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tparentsToNewChildren[newParentId].shapeIds.push(shape.id)\n\t\t\t})\n\t\t}\n\t}\n\n\teditor.run(() => {\n\t\tObject.values(parentsToNewChildren).forEach(({ parentId, shapeIds, index }) => {\n\t\t\tif (shapeIds.length === 0) return\n\t\t\t// Before we reparent, sort the new shape ids by their place in the original absolute order on the page\n\t\t\tshapeIds.sort((a, b) => (sortedShapeIds.indexOf(a) < sortedShapeIds.indexOf(b) ? -1 : 1))\n\t\t\teditor.reparentShapes(shapeIds, parentId, index)\n\t\t})\n\t})\n}\n\n/**\n * Get the shapes that overlap with a given shape.\n *\n * @param editor - The editor instance.\n * @param shape - The shapes or shape IDs to check against.\n * @param otherShapes - The shapes or shape IDs to check for overlap.\n * @returns An array of shapes or shape IDs that overlap with the given shape.\n */\nfunction getOverlappingShapes<T extends TLShape[] | TLShapeId[]>(\n\teditor: Editor,\n\tshape: T[number],\n\totherShapes: T\n) {\n\tif (otherShapes.length === 0) {\n\t\treturn EMPTY_ARRAY\n\t}\n\n\tconst parentPageBounds = editor.getShapePageBounds(shape)\n\tif (!parentPageBounds) return EMPTY_ARRAY\n\n\tconst parentGeometry = editor.getShapeGeometry(shape)\n\tconst parentPageTransform = editor.getShapePageTransform(shape)\n\tconst parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices)\n\n\tconst _shape = editor.getShape(shape)\n\tif (!_shape) return EMPTY_ARRAY\n\n\tconst pageTransform = editor.getShapePageTransform(shape)\n\tconst clipPath = editor.getShapeUtil(_shape.type).getClipPath?.(_shape)\n\n\tconst parentPageMaskVertices = clipPath ? pageTransform.applyToPoints(clipPath) : undefined\n\tconst parentPagePolygon = parentPageMaskVertices\n\t\t? intersectPolygonPolygon(parentPageMaskVertices, parentPageCorners)\n\t\t: parentPageCorners\n\n\tif (!parentPagePolygon) return EMPTY_ARRAY\n\n\treturn otherShapes.filter((childId) => {\n\t\tconst shapePageBounds = editor.getShapePageBounds(childId)\n\t\tif (!shapePageBounds || !parentPageBounds.includes(shapePageBounds)) return false\n\n\t\tconst parentPolygonInShapeShape = editor\n\t\t\t.getShapePageTransform(childId)\n\t\t\t.clone()\n\t\t\t.invert()\n\t\t\t.applyToPoints(parentPagePolygon)\n\n\t\tconst geometry = editor.getShapeGeometry(childId)\n\n\t\treturn geometry.overlapsPolygon(parentPolygonInShapeShape)\n\t})\n}\n\n/**\n * Get the shapes that will be reparented to new parents when the shapes are dropped.\n *\n * @param editor - The editor instance.\n * @param shapes - The shapes to check.\n * @param cb - A callback to filter out certain shapes.\n * @returns An object with the shapes that will be reparented to new parents and the shapes that will be reparented to the page or their ancestral group.\n *\n * @public\n */\nexport function getDroppedShapesToNewParents(\n\teditor: Editor,\n\tshapes: Set<TLShape> | TLShape[],\n\tcb?: (shape: TLShape, parent: TLShape) => boolean\n) {\n\tconst shapesToActuallyCheck = new Set<TLShape>(shapes)\n\tconst movingGroups = new Set<TLGroupShape>()\n\n\tfor (const shape of shapes) {\n\t\tconst parent = editor.getShapeParent(shape)\n\t\tif (parent && editor.isShapeOfType(parent, 'group')) {\n\t\t\tif (!movingGroups.has(parent)) {\n\t\t\t\tmovingGroups.add(parent)\n\t\t\t}\n\t\t}\n\t}\n\n\t// If all of a group's children are moving, then move the group instead\n\tfor (const movingGroup of movingGroups) {\n\t\tconst children = compact(\n\t\t\teditor.getSortedChildIdsForParent(movingGroup).map((id) => editor.getShape(id))\n\t\t)\n\t\tfor (const child of children) {\n\t\t\tshapesToActuallyCheck.delete(child)\n\t\t}\n\t\tshapesToActuallyCheck.add(movingGroup)\n\t}\n\n\t// this could be cached and passed in\n\tconst shapeGroupIds = new Map<TLShapeId, TLShapeId | undefined>()\n\n\tconst reparenting = new Map<TLShapeId, TLShape[]>()\n\n\tconst remainingShapesToReparent = new Set(shapesToActuallyCheck)\n\n\tconst potentialParentShapes = editor\n\t\t.getCurrentPageShapesSorted()\n\t\t// filter out any shapes that aren't frames or that are included among the provided shapes\n\t\t.filter(\n\t\t\t(s) =>\n\t\t\t\teditor.getShapeUtil(s).canReceiveNewChildrenOfType?.(s, s.type) &&\n\t\t\t\t!remainingShapesToReparent.has(s)\n\t\t)\n\n\tparentCheck: for (let i = potentialParentShapes.length - 1; i >= 0; i--) {\n\t\tconst parentShape = potentialParentShapes[i]\n\t\tconst parentShapeContainingGroupId = editor.findShapeAncestor(parentShape, (s) =>\n\t\t\teditor.isShapeOfType(s, 'group')\n\t\t)?.id\n\n\t\tconst parentGeometry = editor.getShapeGeometry(parentShape)\n\t\tconst parentPageTransform = editor.getShapePageTransform(parentShape)\n\t\tconst parentPageMaskVertices = editor.getShapeMask(parentShape)\n\t\tconst parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices)\n\t\tconst parentPagePolygon = parentPageMaskVertices\n\t\t\t? intersectPolygonPolygon(parentPageMaskVertices, parentPageCorners)\n\t\t\t: parentPageCorners\n\n\t\tif (!parentPagePolygon) continue parentCheck\n\n\t\tconst childrenToReparent = []\n\n\t\t// For each of the dropping shapes...\n\t\tshapeCheck: for (const shape of remainingShapesToReparent) {\n\t\t\t// Don't reparent a frame to itself\n\t\t\tif (parentShape.id === shape.id) continue shapeCheck\n\n\t\t\t// Use the callback to filter out certain shapes\n\t\t\tif (cb && !cb(shape, parentShape)) continue shapeCheck\n\n\t\t\tif (!shapeGroupIds.has(shape.id)) {\n\t\t\t\tshapeGroupIds.set(\n\t\t\t\t\tshape.id,\n\t\t\t\t\teditor.findShapeAncestor(shape, (s) => editor.isShapeOfType(s, 'group'))?.id\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst shapeGroupId = shapeGroupIds.get(shape.id)\n\n\t\t\t// Are the shape and the parent part of different groups?\n\t\t\tif (shapeGroupId !== parentShapeContainingGroupId) continue shapeCheck\n\n\t\t\t// Is the shape is actually the ancestor of the parent?\n\t\t\tif (editor.findShapeAncestor(parentShape, (s) => shape.id === s.id)) continue shapeCheck\n\n\t\t\t// Convert the parent polygon to the shape's space\n\t\t\tconst parentPolygonInShapeSpace = editor\n\t\t\t\t.getShapePageTransform(shape)\n\t\t\t\t.clone()\n\t\t\t\t.invert()\n\t\t\t\t.applyToPoints(parentPagePolygon)\n\n\t\t\t// If the shape overlaps the parent polygon, reparent it to that parent\n\t\t\tif (editor.getShapeGeometry(shape).overlapsPolygon(parentPolygonInShapeSpace)) {\n\t\t\t\t// Use the util to check if the shape can be reparented to the parent\n\t\t\t\tif (\n\t\t\t\t\t!editor.getShapeUtil(parentShape).canReceiveNewChildrenOfType?.(parentShape, shape.type)\n\t\t\t\t)\n\t\t\t\t\tcontinue shapeCheck\n\n\t\t\t\tif (shape.parentId !== parentShape.id) {\n\t\t\t\t\tchildrenToReparent.push(shape)\n\t\t\t\t}\n\t\t\t\tremainingShapesToReparent.delete(shape)\n\t\t\t\tcontinue shapeCheck\n\t\t\t}\n\t\t}\n\n\t\tif (childrenToReparent.length) {\n\t\t\treparenting.set(parentShape.id, childrenToReparent)\n\t\t}\n\t}\n\n\treturn {\n\t\t// these are the shapes that will be reparented to new parents\n\t\treparenting,\n\t\t// these are the shapes that will be reparented to the page or their ancestral group\n\t\tremainingShapesToReparent,\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["shapeIds"]
7
7
  }
@@ -1,8 +1,8 @@
1
- const version = "4.3.0-canary.d8da2a99f394";
1
+ const version = "4.3.0-canary.e52fa5385f86";
2
2
  const publishDates = {
3
3
  major: "2025-09-18T14:39:22.803Z",
4
- minor: "2025-11-19T12:05:56.836Z",
5
- patch: "2025-11-19T12:05:56.836Z"
4
+ minor: "2025-12-05T21:53:17.454Z",
5
+ patch: "2025-12-05T21:53:17.454Z"
6
6
  };
7
7
  export {
8
8
  publishDates,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.3.0-canary.d8da2a99f394'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-11-19T12:05:56.836Z',\n\tpatch: '2025-11-19T12:05:56.836Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.3.0-canary.e52fa5385f86'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-12-05T21:53:17.454Z',\n\tpatch: '2025-12-05T21:53:17.454Z',\n}\n"],
5
5
  "mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
6
6
  "names": []
7
7
  }
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.3.0-canary.d8da2a99f394",
4
+ "version": "4.3.0-canary.e52fa5385f86",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -47,15 +47,15 @@
47
47
  "context": "yarn run -T tsx ../../internal/scripts/context.ts"
48
48
  },
49
49
  "dependencies": {
50
- "@tiptap/core": "3.6.2",
51
- "@tiptap/pm": "3.6.2",
52
- "@tiptap/react": "3.6.2",
53
- "@tldraw/state": "4.3.0-canary.d8da2a99f394",
54
- "@tldraw/state-react": "4.3.0-canary.d8da2a99f394",
55
- "@tldraw/store": "4.3.0-canary.d8da2a99f394",
56
- "@tldraw/tlschema": "4.3.0-canary.d8da2a99f394",
57
- "@tldraw/utils": "4.3.0-canary.d8da2a99f394",
58
- "@tldraw/validate": "4.3.0-canary.d8da2a99f394",
50
+ "@tiptap/core": "^3.6.2",
51
+ "@tiptap/pm": "^3.6.2",
52
+ "@tiptap/react": "^3.6.2",
53
+ "@tldraw/state": "4.3.0-canary.e52fa5385f86",
54
+ "@tldraw/state-react": "4.3.0-canary.e52fa5385f86",
55
+ "@tldraw/store": "4.3.0-canary.e52fa5385f86",
56
+ "@tldraw/tlschema": "4.3.0-canary.e52fa5385f86",
57
+ "@tldraw/utils": "4.3.0-canary.e52fa5385f86",
58
+ "@tldraw/validate": "4.3.0-canary.e52fa5385f86",
59
59
  "@types/core-js": "^2.5.8",
60
60
  "@use-gesture/react": "^10.3.1",
61
61
  "classnames": "^2.5.1",
package/src/index.ts CHANGED
@@ -282,7 +282,7 @@ export {
282
282
  type SvgExportDef,
283
283
  } from './lib/editor/types/SvgExportContext'
284
284
  export { getSvgAsImage } from './lib/exports/getSvgAsImage'
285
- export { tlenv } from './lib/globals/environment'
285
+ export { tlenv, tlenvReactive } from './lib/globals/environment'
286
286
  export { tlmenus } from './lib/globals/menus'
287
287
  export { tltime } from './lib/globals/time'
288
288
  export {
@@ -86,6 +86,7 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
86
86
  const transform = `scale(${toDomPrecision(z)}) translate(${toDomPrecision(
87
87
  x + offset
88
88
  )}px,${toDomPrecision(y + offset)}px)`
89
+
89
90
  setStyleProperty(rHtmlLayer.current, 'transform', transform)
90
91
  setStyleProperty(rHtmlLayer2.current, 'transform', transform)
91
92
  },
@@ -6,25 +6,25 @@ import {
6
6
  Rectangle2d,
7
7
  ShapeUtil,
8
8
  T,
9
- TLBaseShape,
9
+ TLShape,
10
10
  createShapeId,
11
11
  createTLStore,
12
12
  } from '../..'
13
13
  import { Editor } from './Editor'
14
14
  import { StateNode } from './tools/StateNode'
15
15
 
16
- type ICustomShape = TLBaseShape<
17
- 'my-custom-shape',
18
- {
19
- w: number
20
- h: number
21
- text: string | undefined
22
- isFilled: boolean
16
+ const MY_CUSTOM_SHAPE_TYPE = 'my-custom-shape'
17
+
18
+ declare module '@tldraw/tlschema' {
19
+ export interface TLGlobalShapePropsMap {
20
+ [MY_CUSTOM_SHAPE_TYPE]: { w: number; h: number; text: string | undefined; isFilled: boolean }
23
21
  }
24
- >
22
+ }
23
+
24
+ type ICustomShape = TLShape<typeof MY_CUSTOM_SHAPE_TYPE>
25
25
 
26
26
  class CustomShape extends ShapeUtil<ICustomShape> {
27
- static override type = 'my-custom-shape' as const
27
+ static override type = MY_CUSTOM_SHAPE_TYPE
28
28
  static override props: RecordProps<ICustomShape> = {
29
29
  w: T.number,
30
30
  h: T.number,