@tldraw/editor 3.12.0-canary.5fcdc0f29d81 → 3.12.0-canary.629125a2e474

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 (90) hide show
  1. package/dist-cjs/index.d.ts +154 -17
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +4 -0
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/GeometryDebuggingView.js +2 -2
  7. package/dist-cjs/lib/components/GeometryDebuggingView.js.map +2 -2
  8. package/dist-cjs/lib/editor/Editor.js +215 -30
  9. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  10. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +9 -0
  11. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  12. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +4 -13
  13. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  14. package/dist-cjs/lib/editor/types/selection-types.js.map +1 -1
  15. package/dist-cjs/lib/exports/StyleEmbedder.js +19 -5
  16. package/dist-cjs/lib/exports/StyleEmbedder.js.map +2 -2
  17. package/dist-cjs/lib/exports/cssRules.js +127 -0
  18. package/dist-cjs/lib/exports/cssRules.js.map +7 -0
  19. package/dist-cjs/lib/exports/parseCss.js +0 -69
  20. package/dist-cjs/lib/exports/parseCss.js.map +2 -2
  21. package/dist-cjs/lib/hooks/useDocumentEvents.js +16 -0
  22. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  23. package/dist-cjs/lib/hooks/useGestureEvents.js +12 -6
  24. package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
  25. package/dist-cjs/lib/license/Watermark.js +2 -0
  26. package/dist-cjs/lib/license/Watermark.js.map +2 -2
  27. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +133 -16
  28. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +3 -3
  29. package/dist-cjs/lib/primitives/geometry/Group2d.js +54 -11
  30. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  31. package/dist-cjs/lib/primitives/intersect.js +20 -0
  32. package/dist-cjs/lib/primitives/intersect.js.map +2 -2
  33. package/dist-cjs/lib/utils/reorderShapes.js +2 -8
  34. package/dist-cjs/lib/utils/reorderShapes.js.map +2 -2
  35. package/dist-cjs/version.js +3 -3
  36. package/dist-cjs/version.js.map +1 -1
  37. package/dist-esm/index.d.mts +154 -17
  38. package/dist-esm/index.mjs +8 -2
  39. package/dist-esm/index.mjs.map +2 -2
  40. package/dist-esm/lib/TldrawEditor.mjs +4 -0
  41. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  42. package/dist-esm/lib/components/GeometryDebuggingView.mjs +3 -3
  43. package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +2 -2
  44. package/dist-esm/lib/editor/Editor.mjs +216 -30
  45. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  46. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +9 -0
  47. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  48. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +4 -13
  49. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  50. package/dist-esm/lib/exports/StyleEmbedder.mjs +21 -12
  51. package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
  52. package/dist-esm/lib/exports/cssRules.mjs +107 -0
  53. package/dist-esm/lib/exports/cssRules.mjs.map +7 -0
  54. package/dist-esm/lib/exports/parseCss.mjs +0 -69
  55. package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
  56. package/dist-esm/lib/hooks/useDocumentEvents.mjs +16 -0
  57. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  58. package/dist-esm/lib/hooks/useGestureEvents.mjs +12 -6
  59. package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
  60. package/dist-esm/lib/license/Watermark.mjs +2 -0
  61. package/dist-esm/lib/license/Watermark.mjs.map +2 -2
  62. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +137 -14
  63. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  64. package/dist-esm/lib/primitives/geometry/Group2d.mjs +55 -12
  65. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  66. package/dist-esm/lib/primitives/intersect.mjs +20 -0
  67. package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
  68. package/dist-esm/lib/utils/reorderShapes.mjs +2 -8
  69. package/dist-esm/lib/utils/reorderShapes.mjs.map +2 -2
  70. package/dist-esm/version.mjs +3 -3
  71. package/dist-esm/version.mjs.map +1 -1
  72. package/package.json +7 -7
  73. package/src/index.ts +11 -2
  74. package/src/lib/TldrawEditor.tsx +29 -3
  75. package/src/lib/components/GeometryDebuggingView.tsx +3 -3
  76. package/src/lib/editor/Editor.ts +322 -36
  77. package/src/lib/editor/shapes/ShapeUtil.ts +10 -0
  78. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +7 -15
  79. package/src/lib/editor/types/selection-types.ts +3 -0
  80. package/src/lib/exports/StyleEmbedder.ts +25 -15
  81. package/src/lib/exports/cssRules.ts +126 -0
  82. package/src/lib/exports/parseCss.ts +0 -79
  83. package/src/lib/hooks/useDocumentEvents.ts +18 -0
  84. package/src/lib/hooks/useGestureEvents.ts +12 -6
  85. package/src/lib/license/Watermark.tsx +2 -0
  86. package/src/lib/primitives/geometry/Geometry2d.ts +196 -16
  87. package/src/lib/primitives/geometry/Group2d.ts +76 -13
  88. package/src/lib/primitives/intersect.ts +41 -0
  89. package/src/lib/utils/reorderShapes.ts +2 -9
  90. package/src/version.ts +3 -3
@@ -69,6 +69,7 @@ function useDocumentEvents() {
69
69
  }
70
70
  if (e.isKilled) return;
71
71
  e.isKilled = true;
72
+ const hasSelectedShapes = !!editor.getSelectedShapeIds().length;
72
73
  switch (e.key) {
73
74
  case "=":
74
75
  case "-":
@@ -83,6 +84,21 @@ function useDocumentEvents() {
83
84
  if (areShortcutsDisabled(editor)) {
84
85
  return;
85
86
  }
87
+ if (hasSelectedShapes) {
88
+ preventDefault(e);
89
+ }
90
+ break;
91
+ }
92
+ case "ArrowLeft":
93
+ case "ArrowRight":
94
+ case "ArrowUp":
95
+ case "ArrowDown": {
96
+ if (areShortcutsDisabled(editor)) {
97
+ return;
98
+ }
99
+ if (hasSelectedShapes && (e.metaKey || e.ctrlKey)) {
100
+ preventDefault(e);
101
+ }
86
102
  break;
87
103
  }
88
104
  case ",": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/hooks/useDocumentEvents.ts"],
4
- "sourcesContent": ["import { useValue } from '@tldraw/state-react'\nimport { useEffect } from 'react'\nimport { Editor } from '../editor/Editor'\nimport { TLKeyboardEventInfo } from '../editor/types/event-types'\nimport { activeElementShouldCaptureKeys, preventDefault, stopEventPropagation } from '../utils/dom'\nimport { isAccelKey } from '../utils/keyboard'\nimport { useContainer } from './useContainer'\nimport { useEditor } from './useEditor'\n\nexport function useDocumentEvents() {\n\tconst editor = useEditor()\n\tconst container = useContainer()\n\n\tconst isAppFocused = useValue('isFocused', () => editor.getIsFocused(), [editor])\n\n\t// Prevent the browser's default drag and drop behavior on our container (UI, etc)\n\tuseEffect(() => {\n\t\tif (!container) return\n\n\t\tfunction onDrop(e: DragEvent) {\n\t\t\t// this is tricky: we don't want the event to do anything\n\t\t\t// here, but we do want it to make its way to the canvas,\n\t\t\t// even if the drop is over some other element (like a toolbar),\n\t\t\t// so we're going to flag the event and then dispatch\n\t\t\t// it to the canvas; the canvas will handle it and try to\n\t\t\t// stop it from propagating back, but in case we do see it again,\n\t\t\t// we'll look for the flag so we know to stop it from being\n\t\t\t// re-dispatched, which would lead to an infinite loop.\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\t\t\tpreventDefault(e)\n\t\t\tstopEventPropagation(e)\n\t\t\tconst cvs = container.querySelector('.tl-canvas')\n\t\t\tif (!cvs) return\n\t\t\tconst newEvent = new DragEvent(e.type, e)\n\t\t\t;(newEvent as any).isSpecialRedispatchedEvent = true\n\t\t\tcvs.dispatchEvent(newEvent)\n\t\t}\n\n\t\tcontainer.addEventListener('dragover', onDrop)\n\t\tcontainer.addEventListener('drop', onDrop)\n\t\treturn () => {\n\t\t\tcontainer.removeEventListener('dragover', onDrop)\n\t\t\tcontainer.removeEventListener('drop', onDrop)\n\t\t}\n\t}, [container])\n\n\tuseEffect(() => {\n\t\tif (typeof window === 'undefined' || !('matchMedia' in window)) return\n\n\t\t// https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#monitoring_screen_resolution_or_zoom_level_changes\n\t\tlet remove: (() => void) | null = null\n\t\tconst updatePixelRatio = () => {\n\t\t\tif (remove != null) {\n\t\t\t\tremove()\n\t\t\t}\n\t\t\tconst mqString = `(resolution: ${window.devicePixelRatio}dppx)`\n\t\t\tconst media = matchMedia(mqString)\n\t\t\t// Safari only started supporting `addEventListener('change',...) in version 14\n\t\t\t// https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/change_event\n\t\t\tconst safariCb = (ev: any) => {\n\t\t\t\tif (ev.type === 'change') {\n\t\t\t\t\tupdatePixelRatio()\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (media.addEventListener) {\n\t\t\t\tmedia.addEventListener('change', updatePixelRatio)\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t} else if (media.addListener) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\tmedia.addListener(safariCb)\n\t\t\t}\n\t\t\tremove = () => {\n\t\t\t\tif (media.removeEventListener) {\n\t\t\t\t\tmedia.removeEventListener('change', updatePixelRatio)\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t} else if (media.removeListener) {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t\tmedia.removeListener(safariCb)\n\t\t\t\t}\n\t\t\t}\n\t\t\teditor.updateInstanceState({ devicePixelRatio: window.devicePixelRatio })\n\t\t}\n\t\tupdatePixelRatio()\n\t\treturn () => {\n\t\t\tremove?.()\n\t\t}\n\t}, [editor])\n\n\tuseEffect(() => {\n\t\tif (!isAppFocused) return\n\n\t\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\t\tif (\n\t\t\t\te.altKey &&\n\t\t\t\t// todo: When should we allow the alt key to be used? Perhaps states should declare which keys matter to them?\n\t\t\t\t(editor.isIn('zoom') || !editor.getPath().endsWith('.idle')) &&\n\t\t\t\t!areShortcutsDisabled(editor)\n\t\t\t) {\n\t\t\t\t// On windows the alt key opens the menu bar.\n\t\t\t\t// We want to prevent that if the user is doing something else,\n\t\t\t\t// e.g. resizing a shape\n\t\t\t\tpreventDefault(e)\n\t\t\t}\n\n\t\t\tif ((e as any).isKilled) return\n\t\t\t;(e as any).isKilled = true\n\n\t\t\tswitch (e.key) {\n\t\t\t\tcase '=':\n\t\t\t\tcase '-':\n\t\t\t\tcase '0': {\n\t\t\t\t\t// These keys are used for zooming. Technically we only use\n\t\t\t\t\t// the + - and 0 keys, however it's common for them to be\n\t\t\t\t\t// paired with modifier keys (command / control) so we need\n\t\t\t\t\t// to prevent the browser's regular actions (i.e. zooming\n\t\t\t\t\t// the page). A user can zoom by unfocusing the editor.\n\t\t\t\t\tif (e.metaKey || e.ctrlKey) {\n\t\t\t\t\t\tpreventDefault(e)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'Tab': {\n\t\t\t\t\tif (areShortcutsDisabled(editor)) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase ',': {\n\t\t\t\t\t// this was moved to useKeyBoardShortcuts; it's possible\n\t\t\t\t\t// that the comma key is pressed when the container is not\n\t\t\t\t\t// focused, for example when the user has just interacted\n\t\t\t\t\t// with the toolbar. We need to handle it on the window\n\t\t\t\t\t// (ofc ensuring it's a correct time for a shortcut)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tcase 'Escape': {\n\t\t\t\t\t// In certain browsers, pressing escape while in full screen mode\n\t\t\t\t\t// will exit full screen mode. We want to allow that, but not when\n\t\t\t\t\t// escape is being handled by the editor. When a user has an editing\n\t\t\t\t\t// shape, escape stops editing. When a user is using a tool, escape\n\t\t\t\t\t// returns to the select tool. When the user has selected shapes,\n\t\t\t\t\t// escape de-selects them. Only when the user's selection is empty\n\t\t\t\t\t// should we allow escape to do its normal thing.\n\n\t\t\t\t\tif (editor.getEditingShape() || editor.getSelectedShapeIds().length > 0) {\n\t\t\t\t\t\tpreventDefault(e)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Don't do anything if we open menus open\n\t\t\t\t\tif (editor.menus.getOpenMenus().length > 0) return\n\n\t\t\t\t\tif (editor.inputs.keys.has('Escape')) {\n\t\t\t\t\t\t// noop\n\t\t\t\t\t} else {\n\t\t\t\t\t\teditor.inputs.keys.add('Escape')\n\n\t\t\t\t\t\teditor.cancel()\n\t\t\t\t\t\t// Pressing escape will focus the document.body,\n\t\t\t\t\t\t// which will cause the app to lose focus, which\n\t\t\t\t\t\t// will break additional shortcuts. We need to\n\t\t\t\t\t\t// refocus the container in order to keep these\n\t\t\t\t\t\t// shortcuts working.\n\t\t\t\t\t\tcontainer.focus()\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tif (areShortcutsDisabled(editor)) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst info: TLKeyboardEventInfo = {\n\t\t\t\ttype: 'keyboard',\n\t\t\t\tname: e.repeat ? 'key_repeat' : 'key_down',\n\t\t\t\tkey: e.key,\n\t\t\t\tcode: e.code,\n\t\t\t\tshiftKey: e.shiftKey,\n\t\t\t\taltKey: e.altKey,\n\t\t\t\tctrlKey: e.metaKey || e.ctrlKey,\n\t\t\t\tmetaKey: e.metaKey,\n\t\t\t\taccelKey: isAccelKey(e),\n\t\t\t}\n\n\t\t\teditor.dispatch(info)\n\t\t}\n\n\t\tconst handleKeyUp = (e: KeyboardEvent) => {\n\t\t\tif ((e as any).isKilled) return\n\t\t\t;(e as any).isKilled = true\n\n\t\t\tif (areShortcutsDisabled(editor)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (e.key === ',') {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst info: TLKeyboardEventInfo = {\n\t\t\t\ttype: 'keyboard',\n\t\t\t\tname: 'key_up',\n\t\t\t\tkey: e.key,\n\t\t\t\tcode: e.code,\n\t\t\t\tshiftKey: e.shiftKey,\n\t\t\t\taltKey: e.altKey,\n\t\t\t\tctrlKey: e.metaKey || e.ctrlKey,\n\t\t\t\tmetaKey: e.metaKey,\n\t\t\t\taccelKey: isAccelKey(e),\n\t\t\t}\n\n\t\t\teditor.dispatch(info)\n\t\t}\n\n\t\tfunction handleTouchStart(e: TouchEvent) {\n\t\t\tif (container.contains(e.target as Node)) {\n\t\t\t\t// Center point of the touch area\n\t\t\t\tconst touchXPosition = e.touches[0].pageX\n\t\t\t\t// Size of the touch area\n\t\t\t\tconst touchXRadius = e.touches[0].radiusX || 0\n\n\t\t\t\t// We set a threshold (10px) on both sizes of the screen,\n\t\t\t\t// if the touch area overlaps with the screen edges\n\t\t\t\t// it's likely to trigger the navigation. We prevent the\n\t\t\t\t// touchstart event in that case.\n\t\t\t\t// todo: make this relative to the actual window, not the editor's screen bounds\n\t\t\t\tif (\n\t\t\t\t\ttouchXPosition - touchXRadius < 10 ||\n\t\t\t\t\ttouchXPosition + touchXRadius > editor.getViewportScreenBounds().width - 10\n\t\t\t\t) {\n\t\t\t\t\tif ((e.target as HTMLElement)?.tagName === 'BUTTON') {\n\t\t\t\t\t\t// Force a click before bailing\n\t\t\t\t\t\t;(e.target as HTMLButtonElement)?.click()\n\t\t\t\t\t}\n\n\t\t\t\t\tpreventDefault(e)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Prevent wheel events that occur inside of the container\n\t\tconst handleWheel = (e: WheelEvent) => {\n\t\t\t// Ctrl/Meta key indicates a pinch event (funny, eh?)\n\t\t\tif (container.contains(e.target as Node) && (e.ctrlKey || e.metaKey)) {\n\t\t\t\tpreventDefault(e)\n\t\t\t}\n\t\t}\n\n\t\tcontainer.addEventListener('touchstart', handleTouchStart, { passive: false })\n\n\t\tcontainer.addEventListener('wheel', handleWheel, { passive: false })\n\n\t\tdocument.addEventListener('gesturestart', preventDefault)\n\t\tdocument.addEventListener('gesturechange', preventDefault)\n\t\tdocument.addEventListener('gestureend', preventDefault)\n\n\t\tcontainer.addEventListener('keydown', handleKeyDown)\n\t\tcontainer.addEventListener('keyup', handleKeyUp)\n\n\t\treturn () => {\n\t\t\tcontainer.removeEventListener('touchstart', handleTouchStart)\n\n\t\t\tcontainer.removeEventListener('wheel', handleWheel)\n\n\t\t\tdocument.removeEventListener('gesturestart', preventDefault)\n\t\t\tdocument.removeEventListener('gesturechange', preventDefault)\n\t\t\tdocument.removeEventListener('gestureend', preventDefault)\n\n\t\t\tcontainer.removeEventListener('keydown', handleKeyDown)\n\t\t\tcontainer.removeEventListener('keyup', handleKeyUp)\n\t\t}\n\t}, [editor, container, isAppFocused])\n}\n\nfunction areShortcutsDisabled(editor: Editor) {\n\treturn editor.menus.hasOpenMenus() || activeElementShouldCaptureKeys()\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { useValue } from '@tldraw/state-react'\nimport { useEffect } from 'react'\nimport { Editor } from '../editor/Editor'\nimport { TLKeyboardEventInfo } from '../editor/types/event-types'\nimport { activeElementShouldCaptureKeys, preventDefault, stopEventPropagation } from '../utils/dom'\nimport { isAccelKey } from '../utils/keyboard'\nimport { useContainer } from './useContainer'\nimport { useEditor } from './useEditor'\n\nexport function useDocumentEvents() {\n\tconst editor = useEditor()\n\tconst container = useContainer()\n\n\tconst isAppFocused = useValue('isFocused', () => editor.getIsFocused(), [editor])\n\n\t// Prevent the browser's default drag and drop behavior on our container (UI, etc)\n\tuseEffect(() => {\n\t\tif (!container) return\n\n\t\tfunction onDrop(e: DragEvent) {\n\t\t\t// this is tricky: we don't want the event to do anything\n\t\t\t// here, but we do want it to make its way to the canvas,\n\t\t\t// even if the drop is over some other element (like a toolbar),\n\t\t\t// so we're going to flag the event and then dispatch\n\t\t\t// it to the canvas; the canvas will handle it and try to\n\t\t\t// stop it from propagating back, but in case we do see it again,\n\t\t\t// we'll look for the flag so we know to stop it from being\n\t\t\t// re-dispatched, which would lead to an infinite loop.\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\t\t\tpreventDefault(e)\n\t\t\tstopEventPropagation(e)\n\t\t\tconst cvs = container.querySelector('.tl-canvas')\n\t\t\tif (!cvs) return\n\t\t\tconst newEvent = new DragEvent(e.type, e)\n\t\t\t;(newEvent as any).isSpecialRedispatchedEvent = true\n\t\t\tcvs.dispatchEvent(newEvent)\n\t\t}\n\n\t\tcontainer.addEventListener('dragover', onDrop)\n\t\tcontainer.addEventListener('drop', onDrop)\n\t\treturn () => {\n\t\t\tcontainer.removeEventListener('dragover', onDrop)\n\t\t\tcontainer.removeEventListener('drop', onDrop)\n\t\t}\n\t}, [container])\n\n\tuseEffect(() => {\n\t\tif (typeof window === 'undefined' || !('matchMedia' in window)) return\n\n\t\t// https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#monitoring_screen_resolution_or_zoom_level_changes\n\t\tlet remove: (() => void) | null = null\n\t\tconst updatePixelRatio = () => {\n\t\t\tif (remove != null) {\n\t\t\t\tremove()\n\t\t\t}\n\t\t\tconst mqString = `(resolution: ${window.devicePixelRatio}dppx)`\n\t\t\tconst media = matchMedia(mqString)\n\t\t\t// Safari only started supporting `addEventListener('change',...) in version 14\n\t\t\t// https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/change_event\n\t\t\tconst safariCb = (ev: any) => {\n\t\t\t\tif (ev.type === 'change') {\n\t\t\t\t\tupdatePixelRatio()\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (media.addEventListener) {\n\t\t\t\tmedia.addEventListener('change', updatePixelRatio)\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t} else if (media.addListener) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\tmedia.addListener(safariCb)\n\t\t\t}\n\t\t\tremove = () => {\n\t\t\t\tif (media.removeEventListener) {\n\t\t\t\t\tmedia.removeEventListener('change', updatePixelRatio)\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t} else if (media.removeListener) {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t\tmedia.removeListener(safariCb)\n\t\t\t\t}\n\t\t\t}\n\t\t\teditor.updateInstanceState({ devicePixelRatio: window.devicePixelRatio })\n\t\t}\n\t\tupdatePixelRatio()\n\t\treturn () => {\n\t\t\tremove?.()\n\t\t}\n\t}, [editor])\n\n\tuseEffect(() => {\n\t\tif (!isAppFocused) return\n\n\t\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\t\tif (\n\t\t\t\te.altKey &&\n\t\t\t\t// todo: When should we allow the alt key to be used? Perhaps states should declare which keys matter to them?\n\t\t\t\t(editor.isIn('zoom') || !editor.getPath().endsWith('.idle')) &&\n\t\t\t\t!areShortcutsDisabled(editor)\n\t\t\t) {\n\t\t\t\t// On windows the alt key opens the menu bar.\n\t\t\t\t// We want to prevent that if the user is doing something else,\n\t\t\t\t// e.g. resizing a shape\n\t\t\t\tpreventDefault(e)\n\t\t\t}\n\n\t\t\tif ((e as any).isKilled) return\n\t\t\t;(e as any).isKilled = true\n\t\t\tconst hasSelectedShapes = !!editor.getSelectedShapeIds().length\n\n\t\t\tswitch (e.key) {\n\t\t\t\tcase '=':\n\t\t\t\tcase '-':\n\t\t\t\tcase '0': {\n\t\t\t\t\t// These keys are used for zooming. Technically we only use\n\t\t\t\t\t// the + - and 0 keys, however it's common for them to be\n\t\t\t\t\t// paired with modifier keys (command / control) so we need\n\t\t\t\t\t// to prevent the browser's regular actions (i.e. zooming\n\t\t\t\t\t// the page). A user can zoom by unfocusing the editor.\n\t\t\t\t\tif (e.metaKey || e.ctrlKey) {\n\t\t\t\t\t\tpreventDefault(e)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'Tab': {\n\t\t\t\t\tif (areShortcutsDisabled(editor)) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif (hasSelectedShapes) {\n\t\t\t\t\t\t// This is used in tandem with shape navigation.\n\t\t\t\t\t\tpreventDefault(e)\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'ArrowLeft':\n\t\t\t\tcase 'ArrowRight':\n\t\t\t\tcase 'ArrowUp':\n\t\t\t\tcase 'ArrowDown': {\n\t\t\t\t\tif (areShortcutsDisabled(editor)) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif (hasSelectedShapes && (e.metaKey || e.ctrlKey)) {\n\t\t\t\t\t\t// This is used in tandem with shape navigation.\n\t\t\t\t\t\tpreventDefault(e)\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase ',': {\n\t\t\t\t\t// this was moved to useKeyBoardShortcuts; it's possible\n\t\t\t\t\t// that the comma key is pressed when the container is not\n\t\t\t\t\t// focused, for example when the user has just interacted\n\t\t\t\t\t// with the toolbar. We need to handle it on the window\n\t\t\t\t\t// (ofc ensuring it's a correct time for a shortcut)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tcase 'Escape': {\n\t\t\t\t\t// In certain browsers, pressing escape while in full screen mode\n\t\t\t\t\t// will exit full screen mode. We want to allow that, but not when\n\t\t\t\t\t// escape is being handled by the editor. When a user has an editing\n\t\t\t\t\t// shape, escape stops editing. When a user is using a tool, escape\n\t\t\t\t\t// returns to the select tool. When the user has selected shapes,\n\t\t\t\t\t// escape de-selects them. Only when the user's selection is empty\n\t\t\t\t\t// should we allow escape to do its normal thing.\n\n\t\t\t\t\tif (editor.getEditingShape() || editor.getSelectedShapeIds().length > 0) {\n\t\t\t\t\t\tpreventDefault(e)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Don't do anything if we open menus open\n\t\t\t\t\tif (editor.menus.getOpenMenus().length > 0) return\n\n\t\t\t\t\tif (editor.inputs.keys.has('Escape')) {\n\t\t\t\t\t\t// noop\n\t\t\t\t\t} else {\n\t\t\t\t\t\teditor.inputs.keys.add('Escape')\n\n\t\t\t\t\t\teditor.cancel()\n\t\t\t\t\t\t// Pressing escape will focus the document.body,\n\t\t\t\t\t\t// which will cause the app to lose focus, which\n\t\t\t\t\t\t// will break additional shortcuts. We need to\n\t\t\t\t\t\t// refocus the container in order to keep these\n\t\t\t\t\t\t// shortcuts working.\n\t\t\t\t\t\tcontainer.focus()\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tif (areShortcutsDisabled(editor)) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst info: TLKeyboardEventInfo = {\n\t\t\t\ttype: 'keyboard',\n\t\t\t\tname: e.repeat ? 'key_repeat' : 'key_down',\n\t\t\t\tkey: e.key,\n\t\t\t\tcode: e.code,\n\t\t\t\tshiftKey: e.shiftKey,\n\t\t\t\taltKey: e.altKey,\n\t\t\t\tctrlKey: e.metaKey || e.ctrlKey,\n\t\t\t\tmetaKey: e.metaKey,\n\t\t\t\taccelKey: isAccelKey(e),\n\t\t\t}\n\n\t\t\teditor.dispatch(info)\n\t\t}\n\n\t\tconst handleKeyUp = (e: KeyboardEvent) => {\n\t\t\tif ((e as any).isKilled) return\n\t\t\t;(e as any).isKilled = true\n\n\t\t\tif (areShortcutsDisabled(editor)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (e.key === ',') {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst info: TLKeyboardEventInfo = {\n\t\t\t\ttype: 'keyboard',\n\t\t\t\tname: 'key_up',\n\t\t\t\tkey: e.key,\n\t\t\t\tcode: e.code,\n\t\t\t\tshiftKey: e.shiftKey,\n\t\t\t\taltKey: e.altKey,\n\t\t\t\tctrlKey: e.metaKey || e.ctrlKey,\n\t\t\t\tmetaKey: e.metaKey,\n\t\t\t\taccelKey: isAccelKey(e),\n\t\t\t}\n\n\t\t\teditor.dispatch(info)\n\t\t}\n\n\t\tfunction handleTouchStart(e: TouchEvent) {\n\t\t\tif (container.contains(e.target as Node)) {\n\t\t\t\t// Center point of the touch area\n\t\t\t\tconst touchXPosition = e.touches[0].pageX\n\t\t\t\t// Size of the touch area\n\t\t\t\tconst touchXRadius = e.touches[0].radiusX || 0\n\n\t\t\t\t// We set a threshold (10px) on both sizes of the screen,\n\t\t\t\t// if the touch area overlaps with the screen edges\n\t\t\t\t// it's likely to trigger the navigation. We prevent the\n\t\t\t\t// touchstart event in that case.\n\t\t\t\t// todo: make this relative to the actual window, not the editor's screen bounds\n\t\t\t\tif (\n\t\t\t\t\ttouchXPosition - touchXRadius < 10 ||\n\t\t\t\t\ttouchXPosition + touchXRadius > editor.getViewportScreenBounds().width - 10\n\t\t\t\t) {\n\t\t\t\t\tif ((e.target as HTMLElement)?.tagName === 'BUTTON') {\n\t\t\t\t\t\t// Force a click before bailing\n\t\t\t\t\t\t;(e.target as HTMLButtonElement)?.click()\n\t\t\t\t\t}\n\n\t\t\t\t\tpreventDefault(e)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Prevent wheel events that occur inside of the container\n\t\tconst handleWheel = (e: WheelEvent) => {\n\t\t\t// Ctrl/Meta key indicates a pinch event (funny, eh?)\n\t\t\tif (container.contains(e.target as Node) && (e.ctrlKey || e.metaKey)) {\n\t\t\t\tpreventDefault(e)\n\t\t\t}\n\t\t}\n\n\t\tcontainer.addEventListener('touchstart', handleTouchStart, { passive: false })\n\n\t\tcontainer.addEventListener('wheel', handleWheel, { passive: false })\n\n\t\tdocument.addEventListener('gesturestart', preventDefault)\n\t\tdocument.addEventListener('gesturechange', preventDefault)\n\t\tdocument.addEventListener('gestureend', preventDefault)\n\n\t\tcontainer.addEventListener('keydown', handleKeyDown)\n\t\tcontainer.addEventListener('keyup', handleKeyUp)\n\n\t\treturn () => {\n\t\t\tcontainer.removeEventListener('touchstart', handleTouchStart)\n\n\t\t\tcontainer.removeEventListener('wheel', handleWheel)\n\n\t\t\tdocument.removeEventListener('gesturestart', preventDefault)\n\t\t\tdocument.removeEventListener('gesturechange', preventDefault)\n\t\t\tdocument.removeEventListener('gestureend', preventDefault)\n\n\t\t\tcontainer.removeEventListener('keydown', handleKeyDown)\n\t\t\tcontainer.removeEventListener('keyup', handleKeyUp)\n\t\t}\n\t}, [editor, container, isAppFocused])\n}\n\nfunction areShortcutsDisabled(editor: Editor) {\n\treturn editor.menus.hasOpenMenus() || activeElementShouldCaptureKeys()\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -63,7 +63,6 @@ function useGestureEvents(ref) {
63
63
  };
64
64
  let initDistanceBetweenFingers = 1;
65
65
  let initZoom = 1;
66
- let currZoom = 1;
67
66
  let currDistanceBetweenFingers = 0;
68
67
  const initPointBetweenFingers = new Vec();
69
68
  const prevPointBetweenFingers = new Vec();
@@ -131,7 +130,7 @@ function useGestureEvents(ref) {
131
130
  updatePinchState(isSafariTrackpadPinch);
132
131
  switch (pinchState) {
133
132
  case "zooming": {
134
- currZoom = offset[0];
133
+ const currZoom = offset[0] ** editor.getCameraOptions().zoomSpeed;
135
134
  editor.dispatch({
136
135
  type: "pinch",
137
136
  name: "pinch",
@@ -166,7 +165,7 @@ function useGestureEvents(ref) {
166
165
  const { event, origin, offset } = gesture;
167
166
  if (event instanceof WheelEvent) return;
168
167
  if (!(event.target === elm || elm?.contains(event.target))) return;
169
- const scale = offset[0];
168
+ const scale = offset[0] ** editor.getCameraOptions().zoomSpeed;
170
169
  pinchState = "not sure";
171
170
  editor.timers.requestAnimationFrame(() => {
172
171
  editor.dispatch({
@@ -193,14 +192,21 @@ function useGestureEvents(ref) {
193
192
  target: ref,
194
193
  eventOptions: { passive: false },
195
194
  pinch: {
196
- from: () => [editor.getZoomLevel(), 0],
195
+ from: () => {
196
+ const { zoomSpeed } = editor.getCameraOptions();
197
+ const level = editor.getZoomLevel() ** (1 / zoomSpeed);
198
+ return [level, 0];
199
+ },
197
200
  // Return the camera z to use when pinch starts
198
201
  scaleBounds: () => {
199
202
  const baseZoom = editor.getBaseZoom();
200
- const zoomSteps = editor.getCameraOptions().zoomSteps;
203
+ const { zoomSteps, zoomSpeed } = editor.getCameraOptions();
201
204
  const zoomMin = zoomSteps[0] * baseZoom;
202
205
  const zoomMax = zoomSteps[zoomSteps.length - 1] * baseZoom;
203
- return { from: editor.getZoomLevel(), max: zoomMax, min: zoomMin };
206
+ return {
207
+ max: zoomMax ** (1 / zoomSpeed),
208
+ min: zoomMin ** (1 / zoomSpeed)
209
+ };
204
210
  }
205
211
  }
206
212
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/hooks/useGestureEvents.ts"],
4
- "sourcesContent": ["import type { AnyHandlerEventTypes, EventTypes, GestureKey, Handler } from '@use-gesture/core/types'\nimport { createUseGesture, pinchAction, wheelAction } from '@use-gesture/react'\nimport * as React from 'react'\nimport { TLWheelEventInfo } from '../editor/types/event-types'\nimport { Vec } from '../primitives/Vec'\nimport { preventDefault, stopEventPropagation } from '../utils/dom'\nimport { isAccelKey } from '../utils/keyboard'\nimport { normalizeWheel } from '../utils/normalizeWheel'\nimport { useEditor } from './useEditor'\n\n/*\n\n# How does pinching work?\n\nThe pinching handler is fired under two circumstances: \n- when a user is on a MacBook trackpad and is ZOOMING with a two-finger pinch\n- when a user is on a touch device and is ZOOMING with a two-finger pinch\n- when a user is on a touch device and is PANNING with two fingers\n\nZooming is much more expensive than panning (because it causes shapes to render), \nso we want to be sure that we don't zoom while two-finger panning. \n\nIn order to do this, we keep track of a \"pinchState\", which is either:\n- \"zooming\"\n- \"panning\"\n- \"not sure\"\n\nIf a user is on a trackpad, the pinchState will be set to \"zooming\". \n\nIf the user is on a touch screen, then we start in the \"not sure\" state and switch back and forth\nbetween \"zooming\", \"panning\", and \"not sure\" based on what the user is doing with their fingers.\n\nIn the \"not sure\" state, we examine whether the user has moved the center of the gesture far enough\nto suggest that they're panning; or else that they've moved their fingers further apart or closer\ntogether enough to suggest that they're zooming. \n\nIn the \"panning\" state, we check whether the user's fingers have moved far enough apart to suggest\nthat they're zooming. If they have, we switch to the \"zooming\" state.\n\nIn the \"zooming\" state, we just stay zooming\u2014it's not YET possible to switch back to panning.\n\ntodo: compare velocities of change in order to determine whether the user has switched back to panning\n*/\n\ntype check<T extends AnyHandlerEventTypes, Key extends GestureKey> = undefined extends T[Key]\n\t? EventTypes[Key]\n\t: T[Key]\ntype PinchHandler = Handler<'pinch', check<EventTypes, 'pinch'>>\n\nconst useGesture = createUseGesture([wheelAction, pinchAction])\n\n/**\n * GOTCHA\n *\n * UseGesture fires a wheel event 140ms after the gesture actually ends, with a momentum-adjusted\n * delta. This creates a messed up interaction where after you stop scrolling suddenly the dang page\n * jumps a tick. why do they do this? you are asking the wrong person. it seems intentional though.\n * anyway we want to ignore that last event, but there's no way to directly detect it so we need to\n * keep track of timestamps. Yes this is awful, I am sorry.\n */\nlet lastWheelTime = undefined as undefined | number\n\nconst isWheelEndEvent = (time: number) => {\n\tif (lastWheelTime === undefined) {\n\t\tlastWheelTime = time\n\t\treturn false\n\t}\n\n\tif (time - lastWheelTime > 120 && time - lastWheelTime < 160) {\n\t\tlastWheelTime = time\n\t\treturn true\n\t}\n\n\tlastWheelTime = time\n\treturn false\n}\n\nexport function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {\n\tconst editor = useEditor()\n\n\tconst events = React.useMemo(() => {\n\t\tlet pinchState = 'not sure' as 'not sure' | 'zooming' | 'panning'\n\n\t\tconst onWheel: Handler<'wheel', WheelEvent> = ({ event }) => {\n\t\t\tif (!editor.getInstanceState().isFocused) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpinchState = 'not sure'\n\n\t\t\tif (isWheelEndEvent(Date.now())) {\n\t\t\t\t// ignore wheelEnd events\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Awful tht we need to put this logic here, but basically\n\t\t\t// we don't want to handle the the wheel event (or call prevent\n\t\t\t// default on the evnet) if the user is wheeling over an a shape\n\t\t\t// that is scrollable which they're currently editing.\n\n\t\t\tconst editingShapeId = editor.getEditingShapeId()\n\t\t\tif (editingShapeId) {\n\t\t\t\tconst shape = editor.getShape(editingShapeId)\n\t\t\t\tif (shape) {\n\t\t\t\t\tconst util = editor.getShapeUtil(shape)\n\t\t\t\t\tif (util.canScroll(shape)) {\n\t\t\t\t\t\tconst bounds = editor.getShapePageBounds(editingShapeId)\n\t\t\t\t\t\tif (bounds?.containsPoint(editor.inputs.currentPagePoint)) {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpreventDefault(event)\n\t\t\tstopEventPropagation(event)\n\t\t\tconst delta = normalizeWheel(event)\n\n\t\t\tif (delta.x === 0 && delta.y === 0) return\n\n\t\t\tconst info: TLWheelEventInfo = {\n\t\t\t\ttype: 'wheel',\n\t\t\t\tname: 'wheel',\n\t\t\t\tdelta,\n\t\t\t\tpoint: new Vec(event.clientX, event.clientY),\n\t\t\t\tshiftKey: event.shiftKey,\n\t\t\t\taltKey: event.altKey,\n\t\t\t\tctrlKey: event.metaKey || event.ctrlKey,\n\t\t\t\tmetaKey: event.metaKey,\n\t\t\t\taccelKey: isAccelKey(event),\n\t\t\t}\n\n\t\t\teditor.dispatch(info)\n\t\t}\n\n\t\tlet initDistanceBetweenFingers = 1 // the distance between the two fingers when the pinch starts\n\t\tlet initZoom = 1 // the browser's zoom level when the pinch starts\n\t\tlet currZoom = 1 // the current zoom level according to the pinch gesture recognizer\n\t\tlet currDistanceBetweenFingers = 0\n\t\tconst initPointBetweenFingers = new Vec()\n\t\tconst prevPointBetweenFingers = new Vec()\n\n\t\tconst onPinchStart: PinchHandler = (gesture) => {\n\t\t\tconst elm = ref.current\n\t\t\tpinchState = 'not sure'\n\n\t\t\tconst { event, origin, da } = gesture\n\n\t\t\tif (event instanceof WheelEvent) return\n\t\t\tif (!(event.target === elm || elm?.contains(event.target as Node))) return\n\n\t\t\tprevPointBetweenFingers.x = origin[0]\n\t\t\tprevPointBetweenFingers.y = origin[1]\n\t\t\tinitPointBetweenFingers.x = origin[0]\n\t\t\tinitPointBetweenFingers.y = origin[1]\n\t\t\tinitDistanceBetweenFingers = da[0]\n\t\t\tinitZoom = editor.getZoomLevel()\n\n\t\t\teditor.dispatch({\n\t\t\t\ttype: 'pinch',\n\t\t\t\tname: 'pinch_start',\n\t\t\t\tpoint: { x: origin[0], y: origin[1], z: editor.getZoomLevel() },\n\t\t\t\tdelta: { x: 0, y: 0 },\n\t\t\t\tshiftKey: event.shiftKey,\n\t\t\t\taltKey: event.altKey,\n\t\t\t\tctrlKey: event.metaKey || event.ctrlKey,\n\t\t\t\tmetaKey: event.metaKey,\n\t\t\t\taccelKey: isAccelKey(event),\n\t\t\t})\n\t\t}\n\n\t\t// let timeout: any\n\t\tconst updatePinchState = (isSafariTrackpadPinch: boolean) => {\n\t\t\tif (isSafariTrackpadPinch) {\n\t\t\t\tpinchState = 'zooming'\n\t\t\t}\n\n\t\t\tif (pinchState === 'zooming') {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Initial: [touch]-------origin-------[touch]\n\t\t\t// Current: [touch]-----------origin----------[touch]\n\t\t\t// |----| |------------|\n\t\t\t// originDistance ^ ^ touchDistance\n\n\t\t\t// How far have the two touch points moved towards or away from eachother?\n\t\t\tconst touchDistance = Math.abs(currDistanceBetweenFingers - initDistanceBetweenFingers)\n\t\t\t// How far has the point between the touches moved?\n\t\t\tconst originDistance = Vec.Dist(initPointBetweenFingers, prevPointBetweenFingers)\n\n\t\t\tswitch (pinchState) {\n\t\t\t\tcase 'not sure': {\n\t\t\t\t\tif (touchDistance > 24) {\n\t\t\t\t\t\tpinchState = 'zooming'\n\t\t\t\t\t} else if (originDistance > 16) {\n\t\t\t\t\t\tpinchState = 'panning'\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'panning': {\n\t\t\t\t\t// Slightly more touch distance needed to go from panning to zooming\n\t\t\t\t\tif (touchDistance > 64) {\n\t\t\t\t\t\tpinchState = 'zooming'\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst onPinch: PinchHandler = (gesture) => {\n\t\t\tconst elm = ref.current\n\t\t\tconst { event, origin, offset, da } = gesture\n\n\t\t\tif (event instanceof WheelEvent) return\n\t\t\tif (!(event.target === elm || elm?.contains(event.target as Node))) return\n\n\t\t\t// In (desktop) Safari, a two finger trackpad pinch will be a \"gesturechange\" event\n\t\t\t// and will have 0 touches; on iOS, a two-finger pinch will be a \"pointermove\" event\n\t\t\t// with two touches.\n\t\t\tconst isSafariTrackpadPinch =\n\t\t\t\tgesture.type === 'gesturechange' || gesture.type === 'gestureend'\n\n\t\t\t// The distance between the two touch points\n\t\t\tcurrDistanceBetweenFingers = da[0]\n\n\t\t\t// Only update the zoom if the pointers are far enough apart;\n\t\t\t// a very small touchDistance means that the user has probably\n\t\t\t// pinched out and their fingers are touching; this produces\n\t\t\t// very unstable zooming behavior.\n\n\t\t\tconst dx = origin[0] - prevPointBetweenFingers.x\n\t\t\tconst dy = origin[1] - prevPointBetweenFingers.y\n\n\t\t\tprevPointBetweenFingers.x = origin[0]\n\t\t\tprevPointBetweenFingers.y = origin[1]\n\n\t\t\tupdatePinchState(isSafariTrackpadPinch)\n\n\t\t\tswitch (pinchState) {\n\t\t\t\tcase 'zooming': {\n\t\t\t\t\tcurrZoom = offset[0]\n\n\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\ttype: 'pinch',\n\t\t\t\t\t\tname: 'pinch',\n\t\t\t\t\t\tpoint: { x: origin[0], y: origin[1], z: currZoom },\n\t\t\t\t\t\tdelta: { x: dx, y: dy },\n\t\t\t\t\t\tshiftKey: event.shiftKey,\n\t\t\t\t\t\taltKey: event.altKey,\n\t\t\t\t\t\tctrlKey: event.metaKey || event.ctrlKey,\n\t\t\t\t\t\tmetaKey: event.metaKey,\n\t\t\t\t\t\taccelKey: isAccelKey(event),\n\t\t\t\t\t})\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'panning': {\n\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\ttype: 'pinch',\n\t\t\t\t\t\tname: 'pinch',\n\t\t\t\t\t\tpoint: { x: origin[0], y: origin[1], z: initZoom },\n\t\t\t\t\t\tdelta: { x: dx, y: dy },\n\t\t\t\t\t\tshiftKey: event.shiftKey,\n\t\t\t\t\t\taltKey: event.altKey,\n\t\t\t\t\t\tctrlKey: event.metaKey || event.ctrlKey,\n\t\t\t\t\t\tmetaKey: event.metaKey,\n\t\t\t\t\t\taccelKey: isAccelKey(event),\n\t\t\t\t\t})\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst onPinchEnd: PinchHandler = (gesture) => {\n\t\t\tconst elm = ref.current\n\t\t\tconst { event, origin, offset } = gesture\n\n\t\t\tif (event instanceof WheelEvent) return\n\t\t\tif (!(event.target === elm || elm?.contains(event.target as Node))) return\n\n\t\t\tconst scale = offset[0]\n\n\t\t\tpinchState = 'not sure'\n\n\t\t\teditor.timers.requestAnimationFrame(() => {\n\t\t\t\teditor.dispatch({\n\t\t\t\t\ttype: 'pinch',\n\t\t\t\t\tname: 'pinch_end',\n\t\t\t\t\tpoint: { x: origin[0], y: origin[1], z: scale },\n\t\t\t\t\tdelta: { x: origin[0], y: origin[1] },\n\t\t\t\t\tshiftKey: event.shiftKey,\n\t\t\t\t\taltKey: event.altKey,\n\t\t\t\t\tctrlKey: event.metaKey || event.ctrlKey,\n\t\t\t\t\tmetaKey: event.metaKey,\n\t\t\t\t\taccelKey: isAccelKey(event),\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\n\t\treturn {\n\t\t\tonWheel,\n\t\t\tonPinchStart,\n\t\t\tonPinchEnd,\n\t\t\tonPinch,\n\t\t}\n\t}, [editor, ref])\n\n\tuseGesture(events, {\n\t\ttarget: ref,\n\t\teventOptions: { passive: false },\n\t\tpinch: {\n\t\t\tfrom: () => [editor.getZoomLevel(), 0], // Return the camera z to use when pinch starts\n\t\t\tscaleBounds: () => {\n\t\t\t\tconst baseZoom = editor.getBaseZoom()\n\t\t\t\tconst zoomSteps = editor.getCameraOptions().zoomSteps\n\t\t\t\tconst zoomMin = zoomSteps[0] * baseZoom\n\t\t\t\tconst zoomMax = zoomSteps[zoomSteps.length - 1] * baseZoom\n\n\t\t\t\treturn { from: editor.getZoomLevel(), max: zoomMax, min: zoomMin }\n\t\t\t},\n\t\t},\n\t})\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { AnyHandlerEventTypes, EventTypes, GestureKey, Handler } from '@use-gesture/core/types'\nimport { createUseGesture, pinchAction, wheelAction } from '@use-gesture/react'\nimport * as React from 'react'\nimport { TLWheelEventInfo } from '../editor/types/event-types'\nimport { Vec } from '../primitives/Vec'\nimport { preventDefault, stopEventPropagation } from '../utils/dom'\nimport { isAccelKey } from '../utils/keyboard'\nimport { normalizeWheel } from '../utils/normalizeWheel'\nimport { useEditor } from './useEditor'\n\n/*\n\n# How does pinching work?\n\nThe pinching handler is fired under two circumstances: \n- when a user is on a MacBook trackpad and is ZOOMING with a two-finger pinch\n- when a user is on a touch device and is ZOOMING with a two-finger pinch\n- when a user is on a touch device and is PANNING with two fingers\n\nZooming is much more expensive than panning (because it causes shapes to render), \nso we want to be sure that we don't zoom while two-finger panning. \n\nIn order to do this, we keep track of a \"pinchState\", which is either:\n- \"zooming\"\n- \"panning\"\n- \"not sure\"\n\nIf a user is on a trackpad, the pinchState will be set to \"zooming\". \n\nIf the user is on a touch screen, then we start in the \"not sure\" state and switch back and forth\nbetween \"zooming\", \"panning\", and \"not sure\" based on what the user is doing with their fingers.\n\nIn the \"not sure\" state, we examine whether the user has moved the center of the gesture far enough\nto suggest that they're panning; or else that they've moved their fingers further apart or closer\ntogether enough to suggest that they're zooming. \n\nIn the \"panning\" state, we check whether the user's fingers have moved far enough apart to suggest\nthat they're zooming. If they have, we switch to the \"zooming\" state.\n\nIn the \"zooming\" state, we just stay zooming\u2014it's not YET possible to switch back to panning.\n\ntodo: compare velocities of change in order to determine whether the user has switched back to panning\n*/\n\ntype check<T extends AnyHandlerEventTypes, Key extends GestureKey> = undefined extends T[Key]\n\t? EventTypes[Key]\n\t: T[Key]\ntype PinchHandler = Handler<'pinch', check<EventTypes, 'pinch'>>\n\nconst useGesture = createUseGesture([wheelAction, pinchAction])\n\n/**\n * GOTCHA\n *\n * UseGesture fires a wheel event 140ms after the gesture actually ends, with a momentum-adjusted\n * delta. This creates a messed up interaction where after you stop scrolling suddenly the dang page\n * jumps a tick. why do they do this? you are asking the wrong person. it seems intentional though.\n * anyway we want to ignore that last event, but there's no way to directly detect it so we need to\n * keep track of timestamps. Yes this is awful, I am sorry.\n */\nlet lastWheelTime = undefined as undefined | number\n\nconst isWheelEndEvent = (time: number) => {\n\tif (lastWheelTime === undefined) {\n\t\tlastWheelTime = time\n\t\treturn false\n\t}\n\n\tif (time - lastWheelTime > 120 && time - lastWheelTime < 160) {\n\t\tlastWheelTime = time\n\t\treturn true\n\t}\n\n\tlastWheelTime = time\n\treturn false\n}\n\nexport function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {\n\tconst editor = useEditor()\n\n\tconst events = React.useMemo(() => {\n\t\tlet pinchState = 'not sure' as 'not sure' | 'zooming' | 'panning'\n\n\t\tconst onWheel: Handler<'wheel', WheelEvent> = ({ event }) => {\n\t\t\tif (!editor.getInstanceState().isFocused) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpinchState = 'not sure'\n\n\t\t\tif (isWheelEndEvent(Date.now())) {\n\t\t\t\t// ignore wheelEnd events\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Awful tht we need to put this logic here, but basically\n\t\t\t// we don't want to handle the the wheel event (or call prevent\n\t\t\t// default on the evnet) if the user is wheeling over an a shape\n\t\t\t// that is scrollable which they're currently editing.\n\n\t\t\tconst editingShapeId = editor.getEditingShapeId()\n\t\t\tif (editingShapeId) {\n\t\t\t\tconst shape = editor.getShape(editingShapeId)\n\t\t\t\tif (shape) {\n\t\t\t\t\tconst util = editor.getShapeUtil(shape)\n\t\t\t\t\tif (util.canScroll(shape)) {\n\t\t\t\t\t\tconst bounds = editor.getShapePageBounds(editingShapeId)\n\t\t\t\t\t\tif (bounds?.containsPoint(editor.inputs.currentPagePoint)) {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpreventDefault(event)\n\t\t\tstopEventPropagation(event)\n\t\t\tconst delta = normalizeWheel(event)\n\n\t\t\tif (delta.x === 0 && delta.y === 0) return\n\n\t\t\tconst info: TLWheelEventInfo = {\n\t\t\t\ttype: 'wheel',\n\t\t\t\tname: 'wheel',\n\t\t\t\tdelta,\n\t\t\t\tpoint: new Vec(event.clientX, event.clientY),\n\t\t\t\tshiftKey: event.shiftKey,\n\t\t\t\taltKey: event.altKey,\n\t\t\t\tctrlKey: event.metaKey || event.ctrlKey,\n\t\t\t\tmetaKey: event.metaKey,\n\t\t\t\taccelKey: isAccelKey(event),\n\t\t\t}\n\n\t\t\teditor.dispatch(info)\n\t\t}\n\n\t\tlet initDistanceBetweenFingers = 1 // the distance between the two fingers when the pinch starts\n\t\tlet initZoom = 1 // the browser's zoom level when the pinch starts\n\t\tlet currDistanceBetweenFingers = 0\n\t\tconst initPointBetweenFingers = new Vec()\n\t\tconst prevPointBetweenFingers = new Vec()\n\n\t\tconst onPinchStart: PinchHandler = (gesture) => {\n\t\t\tconst elm = ref.current\n\t\t\tpinchState = 'not sure'\n\n\t\t\tconst { event, origin, da } = gesture\n\n\t\t\tif (event instanceof WheelEvent) return\n\t\t\tif (!(event.target === elm || elm?.contains(event.target as Node))) return\n\n\t\t\tprevPointBetweenFingers.x = origin[0]\n\t\t\tprevPointBetweenFingers.y = origin[1]\n\t\t\tinitPointBetweenFingers.x = origin[0]\n\t\t\tinitPointBetweenFingers.y = origin[1]\n\t\t\tinitDistanceBetweenFingers = da[0]\n\t\t\tinitZoom = editor.getZoomLevel()\n\n\t\t\teditor.dispatch({\n\t\t\t\ttype: 'pinch',\n\t\t\t\tname: 'pinch_start',\n\t\t\t\tpoint: { x: origin[0], y: origin[1], z: editor.getZoomLevel() },\n\t\t\t\tdelta: { x: 0, y: 0 },\n\t\t\t\tshiftKey: event.shiftKey,\n\t\t\t\taltKey: event.altKey,\n\t\t\t\tctrlKey: event.metaKey || event.ctrlKey,\n\t\t\t\tmetaKey: event.metaKey,\n\t\t\t\taccelKey: isAccelKey(event),\n\t\t\t})\n\t\t}\n\n\t\t// let timeout: any\n\t\tconst updatePinchState = (isSafariTrackpadPinch: boolean) => {\n\t\t\tif (isSafariTrackpadPinch) {\n\t\t\t\tpinchState = 'zooming'\n\t\t\t}\n\n\t\t\tif (pinchState === 'zooming') {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Initial: [touch]-------origin-------[touch]\n\t\t\t// Current: [touch]-----------origin----------[touch]\n\t\t\t// |----| |------------|\n\t\t\t// originDistance ^ ^ touchDistance\n\n\t\t\t// How far have the two touch points moved towards or away from eachother?\n\t\t\tconst touchDistance = Math.abs(currDistanceBetweenFingers - initDistanceBetweenFingers)\n\t\t\t// How far has the point between the touches moved?\n\t\t\tconst originDistance = Vec.Dist(initPointBetweenFingers, prevPointBetweenFingers)\n\n\t\t\tswitch (pinchState) {\n\t\t\t\tcase 'not sure': {\n\t\t\t\t\tif (touchDistance > 24) {\n\t\t\t\t\t\tpinchState = 'zooming'\n\t\t\t\t\t} else if (originDistance > 16) {\n\t\t\t\t\t\tpinchState = 'panning'\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'panning': {\n\t\t\t\t\t// Slightly more touch distance needed to go from panning to zooming\n\t\t\t\t\tif (touchDistance > 64) {\n\t\t\t\t\t\tpinchState = 'zooming'\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst onPinch: PinchHandler = (gesture) => {\n\t\t\tconst elm = ref.current\n\t\t\tconst { event, origin, offset, da } = gesture\n\n\t\t\tif (event instanceof WheelEvent) return\n\t\t\tif (!(event.target === elm || elm?.contains(event.target as Node))) return\n\n\t\t\t// In (desktop) Safari, a two finger trackpad pinch will be a \"gesturechange\" event\n\t\t\t// and will have 0 touches; on iOS, a two-finger pinch will be a \"pointermove\" event\n\t\t\t// with two touches.\n\t\t\tconst isSafariTrackpadPinch =\n\t\t\t\tgesture.type === 'gesturechange' || gesture.type === 'gestureend'\n\n\t\t\t// The distance between the two touch points\n\t\t\tcurrDistanceBetweenFingers = da[0]\n\n\t\t\t// Only update the zoom if the pointers are far enough apart;\n\t\t\t// a very small touchDistance means that the user has probably\n\t\t\t// pinched out and their fingers are touching; this produces\n\t\t\t// very unstable zooming behavior.\n\n\t\t\tconst dx = origin[0] - prevPointBetweenFingers.x\n\t\t\tconst dy = origin[1] - prevPointBetweenFingers.y\n\n\t\t\tprevPointBetweenFingers.x = origin[0]\n\t\t\tprevPointBetweenFingers.y = origin[1]\n\n\t\t\tupdatePinchState(isSafariTrackpadPinch)\n\n\t\t\tswitch (pinchState) {\n\t\t\t\tcase 'zooming': {\n\t\t\t\t\tconst currZoom = offset[0] ** editor.getCameraOptions().zoomSpeed\n\n\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\ttype: 'pinch',\n\t\t\t\t\t\tname: 'pinch',\n\t\t\t\t\t\tpoint: { x: origin[0], y: origin[1], z: currZoom },\n\t\t\t\t\t\tdelta: { x: dx, y: dy },\n\t\t\t\t\t\tshiftKey: event.shiftKey,\n\t\t\t\t\t\taltKey: event.altKey,\n\t\t\t\t\t\tctrlKey: event.metaKey || event.ctrlKey,\n\t\t\t\t\t\tmetaKey: event.metaKey,\n\t\t\t\t\t\taccelKey: isAccelKey(event),\n\t\t\t\t\t})\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'panning': {\n\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\ttype: 'pinch',\n\t\t\t\t\t\tname: 'pinch',\n\t\t\t\t\t\tpoint: { x: origin[0], y: origin[1], z: initZoom },\n\t\t\t\t\t\tdelta: { x: dx, y: dy },\n\t\t\t\t\t\tshiftKey: event.shiftKey,\n\t\t\t\t\t\taltKey: event.altKey,\n\t\t\t\t\t\tctrlKey: event.metaKey || event.ctrlKey,\n\t\t\t\t\t\tmetaKey: event.metaKey,\n\t\t\t\t\t\taccelKey: isAccelKey(event),\n\t\t\t\t\t})\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst onPinchEnd: PinchHandler = (gesture) => {\n\t\t\tconst elm = ref.current\n\t\t\tconst { event, origin, offset } = gesture\n\n\t\t\tif (event instanceof WheelEvent) return\n\t\t\tif (!(event.target === elm || elm?.contains(event.target as Node))) return\n\n\t\t\tconst scale = offset[0] ** editor.getCameraOptions().zoomSpeed\n\n\t\t\tpinchState = 'not sure'\n\n\t\t\teditor.timers.requestAnimationFrame(() => {\n\t\t\t\teditor.dispatch({\n\t\t\t\t\ttype: 'pinch',\n\t\t\t\t\tname: 'pinch_end',\n\t\t\t\t\tpoint: { x: origin[0], y: origin[1], z: scale },\n\t\t\t\t\tdelta: { x: origin[0], y: origin[1] },\n\t\t\t\t\tshiftKey: event.shiftKey,\n\t\t\t\t\taltKey: event.altKey,\n\t\t\t\t\tctrlKey: event.metaKey || event.ctrlKey,\n\t\t\t\t\tmetaKey: event.metaKey,\n\t\t\t\t\taccelKey: isAccelKey(event),\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\n\t\treturn {\n\t\t\tonWheel,\n\t\t\tonPinchStart,\n\t\t\tonPinchEnd,\n\t\t\tonPinch,\n\t\t}\n\t}, [editor, ref])\n\n\tuseGesture(events, {\n\t\ttarget: ref,\n\t\teventOptions: { passive: false },\n\t\tpinch: {\n\t\t\tfrom: () => {\n\t\t\t\tconst { zoomSpeed } = editor.getCameraOptions()\n\t\t\t\tconst level = editor.getZoomLevel() ** (1 / zoomSpeed)\n\t\t\t\treturn [level, 0]\n\t\t\t}, // Return the camera z to use when pinch starts\n\t\t\tscaleBounds: () => {\n\t\t\t\tconst baseZoom = editor.getBaseZoom()\n\t\t\t\tconst { zoomSteps, zoomSpeed } = editor.getCameraOptions()\n\t\t\t\tconst zoomMin = zoomSteps[0] * baseZoom\n\t\t\t\tconst zoomMax = zoomSteps[zoomSteps.length - 1] * baseZoom\n\n\t\t\t\treturn {\n\t\t\t\t\tmax: zoomMax ** (1 / zoomSpeed),\n\t\t\t\t\tmin: zoomMin ** (1 / zoomSpeed),\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t})\n}\n"],
5
+ "mappings": "AACA,SAAS,kBAAkB,aAAa,mBAAmB;AAC3D,YAAY,WAAW;AAEvB,SAAS,WAAW;AACpB,SAAS,gBAAgB,4BAA4B;AACrD,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB;AAC/B,SAAS,iBAAiB;AAyC1B,MAAM,aAAa,iBAAiB,CAAC,aAAa,WAAW,CAAC;AAW9D,IAAI,gBAAgB;AAEpB,MAAM,kBAAkB,CAAC,SAAiB;AACzC,MAAI,kBAAkB,QAAW;AAChC,oBAAgB;AAChB,WAAO;AAAA,EACR;AAEA,MAAI,OAAO,gBAAgB,OAAO,OAAO,gBAAgB,KAAK;AAC7D,oBAAgB;AAChB,WAAO;AAAA,EACR;AAEA,kBAAgB;AAChB,SAAO;AACR;AAEO,SAAS,iBAAiB,KAAsC;AACtE,QAAM,SAAS,UAAU;AAEzB,QAAM,SAAS,MAAM,QAAQ,MAAM;AAClC,QAAI,aAAa;AAEjB,UAAM,UAAwC,CAAC,EAAE,MAAM,MAAM;AAC5D,UAAI,CAAC,OAAO,iBAAiB,EAAE,WAAW;AACzC;AAAA,MACD;AAEA,mBAAa;AAEb,UAAI,gBAAgB,KAAK,IAAI,CAAC,GAAG;AAEhC;AAAA,MACD;AAOA,YAAM,iBAAiB,OAAO,kBAAkB;AAChD,UAAI,gBAAgB;AACnB,cAAM,QAAQ,OAAO,SAAS,cAAc;AAC5C,YAAI,OAAO;AACV,gBAAM,OAAO,OAAO,aAAa,KAAK;AACtC,cAAI,KAAK,UAAU,KAAK,GAAG;AAC1B,kBAAM,SAAS,OAAO,mBAAmB,cAAc;AACvD,gBAAI,QAAQ,cAAc,OAAO,OAAO,gBAAgB,GAAG;AAC1D;AAAA,YACD;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAEA,qBAAe,KAAK;AACpB,2BAAqB,KAAK;AAC1B,YAAM,QAAQ,eAAe,KAAK;AAElC,UAAI,MAAM,MAAM,KAAK,MAAM,MAAM,EAAG;AAEpC,YAAM,OAAyB;AAAA,QAC9B,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,OAAO,IAAI,IAAI,MAAM,SAAS,MAAM,OAAO;AAAA,QAC3C,UAAU,MAAM;AAAA,QAChB,QAAQ,MAAM;AAAA,QACd,SAAS,MAAM,WAAW,MAAM;AAAA,QAChC,SAAS,MAAM;AAAA,QACf,UAAU,WAAW,KAAK;AAAA,MAC3B;AAEA,aAAO,SAAS,IAAI;AAAA,IACrB;AAEA,QAAI,6BAA6B;AACjC,QAAI,WAAW;AACf,QAAI,6BAA6B;AACjC,UAAM,0BAA0B,IAAI,IAAI;AACxC,UAAM,0BAA0B,IAAI,IAAI;AAExC,UAAM,eAA6B,CAAC,YAAY;AAC/C,YAAM,MAAM,IAAI;AAChB,mBAAa;AAEb,YAAM,EAAE,OAAO,QAAQ,GAAG,IAAI;AAE9B,UAAI,iBAAiB,WAAY;AACjC,UAAI,EAAE,MAAM,WAAW,OAAO,KAAK,SAAS,MAAM,MAAc,GAAI;AAEpE,8BAAwB,IAAI,OAAO,CAAC;AACpC,8BAAwB,IAAI,OAAO,CAAC;AACpC,8BAAwB,IAAI,OAAO,CAAC;AACpC,8BAAwB,IAAI,OAAO,CAAC;AACpC,mCAA6B,GAAG,CAAC;AACjC,iBAAW,OAAO,aAAa;AAE/B,aAAO,SAAS;AAAA,QACf,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,GAAG,OAAO,aAAa,EAAE;AAAA,QAC9D,OAAO,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,QACpB,UAAU,MAAM;AAAA,QAChB,QAAQ,MAAM;AAAA,QACd,SAAS,MAAM,WAAW,MAAM;AAAA,QAChC,SAAS,MAAM;AAAA,QACf,UAAU,WAAW,KAAK;AAAA,MAC3B,CAAC;AAAA,IACF;AAGA,UAAM,mBAAmB,CAAC,0BAAmC;AAC5D,UAAI,uBAAuB;AAC1B,qBAAa;AAAA,MACd;AAEA,UAAI,eAAe,WAAW;AAC7B;AAAA,MACD;AAQA,YAAM,gBAAgB,KAAK,IAAI,6BAA6B,0BAA0B;AAEtF,YAAM,iBAAiB,IAAI,KAAK,yBAAyB,uBAAuB;AAEhF,cAAQ,YAAY;AAAA,QACnB,KAAK,YAAY;AAChB,cAAI,gBAAgB,IAAI;AACvB,yBAAa;AAAA,UACd,WAAW,iBAAiB,IAAI;AAC/B,yBAAa;AAAA,UACd;AACA;AAAA,QACD;AAAA,QACA,KAAK,WAAW;AAEf,cAAI,gBAAgB,IAAI;AACvB,yBAAa;AAAA,UACd;AACA;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAEA,UAAM,UAAwB,CAAC,YAAY;AAC1C,YAAM,MAAM,IAAI;AAChB,YAAM,EAAE,OAAO,QAAQ,QAAQ,GAAG,IAAI;AAEtC,UAAI,iBAAiB,WAAY;AACjC,UAAI,EAAE,MAAM,WAAW,OAAO,KAAK,SAAS,MAAM,MAAc,GAAI;AAKpE,YAAM,wBACL,QAAQ,SAAS,mBAAmB,QAAQ,SAAS;AAGtD,mCAA6B,GAAG,CAAC;AAOjC,YAAM,KAAK,OAAO,CAAC,IAAI,wBAAwB;AAC/C,YAAM,KAAK,OAAO,CAAC,IAAI,wBAAwB;AAE/C,8BAAwB,IAAI,OAAO,CAAC;AACpC,8BAAwB,IAAI,OAAO,CAAC;AAEpC,uBAAiB,qBAAqB;AAEtC,cAAQ,YAAY;AAAA,QACnB,KAAK,WAAW;AACf,gBAAM,WAAW,OAAO,CAAC,KAAK,OAAO,iBAAiB,EAAE;AAExD,iBAAO,SAAS;AAAA,YACf,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,GAAG,SAAS;AAAA,YACjD,OAAO,EAAE,GAAG,IAAI,GAAG,GAAG;AAAA,YACtB,UAAU,MAAM;AAAA,YAChB,QAAQ,MAAM;AAAA,YACd,SAAS,MAAM,WAAW,MAAM;AAAA,YAChC,SAAS,MAAM;AAAA,YACf,UAAU,WAAW,KAAK;AAAA,UAC3B,CAAC;AACD;AAAA,QACD;AAAA,QACA,KAAK,WAAW;AACf,iBAAO,SAAS;AAAA,YACf,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,GAAG,SAAS;AAAA,YACjD,OAAO,EAAE,GAAG,IAAI,GAAG,GAAG;AAAA,YACtB,UAAU,MAAM;AAAA,YAChB,QAAQ,MAAM;AAAA,YACd,SAAS,MAAM,WAAW,MAAM;AAAA,YAChC,SAAS,MAAM;AAAA,YACf,UAAU,WAAW,KAAK;AAAA,UAC3B,CAAC;AACD;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAEA,UAAM,aAA2B,CAAC,YAAY;AAC7C,YAAM,MAAM,IAAI;AAChB,YAAM,EAAE,OAAO,QAAQ,OAAO,IAAI;AAElC,UAAI,iBAAiB,WAAY;AACjC,UAAI,EAAE,MAAM,WAAW,OAAO,KAAK,SAAS,MAAM,MAAc,GAAI;AAEpE,YAAM,QAAQ,OAAO,CAAC,KAAK,OAAO,iBAAiB,EAAE;AAErD,mBAAa;AAEb,aAAO,OAAO,sBAAsB,MAAM;AACzC,eAAO,SAAS;AAAA,UACf,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,GAAG,MAAM;AAAA,UAC9C,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,EAAE;AAAA,UACpC,UAAU,MAAM;AAAA,UAChB,QAAQ,MAAM;AAAA,UACd,SAAS,MAAM,WAAW,MAAM;AAAA,UAChC,SAAS,MAAM;AAAA,UACf,UAAU,WAAW,KAAK;AAAA,QAC3B,CAAC;AAAA,MACF,CAAC;AAAA,IACF;AAEA,WAAO;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACD,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,aAAW,QAAQ;AAAA,IAClB,QAAQ;AAAA,IACR,cAAc,EAAE,SAAS,MAAM;AAAA,IAC/B,OAAO;AAAA,MACN,MAAM,MAAM;AACX,cAAM,EAAE,UAAU,IAAI,OAAO,iBAAiB;AAC9C,cAAM,QAAQ,OAAO,aAAa,MAAM,IAAI;AAC5C,eAAO,CAAC,OAAO,CAAC;AAAA,MACjB;AAAA;AAAA,MACA,aAAa,MAAM;AAClB,cAAM,WAAW,OAAO,YAAY;AACpC,cAAM,EAAE,WAAW,UAAU,IAAI,OAAO,iBAAiB;AACzD,cAAM,UAAU,UAAU,CAAC,IAAI;AAC/B,cAAM,UAAU,UAAU,UAAU,SAAS,CAAC,IAAI;AAElD,eAAO;AAAA,UACN,KAAK,YAAY,IAAI;AAAA,UACrB,KAAK,YAAY,IAAI;AAAA,QACtB;AAAA,MACD;AAAA,IACD;AAAA,EACD,CAAC;AACF;",
6
6
  "names": []
7
7
  }
@@ -55,6 +55,7 @@ const WatermarkInner = memo(function WatermarkInner2({ src }) {
55
55
  stopEventPropagation(e);
56
56
  preventDefault(e);
57
57
  },
58
+ title: "made with tldraw",
58
59
  onClick: () => runtime.openWindow(url, "_blank"),
59
60
  style: { mask: maskCss, WebkitMask: maskCss }
60
61
  }
@@ -68,6 +69,7 @@ const WatermarkInner = memo(function WatermarkInner2({ src }) {
68
69
  onPointerDown: (e) => {
69
70
  stopEventPropagation(e);
70
71
  },
72
+ title: "made with tldraw",
71
73
  style: { mask: maskCss, WebkitMask: maskCss }
72
74
  }
73
75
  )
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/license/Watermark.tsx"],
4
- "sourcesContent": ["import { useValue } from '@tldraw/state-react'\nimport { memo, useRef } from 'react'\nimport { tlenv } from '../globals/environment'\nimport { useCanvasEvents } from '../hooks/useCanvasEvents'\nimport { useEditor } from '../hooks/useEditor'\nimport { usePassThroughWheelEvents } from '../hooks/usePassThroughWheelEvents'\nimport { preventDefault, stopEventPropagation } from '../utils/dom'\nimport { runtime } from '../utils/runtime'\nimport { watermarkDesktopSvg, watermarkMobileSvg } from '../watermarks'\nimport { LicenseManager } from './LicenseManager'\nimport { useLicenseContext } from './LicenseProvider'\nimport { useLicenseManagerState } from './useLicenseManagerState'\n\nconst WATERMARK_DESKTOP_LOCAL_SRC = `data:image/svg+xml;utf8,${encodeURIComponent(watermarkDesktopSvg)}`\nconst WATERMARK_MOBILE_LOCAL_SRC = `data:image/svg+xml;utf8,${encodeURIComponent(watermarkMobileSvg)}`\n\n/** @internal */\nexport const Watermark = memo(function Watermark() {\n\tconst licenseManager = useLicenseContext()\n\tconst editor = useEditor()\n\tconst isMobile = useValue('is mobile', () => editor.getViewportScreenBounds().width < 700, [\n\t\teditor,\n\t])\n\n\tconst licenseManagerState = useLicenseManagerState(licenseManager)\n\n\tif (!['licensed-with-watermark', 'unlicensed'].includes(licenseManagerState)) return null\n\n\treturn (\n\t\t<>\n\t\t\t<LicenseStyles />\n\t\t\t<WatermarkInner src={isMobile ? WATERMARK_MOBILE_LOCAL_SRC : WATERMARK_DESKTOP_LOCAL_SRC} />\n\t\t</>\n\t)\n})\n\nconst WatermarkInner = memo(function WatermarkInner({ src }: { src: string }) {\n\tconst editor = useEditor()\n\tconst isDebugMode = useValue('debug mode', () => editor.getInstanceState().isDebugMode, [editor])\n\tconst isMobile = useValue('is mobile', () => editor.getViewportScreenBounds().width < 700, [\n\t\teditor,\n\t])\n\tconst events = useCanvasEvents()\n\n\tconst ref = useRef<HTMLDivElement>(null)\n\tusePassThroughWheelEvents(ref)\n\n\tconst maskCss = `url('${src}') center 100% / 100% no-repeat`\n\tconst url = 'https://tldraw.dev/?utm_source=dotcom&utm_medium=organic&utm_campaign=watermark'\n\n\treturn (\n\t\t<div\n\t\t\tref={ref}\n\t\t\tclassName={LicenseManager.className}\n\t\t\tdata-debug={isDebugMode}\n\t\t\tdata-mobile={isMobile}\n\t\t\tdraggable={false}\n\t\t\t{...events}\n\t\t>\n\t\t\t{tlenv.isWebview ? (\n\t\t\t\t<a\n\t\t\t\t\tdraggable={false}\n\t\t\t\t\trole=\"button\"\n\t\t\t\t\tonPointerDown={(e) => {\n\t\t\t\t\t\tstopEventPropagation(e)\n\t\t\t\t\t\tpreventDefault(e)\n\t\t\t\t\t}}\n\t\t\t\t\tonClick={() => runtime.openWindow(url, '_blank')}\n\t\t\t\t\tstyle={{ mask: maskCss, WebkitMask: maskCss }}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<a\n\t\t\t\t\thref={url}\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\tdraggable={false}\n\t\t\t\t\tonPointerDown={(e) => {\n\t\t\t\t\t\tstopEventPropagation(e)\n\t\t\t\t\t}}\n\t\t\t\t\tstyle={{ mask: maskCss, WebkitMask: maskCss }}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t)\n})\n\nconst LicenseStyles = memo(function LicenseStyles() {\n\tconst editor = useEditor()\n\tconst className = LicenseManager.className\n\n\tconst CSS = `/* ------------------- SEE LICENSE -------------------\nThe tldraw watermark is part of tldraw's license. It is shown for unlicensed\nor \"licensed-with-watermark\" users. By using this library, you agree to\npreserve the watermark's behavior, keeping it visible, unobscured, and\navailable to user-interaction.\n\nTo remove the watermark, please purchase a license at tldraw.dev.\n*/\n\n\t.${className} {\n\t\tposition: absolute;\n\t\tbottom: var(--space-2);\n\t\tright: var(--space-2);\n\t\twidth: 96px;\n\t\theight: 32px;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tz-index: var(--layer-watermark) !important;\n\t\tbackground-color: color-mix(in srgb, var(--color-background) 62%, transparent);\n\t\topacity: 1;\n\t\tborder-radius: 5px;\n\t\tpointer-events: all;\n\t\tpadding: 2px;\n\t\tbox-sizing: content-box;\n\t}\n\n\t.${className} > a {\n\t\tposition: absolute;\n\t\twidth: 96px;\n\t\theight: 32px;\n\t\tpointer-events: all;\n\t\tcursor: inherit;\n\t\tcolor: var(--color-text);\n\t\topacity: .38;\n\t\tbackground-color: currentColor;\n\t}\n\n\t.${className}[data-debug='true'] {\n\t\tbottom: 46px;\n\t}\n\n\t.${className}[data-mobile='true'] {\n\t\tborder-radius: 4px 0px 0px 4px;\n\t\tright: -2px;\n\t\twidth: 8px;\n\t\theight: 48px;\n\t}\n\n\t.${className}[data-mobile='true'] > a {\n\t\twidth: 8px;\n\t\theight: 32px;\n\t}\n\n\t@media (hover: hover) {\n\t\t.${className} > a {\n\t\t\tpointer-events: none;\n\t\t}\n\n\t\t.${className}:hover {\n\t\t\tbackground-color: var(--color-background);\n\t\t\ttransition: background-color 0.2s ease-in-out;\n\t\t\ttransition-delay: 0.32s;\n\t\t}\n\n\t\t.${className}:hover > a {\n\t\t\tanimation: delayed_link 0.2s forwards ease-in-out;\n\t\t\tanimation-delay: 0.32s;\n\t\t}\n\n\t\t.${className} > a:focus-visible {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\n\t@keyframes delayed_link {\n\t\t0% {\n\t\t\tcursor: inherit;\n\t\t\topacity: .38;\n\t\t\tpointer-events: none;\n\t\t}\n\t\t100% {\n\t\t\tcursor: pointer;\n\t\t\topacity: 1;\n\t\t\tpointer-events: all;\n\t\t}\n\t}`\n\n\treturn <style nonce={editor.options.nonce}>{CSS}</style>\n})\n"],
5
- "mappings": "AA6BE,mBACC,KADD;AA7BF,SAAS,gBAAgB;AACzB,SAAS,MAAM,cAAc;AAC7B,SAAS,aAAa;AACtB,SAAS,uBAAuB;AAChC,SAAS,iBAAiB;AAC1B,SAAS,iCAAiC;AAC1C,SAAS,gBAAgB,4BAA4B;AACrD,SAAS,eAAe;AACxB,SAAS,qBAAqB,0BAA0B;AACxD,SAAS,sBAAsB;AAC/B,SAAS,yBAAyB;AAClC,SAAS,8BAA8B;AAEvC,MAAM,8BAA8B,2BAA2B,mBAAmB,mBAAmB,CAAC;AACtG,MAAM,6BAA6B,2BAA2B,mBAAmB,kBAAkB,CAAC;AAG7F,MAAM,YAAY,KAAK,SAASA,aAAY;AAClD,QAAM,iBAAiB,kBAAkB;AACzC,QAAM,SAAS,UAAU;AACzB,QAAM,WAAW,SAAS,aAAa,MAAM,OAAO,wBAAwB,EAAE,QAAQ,KAAK;AAAA,IAC1F;AAAA,EACD,CAAC;AAED,QAAM,sBAAsB,uBAAuB,cAAc;AAEjE,MAAI,CAAC,CAAC,2BAA2B,YAAY,EAAE,SAAS,mBAAmB,EAAG,QAAO;AAErF,SACC,iCACC;AAAA,wBAAC,iBAAc;AAAA,IACf,oBAAC,kBAAe,KAAK,WAAW,6BAA6B,6BAA6B;AAAA,KAC3F;AAEF,CAAC;AAED,MAAM,iBAAiB,KAAK,SAASC,gBAAe,EAAE,IAAI,GAAoB;AAC7E,QAAM,SAAS,UAAU;AACzB,QAAM,cAAc,SAAS,cAAc,MAAM,OAAO,iBAAiB,EAAE,aAAa,CAAC,MAAM,CAAC;AAChG,QAAM,WAAW,SAAS,aAAa,MAAM,OAAO,wBAAwB,EAAE,QAAQ,KAAK;AAAA,IAC1F;AAAA,EACD,CAAC;AACD,QAAM,SAAS,gBAAgB;AAE/B,QAAM,MAAM,OAAuB,IAAI;AACvC,4BAA0B,GAAG;AAE7B,QAAM,UAAU,QAAQ,GAAG;AAC3B,QAAM,MAAM;AAEZ,SACC;AAAA,IAAC;AAAA;AAAA,MACA;AAAA,MACA,WAAW,eAAe;AAAA,MAC1B,cAAY;AAAA,MACZ,eAAa;AAAA,MACb,WAAW;AAAA,MACV,GAAG;AAAA,MAEH,gBAAM,YACN;AAAA,QAAC;AAAA;AAAA,UACA,WAAW;AAAA,UACX,MAAK;AAAA,UACL,eAAe,CAAC,MAAM;AACrB,iCAAqB,CAAC;AACtB,2BAAe,CAAC;AAAA,UACjB;AAAA,UACA,SAAS,MAAM,QAAQ,WAAW,KAAK,QAAQ;AAAA,UAC/C,OAAO,EAAE,MAAM,SAAS,YAAY,QAAQ;AAAA;AAAA,MAC7C,IAEA;AAAA,QAAC;AAAA;AAAA,UACA,MAAM;AAAA,UACN,QAAO;AAAA,UACP,KAAI;AAAA,UACJ,WAAW;AAAA,UACX,eAAe,CAAC,MAAM;AACrB,iCAAqB,CAAC;AAAA,UACvB;AAAA,UACA,OAAO,EAAE,MAAM,SAAS,YAAY,QAAQ;AAAA;AAAA,MAC7C;AAAA;AAAA,EAEF;AAEF,CAAC;AAED,MAAM,gBAAgB,KAAK,SAASC,iBAAgB;AACnD,QAAM,SAAS,UAAU;AACzB,QAAM,YAAY,eAAe;AAEjC,QAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAST,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWT,SAAS;AAAA;AAAA;AAAA;AAAA,IAIT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMR,SAAS;AAAA;AAAA;AAAA;AAAA,KAIT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,KAKT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBb,SAAO,oBAAC,WAAM,OAAO,OAAO,QAAQ,OAAQ,eAAI;AACjD,CAAC;",
4
+ "sourcesContent": ["import { useValue } from '@tldraw/state-react'\nimport { memo, useRef } from 'react'\nimport { tlenv } from '../globals/environment'\nimport { useCanvasEvents } from '../hooks/useCanvasEvents'\nimport { useEditor } from '../hooks/useEditor'\nimport { usePassThroughWheelEvents } from '../hooks/usePassThroughWheelEvents'\nimport { preventDefault, stopEventPropagation } from '../utils/dom'\nimport { runtime } from '../utils/runtime'\nimport { watermarkDesktopSvg, watermarkMobileSvg } from '../watermarks'\nimport { LicenseManager } from './LicenseManager'\nimport { useLicenseContext } from './LicenseProvider'\nimport { useLicenseManagerState } from './useLicenseManagerState'\n\nconst WATERMARK_DESKTOP_LOCAL_SRC = `data:image/svg+xml;utf8,${encodeURIComponent(watermarkDesktopSvg)}`\nconst WATERMARK_MOBILE_LOCAL_SRC = `data:image/svg+xml;utf8,${encodeURIComponent(watermarkMobileSvg)}`\n\n/** @internal */\nexport const Watermark = memo(function Watermark() {\n\tconst licenseManager = useLicenseContext()\n\tconst editor = useEditor()\n\tconst isMobile = useValue('is mobile', () => editor.getViewportScreenBounds().width < 700, [\n\t\teditor,\n\t])\n\n\tconst licenseManagerState = useLicenseManagerState(licenseManager)\n\n\tif (!['licensed-with-watermark', 'unlicensed'].includes(licenseManagerState)) return null\n\n\treturn (\n\t\t<>\n\t\t\t<LicenseStyles />\n\t\t\t<WatermarkInner src={isMobile ? WATERMARK_MOBILE_LOCAL_SRC : WATERMARK_DESKTOP_LOCAL_SRC} />\n\t\t</>\n\t)\n})\n\nconst WatermarkInner = memo(function WatermarkInner({ src }: { src: string }) {\n\tconst editor = useEditor()\n\tconst isDebugMode = useValue('debug mode', () => editor.getInstanceState().isDebugMode, [editor])\n\tconst isMobile = useValue('is mobile', () => editor.getViewportScreenBounds().width < 700, [\n\t\teditor,\n\t])\n\tconst events = useCanvasEvents()\n\n\tconst ref = useRef<HTMLDivElement>(null)\n\tusePassThroughWheelEvents(ref)\n\n\tconst maskCss = `url('${src}') center 100% / 100% no-repeat`\n\tconst url = 'https://tldraw.dev/?utm_source=dotcom&utm_medium=organic&utm_campaign=watermark'\n\n\treturn (\n\t\t<div\n\t\t\tref={ref}\n\t\t\tclassName={LicenseManager.className}\n\t\t\tdata-debug={isDebugMode}\n\t\t\tdata-mobile={isMobile}\n\t\t\tdraggable={false}\n\t\t\t{...events}\n\t\t>\n\t\t\t{tlenv.isWebview ? (\n\t\t\t\t<a\n\t\t\t\t\tdraggable={false}\n\t\t\t\t\trole=\"button\"\n\t\t\t\t\tonPointerDown={(e) => {\n\t\t\t\t\t\tstopEventPropagation(e)\n\t\t\t\t\t\tpreventDefault(e)\n\t\t\t\t\t}}\n\t\t\t\t\ttitle=\"made with tldraw\"\n\t\t\t\t\tonClick={() => runtime.openWindow(url, '_blank')}\n\t\t\t\t\tstyle={{ mask: maskCss, WebkitMask: maskCss }}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<a\n\t\t\t\t\thref={url}\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\tdraggable={false}\n\t\t\t\t\tonPointerDown={(e) => {\n\t\t\t\t\t\tstopEventPropagation(e)\n\t\t\t\t\t}}\n\t\t\t\t\ttitle=\"made with tldraw\"\n\t\t\t\t\tstyle={{ mask: maskCss, WebkitMask: maskCss }}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t)\n})\n\nconst LicenseStyles = memo(function LicenseStyles() {\n\tconst editor = useEditor()\n\tconst className = LicenseManager.className\n\n\tconst CSS = `/* ------------------- SEE LICENSE -------------------\nThe tldraw watermark is part of tldraw's license. It is shown for unlicensed\nor \"licensed-with-watermark\" users. By using this library, you agree to\npreserve the watermark's behavior, keeping it visible, unobscured, and\navailable to user-interaction.\n\nTo remove the watermark, please purchase a license at tldraw.dev.\n*/\n\n\t.${className} {\n\t\tposition: absolute;\n\t\tbottom: var(--space-2);\n\t\tright: var(--space-2);\n\t\twidth: 96px;\n\t\theight: 32px;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tz-index: var(--layer-watermark) !important;\n\t\tbackground-color: color-mix(in srgb, var(--color-background) 62%, transparent);\n\t\topacity: 1;\n\t\tborder-radius: 5px;\n\t\tpointer-events: all;\n\t\tpadding: 2px;\n\t\tbox-sizing: content-box;\n\t}\n\n\t.${className} > a {\n\t\tposition: absolute;\n\t\twidth: 96px;\n\t\theight: 32px;\n\t\tpointer-events: all;\n\t\tcursor: inherit;\n\t\tcolor: var(--color-text);\n\t\topacity: .38;\n\t\tbackground-color: currentColor;\n\t}\n\n\t.${className}[data-debug='true'] {\n\t\tbottom: 46px;\n\t}\n\n\t.${className}[data-mobile='true'] {\n\t\tborder-radius: 4px 0px 0px 4px;\n\t\tright: -2px;\n\t\twidth: 8px;\n\t\theight: 48px;\n\t}\n\n\t.${className}[data-mobile='true'] > a {\n\t\twidth: 8px;\n\t\theight: 32px;\n\t}\n\n\t@media (hover: hover) {\n\t\t.${className} > a {\n\t\t\tpointer-events: none;\n\t\t}\n\n\t\t.${className}:hover {\n\t\t\tbackground-color: var(--color-background);\n\t\t\ttransition: background-color 0.2s ease-in-out;\n\t\t\ttransition-delay: 0.32s;\n\t\t}\n\n\t\t.${className}:hover > a {\n\t\t\tanimation: delayed_link 0.2s forwards ease-in-out;\n\t\t\tanimation-delay: 0.32s;\n\t\t}\n\n\t\t.${className} > a:focus-visible {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\n\t@keyframes delayed_link {\n\t\t0% {\n\t\t\tcursor: inherit;\n\t\t\topacity: .38;\n\t\t\tpointer-events: none;\n\t\t}\n\t\t100% {\n\t\t\tcursor: pointer;\n\t\t\topacity: 1;\n\t\t\tpointer-events: all;\n\t\t}\n\t}`\n\n\treturn <style nonce={editor.options.nonce}>{CSS}</style>\n})\n"],
5
+ "mappings": "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",
6
6
  "names": ["Watermark", "WatermarkInner", "LicenseStyles"]
7
7
  }
@@ -1,34 +1,57 @@
1
+ import { assert } from "@tldraw/utils";
1
2
  import { Box } from "../Box.mjs";
3
+ import { Mat } from "../Mat.mjs";
2
4
  import { Vec } from "../Vec.mjs";
3
- import { pointInPolygon } from "../utils.mjs";
5
+ import {
6
+ intersectCirclePolygon,
7
+ intersectCirclePolyline,
8
+ intersectLineSegmentPolygon,
9
+ intersectLineSegmentPolyline,
10
+ intersectPolys
11
+ } from "../intersect.mjs";
12
+ import { approximately, pointInPolygon } from "../utils.mjs";
13
+ const Geometry2dFilters = {
14
+ EXCLUDE_NON_STANDARD: {
15
+ includeLabels: false,
16
+ includeInternal: false
17
+ },
18
+ INCLUDE_ALL: { includeLabels: true, includeInternal: true },
19
+ EXCLUDE_LABELS: { includeLabels: false, includeInternal: true },
20
+ EXCLUDE_INTERNAL: { includeLabels: true, includeInternal: false }
21
+ };
4
22
  class Geometry2d {
5
23
  isFilled = false;
6
24
  isClosed = true;
7
25
  isLabel = false;
26
+ isInternal = false;
8
27
  debugColor;
9
28
  ignore;
10
29
  constructor(opts) {
11
30
  this.isFilled = opts.isFilled;
12
31
  this.isClosed = opts.isClosed;
13
32
  this.isLabel = opts.isLabel ?? false;
33
+ this.isInternal = opts.isInternal ?? false;
14
34
  this.debugColor = opts.debugColor;
15
35
  this.ignore = opts.ignore;
16
36
  }
17
- // hitTestPoint(point: Vec, margin = 0, hitInside = false) {
18
- // // We've removed the broad phase here; that should be done outside of the call
19
- // return this.distanceToPoint(point, hitInside) <= margin
20
- // }
21
- hitTestPoint(point, margin = 0, hitInside = false) {
37
+ isExcludedByFilter(filters) {
38
+ if (!filters) return false;
39
+ if (this.isLabel && !filters.includeLabels) return true;
40
+ if (this.isInternal && !filters.includeInternal) return true;
41
+ return false;
42
+ }
43
+ hitTestPoint(point, margin = 0, hitInside = false, filters) {
44
+ if (this.isExcludedByFilter(filters)) return false;
22
45
  if (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)) {
23
46
  return true;
24
47
  }
25
48
  return Vec.Dist2(point, this.nearestPoint(point)) <= margin * margin;
26
49
  }
27
- distanceToPoint(point, hitInside = false) {
28
- return point.dist(this.nearestPoint(point)) * (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices) ? -1 : 1);
50
+ distanceToPoint(point, hitInside = false, filters) {
51
+ return point.dist(this.nearestPoint(point, filters)) * (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices) ? -1 : 1);
29
52
  }
30
- distanceToLineSegment(A, B) {
31
- if (A.equals(B)) return this.distanceToPoint(A);
53
+ distanceToLineSegment(A, B, filters) {
54
+ if (A.equals(B)) return this.distanceToPoint(A, false, filters);
32
55
  const { vertices } = this;
33
56
  let nearest;
34
57
  let dist = Infinity;
@@ -45,9 +68,28 @@ class Geometry2d {
45
68
  if (!nearest) throw Error("nearest point not found");
46
69
  return this.isClosed && this.isFilled && pointInPolygon(nearest, this.vertices) ? -dist : dist;
47
70
  }
48
- hitTestLineSegment(A, B, distance = 0) {
49
- return this.distanceToLineSegment(A, B) <= distance;
71
+ hitTestLineSegment(A, B, distance = 0, filters) {
72
+ return this.distanceToLineSegment(A, B, filters) <= distance;
73
+ }
74
+ intersectLineSegment(A, B, filters) {
75
+ if (this.isExcludedByFilter(filters)) return [];
76
+ const intersections = this.isClosed ? intersectLineSegmentPolygon(A, B, this.vertices) : intersectLineSegmentPolyline(A, B, this.vertices);
77
+ return intersections ?? [];
78
+ }
79
+ intersectCircle(center, radius, filters) {
80
+ if (this.isExcludedByFilter(filters)) return [];
81
+ const intersections = this.isClosed ? intersectCirclePolygon(center, radius, this.vertices) : intersectCirclePolyline(center, radius, this.vertices);
82
+ return intersections ?? [];
83
+ }
84
+ intersectPolygon(polygon, filters) {
85
+ if (this.isExcludedByFilter(filters)) return [];
86
+ return intersectPolys(polygon, this.vertices, true, this.isClosed);
87
+ }
88
+ intersectPolyline(polyline, filters) {
89
+ if (this.isExcludedByFilter(filters)) return [];
90
+ return intersectPolys(polyline, this.vertices, false, this.isClosed);
50
91
  }
92
+ /** @deprecated Iterate the vertices instead. */
51
93
  nearestPointOnLineSegment(A, B) {
52
94
  const { vertices } = this;
53
95
  let nearest;
@@ -69,11 +111,14 @@ class Geometry2d {
69
111
  const { bounds } = this;
70
112
  return !(point.x < bounds.minX - margin || point.y < bounds.minY - margin || point.x > bounds.maxX + margin || point.y > bounds.maxY + margin);
71
113
  }
114
+ transform(transform) {
115
+ return new TransformedGeometry2d(this, transform);
116
+ }
72
117
  _vertices;
73
118
  // eslint-disable-next-line no-restricted-syntax
74
119
  get vertices() {
75
120
  if (!this._vertices) {
76
- this._vertices = this.getVertices();
121
+ this._vertices = this.getVertices(Geometry2dFilters.EXCLUDE_LABELS);
77
122
  }
78
123
  return this._vertices;
79
124
  }
@@ -145,7 +190,85 @@ class Geometry2d {
145
190
  return Math.sqrt(length);
146
191
  }
147
192
  }
193
+ class TransformedGeometry2d extends Geometry2d {
194
+ constructor(geometry, matrix) {
195
+ super(geometry);
196
+ this.geometry = geometry;
197
+ this.matrix = matrix;
198
+ this.inverse = Mat.Inverse(matrix);
199
+ this.decomposed = Mat.Decompose(matrix);
200
+ assert(
201
+ approximately(this.decomposed.scaleX, this.decomposed.scaleY),
202
+ "non-uniform scaling is not yet supported"
203
+ );
204
+ }
205
+ inverse;
206
+ decomposed;
207
+ getVertices(filters) {
208
+ return this.geometry.getVertices(filters).map((v) => Mat.applyToPoint(this.matrix, v));
209
+ }
210
+ nearestPoint(point, filters) {
211
+ return Mat.applyToPoint(
212
+ this.matrix,
213
+ this.geometry.nearestPoint(Mat.applyToPoint(this.inverse, point), filters)
214
+ );
215
+ }
216
+ hitTestPoint(point, margin = 0, hitInside, filters) {
217
+ return this.geometry.hitTestPoint(
218
+ Mat.applyToPoint(this.inverse, point),
219
+ margin / this.decomposed.scaleX,
220
+ hitInside,
221
+ filters
222
+ );
223
+ }
224
+ distanceToPoint(point, hitInside = false, filters) {
225
+ return this.geometry.distanceToPoint(Mat.applyToPoint(this.inverse, point), hitInside, filters) * this.decomposed.scaleX;
226
+ }
227
+ distanceToLineSegment(A, B, filters) {
228
+ return this.geometry.distanceToLineSegment(
229
+ Mat.applyToPoint(this.inverse, A),
230
+ Mat.applyToPoint(this.inverse, B),
231
+ filters
232
+ ) * this.decomposed.scaleX;
233
+ }
234
+ hitTestLineSegment(A, B, distance = 0, filters) {
235
+ return this.geometry.hitTestLineSegment(
236
+ Mat.applyToPoint(this.inverse, A),
237
+ Mat.applyToPoint(this.inverse, B),
238
+ distance / this.decomposed.scaleX,
239
+ filters
240
+ );
241
+ }
242
+ intersectLineSegment(A, B, filters) {
243
+ return this.geometry.intersectLineSegment(
244
+ Mat.applyToPoint(this.inverse, A),
245
+ Mat.applyToPoint(this.inverse, B),
246
+ filters
247
+ );
248
+ }
249
+ intersectCircle(center, radius, filters) {
250
+ return this.geometry.intersectCircle(
251
+ Mat.applyToPoint(this.inverse, center),
252
+ radius / this.decomposed.scaleX,
253
+ filters
254
+ );
255
+ }
256
+ intersectPolygon(polygon, filters) {
257
+ return this.geometry.intersectPolygon(Mat.applyToPoints(this.inverse, polygon), filters);
258
+ }
259
+ intersectPolyline(polyline, filters) {
260
+ return this.geometry.intersectPolyline(Mat.applyToPoints(this.inverse, polyline), filters);
261
+ }
262
+ transform(transform) {
263
+ return new TransformedGeometry2d(this.geometry, Mat.Multiply(transform, this.matrix));
264
+ }
265
+ getSvgPathData() {
266
+ throw new Error("Cannot get SVG path data for transformed geometry.");
267
+ }
268
+ }
148
269
  export {
149
- Geometry2d
270
+ Geometry2d,
271
+ Geometry2dFilters,
272
+ TransformedGeometry2d
150
273
  };
151
274
  //# sourceMappingURL=Geometry2d.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/primitives/geometry/Geometry2d.ts"],
4
- "sourcesContent": ["import { Box } from '../Box'\nimport { Vec } from '../Vec'\nimport { pointInPolygon } from '../utils'\n\n/** @public */\nexport interface Geometry2dOptions {\n\tisFilled: boolean\n\tisClosed: boolean\n\tisLabel?: boolean\n\tdebugColor?: string\n\tignore?: boolean\n}\n\n/** @public */\nexport abstract class Geometry2d {\n\tisFilled = false\n\tisClosed = true\n\tisLabel = false\n\tdebugColor?: string\n\tignore?: boolean\n\n\tconstructor(opts: Geometry2dOptions) {\n\t\tthis.isFilled = opts.isFilled\n\t\tthis.isClosed = opts.isClosed\n\t\tthis.isLabel = opts.isLabel ?? false\n\t\tthis.debugColor = opts.debugColor\n\t\tthis.ignore = opts.ignore\n\t}\n\n\tabstract getVertices(): Vec[]\n\n\tabstract nearestPoint(point: Vec): Vec\n\n\t// hitTestPoint(point: Vec, margin = 0, hitInside = false) {\n\t// \t// We've removed the broad phase here; that should be done outside of the call\n\t// \treturn this.distanceToPoint(point, hitInside) <= margin\n\t// }\n\n\thitTestPoint(point: Vec, margin = 0, hitInside = false) {\n\t\t// First check whether the point is inside\n\t\tif (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)) {\n\t\t\treturn true\n\t\t}\n\t\t// Then check whether the distance is within the margin\n\t\treturn Vec.Dist2(point, this.nearestPoint(point)) <= margin * margin\n\t}\n\n\tdistanceToPoint(point: Vec, hitInside = false) {\n\t\treturn (\n\t\t\tpoint.dist(this.nearestPoint(point)) *\n\t\t\t(this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)\n\t\t\t\t? -1\n\t\t\t\t: 1)\n\t\t)\n\t}\n\n\tdistanceToLineSegment(A: Vec, B: Vec) {\n\t\tif (A.equals(B)) return this.distanceToPoint(A)\n\t\tconst { vertices } = this\n\t\tlet nearest: Vec | undefined\n\t\tlet dist = Infinity\n\t\tlet d: number, p: Vec, q: Vec\n\t\tfor (let i = 0; i < vertices.length; i++) {\n\t\t\tp = vertices[i]\n\t\t\tq = Vec.NearestPointOnLineSegment(A, B, p, true)\n\t\t\td = Vec.Dist2(p, q)\n\t\t\tif (d < dist) {\n\t\t\t\tdist = d\n\t\t\t\tnearest = q\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn this.isClosed && this.isFilled && pointInPolygon(nearest, this.vertices) ? -dist : dist\n\t}\n\n\thitTestLineSegment(A: Vec, B: Vec, distance = 0): boolean {\n\t\treturn this.distanceToLineSegment(A, B) <= distance\n\t}\n\n\tnearestPointOnLineSegment(A: Vec, B: Vec): Vec {\n\t\tconst { vertices } = this\n\t\tlet nearest: Vec | undefined\n\t\tlet dist = Infinity\n\t\tlet d: number, p: Vec, q: Vec\n\t\tfor (let i = 0; i < vertices.length; i++) {\n\t\t\tp = vertices[i]\n\t\t\tq = Vec.NearestPointOnLineSegment(A, B, p, true)\n\t\t\td = Vec.Dist2(p, q)\n\t\t\tif (d < dist) {\n\t\t\t\tdist = d\n\t\t\t\tnearest = q\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn nearest\n\t}\n\n\tisPointInBounds(point: Vec, margin = 0) {\n\t\tconst { bounds } = this\n\t\treturn !(\n\t\t\tpoint.x < bounds.minX - margin ||\n\t\t\tpoint.y < bounds.minY - margin ||\n\t\t\tpoint.x > bounds.maxX + margin ||\n\t\t\tpoint.y > bounds.maxY + margin\n\t\t)\n\t}\n\n\tprivate _vertices: Vec[] | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget vertices(): Vec[] {\n\t\tif (!this._vertices) {\n\t\t\tthis._vertices = this.getVertices()\n\t\t}\n\n\t\treturn this._vertices\n\t}\n\n\tgetBounds() {\n\t\treturn Box.FromPoints(this.vertices)\n\t}\n\n\tprivate _bounds: Box | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget bounds(): Box {\n\t\tif (!this._bounds) {\n\t\t\tthis._bounds = this.getBounds()\n\t\t}\n\t\treturn this._bounds\n\t}\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget center() {\n\t\treturn this.bounds.center\n\t}\n\n\tprivate _area: number | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget area() {\n\t\tif (!this._area) {\n\t\t\tthis._area = this.getArea()\n\t\t}\n\t\treturn this._area\n\t}\n\n\tgetArea() {\n\t\tif (!this.isClosed) {\n\t\t\treturn 0\n\t\t}\n\t\tconst { vertices } = this\n\t\tlet area = 0\n\t\tfor (let i = 0, n = vertices.length; i < n; i++) {\n\t\t\tconst curr = vertices[i]\n\t\t\tconst next = vertices[(i + 1) % n]\n\t\t\tarea += curr.x * next.y - next.x * curr.y\n\t\t}\n\t\treturn area / 2\n\t}\n\n\ttoSimpleSvgPath() {\n\t\tlet path = ''\n\n\t\tconst { vertices } = this\n\t\tconst n = vertices.length\n\n\t\tif (n === 0) return path\n\n\t\tpath += `M${vertices[0].x},${vertices[0].y}`\n\n\t\tfor (let i = 1; i < n; i++) {\n\t\t\tpath += `L${vertices[i].x},${vertices[i].y}`\n\t\t}\n\n\t\tif (this.isClosed) {\n\t\t\tpath += 'Z'\n\t\t}\n\n\t\treturn path\n\t}\n\n\tprivate _length?: number\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget length() {\n\t\tif (this._length) return this._length\n\t\tthis._length = this.getLength()\n\t\treturn this._length\n\t}\n\n\tgetLength() {\n\t\tconst { vertices } = this\n\t\tlet n1: Vec,\n\t\t\tp1 = vertices[0],\n\t\t\tlength = 0\n\t\tfor (let i = 1; i < vertices.length; i++) {\n\t\t\tn1 = vertices[i]\n\t\t\tlength += Vec.Dist2(p1, n1)\n\t\t\tp1 = n1\n\t\t}\n\t\treturn Math.sqrt(length)\n\t}\n\n\tabstract getSvgPathData(first: boolean): string\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { assert } from '@tldraw/utils'\nimport { Box } from '../Box'\nimport { Mat, MatModel } from '../Mat'\nimport { Vec, VecLike } from '../Vec'\nimport {\n\tintersectCirclePolygon,\n\tintersectCirclePolyline,\n\tintersectLineSegmentPolygon,\n\tintersectLineSegmentPolyline,\n\tintersectPolys,\n} from '../intersect'\nimport { approximately, pointInPolygon } from '../utils'\n\n/** @public */\nexport interface Geometry2dFilters {\n\treadonly includeLabels?: boolean\n\treadonly includeInternal?: boolean\n}\n\n/** @public */\nexport const Geometry2dFilters: {\n\tEXCLUDE_NON_STANDARD: Geometry2dFilters\n\tINCLUDE_ALL: Geometry2dFilters\n\tEXCLUDE_LABELS: Geometry2dFilters\n\tEXCLUDE_INTERNAL: Geometry2dFilters\n} = {\n\tEXCLUDE_NON_STANDARD: {\n\t\tincludeLabels: false,\n\t\tincludeInternal: false,\n\t},\n\tINCLUDE_ALL: { includeLabels: true, includeInternal: true },\n\tEXCLUDE_LABELS: { includeLabels: false, includeInternal: true },\n\tEXCLUDE_INTERNAL: { includeLabels: true, includeInternal: false },\n}\n\n/** @public */\nexport interface Geometry2dOptions {\n\tisFilled: boolean\n\tisClosed: boolean\n\tisLabel?: boolean\n\tisInternal?: boolean\n\tdebugColor?: string\n\tignore?: boolean\n}\n\n/** @public */\nexport abstract class Geometry2d {\n\tisFilled = false\n\tisClosed = true\n\tisLabel = false\n\tisInternal = false\n\tdebugColor?: string\n\tignore?: boolean\n\n\tconstructor(opts: Geometry2dOptions) {\n\t\tthis.isFilled = opts.isFilled\n\t\tthis.isClosed = opts.isClosed\n\t\tthis.isLabel = opts.isLabel ?? false\n\t\tthis.isInternal = opts.isInternal ?? false\n\t\tthis.debugColor = opts.debugColor\n\t\tthis.ignore = opts.ignore\n\t}\n\n\tisExcludedByFilter(filters?: Geometry2dFilters) {\n\t\tif (!filters) return false\n\t\tif (this.isLabel && !filters.includeLabels) return true\n\t\tif (this.isInternal && !filters.includeInternal) return true\n\t\treturn false\n\t}\n\n\tabstract getVertices(filters: Geometry2dFilters): Vec[]\n\n\tabstract nearestPoint(point: Vec, filters?: Geometry2dFilters): Vec\n\n\thitTestPoint(point: Vec, margin = 0, hitInside = false, filters?: Geometry2dFilters) {\n\t\tif (this.isExcludedByFilter(filters)) return false\n\t\t// First check whether the point is inside\n\t\tif (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)) {\n\t\t\treturn true\n\t\t}\n\t\t// Then check whether the distance is within the margin\n\t\treturn Vec.Dist2(point, this.nearestPoint(point)) <= margin * margin\n\t}\n\n\tdistanceToPoint(point: Vec, hitInside = false, filters?: Geometry2dFilters) {\n\t\treturn (\n\t\t\tpoint.dist(this.nearestPoint(point, filters)) *\n\t\t\t(this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)\n\t\t\t\t? -1\n\t\t\t\t: 1)\n\t\t)\n\t}\n\n\tdistanceToLineSegment(A: Vec, B: Vec, filters?: Geometry2dFilters) {\n\t\tif (A.equals(B)) return this.distanceToPoint(A, false, filters)\n\t\tconst { vertices } = this\n\t\tlet nearest: Vec | undefined\n\t\tlet dist = Infinity\n\t\tlet d: number, p: Vec, q: Vec\n\t\tfor (let i = 0; i < vertices.length; i++) {\n\t\t\tp = vertices[i]\n\t\t\tq = Vec.NearestPointOnLineSegment(A, B, p, true)\n\t\t\td = Vec.Dist2(p, q)\n\t\t\tif (d < dist) {\n\t\t\t\tdist = d\n\t\t\t\tnearest = q\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn this.isClosed && this.isFilled && pointInPolygon(nearest, this.vertices) ? -dist : dist\n\t}\n\n\thitTestLineSegment(A: Vec, B: Vec, distance = 0, filters?: Geometry2dFilters): boolean {\n\t\treturn this.distanceToLineSegment(A, B, filters) <= distance\n\t}\n\n\tintersectLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters): VecLike[] {\n\t\tif (this.isExcludedByFilter(filters)) return []\n\n\t\tconst intersections = this.isClosed\n\t\t\t? intersectLineSegmentPolygon(A, B, this.vertices)\n\t\t\t: intersectLineSegmentPolyline(A, B, this.vertices)\n\n\t\treturn intersections ?? []\n\t}\n\n\tintersectCircle(center: VecLike, radius: number, filters?: Geometry2dFilters): VecLike[] {\n\t\tif (this.isExcludedByFilter(filters)) return []\n\t\tconst intersections = this.isClosed\n\t\t\t? intersectCirclePolygon(center, radius, this.vertices)\n\t\t\t: intersectCirclePolyline(center, radius, this.vertices)\n\n\t\treturn intersections ?? []\n\t}\n\n\tintersectPolygon(polygon: VecLike[], filters?: Geometry2dFilters): VecLike[] {\n\t\tif (this.isExcludedByFilter(filters)) return []\n\n\t\treturn intersectPolys(polygon, this.vertices, true, this.isClosed)\n\t}\n\n\tintersectPolyline(polyline: VecLike[], filters?: Geometry2dFilters): VecLike[] {\n\t\tif (this.isExcludedByFilter(filters)) return []\n\t\treturn intersectPolys(polyline, this.vertices, false, this.isClosed)\n\t}\n\n\t/** @deprecated Iterate the vertices instead. */\n\tnearestPointOnLineSegment(A: Vec, B: Vec): Vec {\n\t\tconst { vertices } = this\n\t\tlet nearest: Vec | undefined\n\t\tlet dist = Infinity\n\t\tlet d: number, p: Vec, q: Vec\n\t\tfor (let i = 0; i < vertices.length; i++) {\n\t\t\tp = vertices[i]\n\t\t\tq = Vec.NearestPointOnLineSegment(A, B, p, true)\n\t\t\td = Vec.Dist2(p, q)\n\t\t\tif (d < dist) {\n\t\t\t\tdist = d\n\t\t\t\tnearest = q\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn nearest\n\t}\n\n\tisPointInBounds(point: Vec, margin = 0) {\n\t\tconst { bounds } = this\n\t\treturn !(\n\t\t\tpoint.x < bounds.minX - margin ||\n\t\t\tpoint.y < bounds.minY - margin ||\n\t\t\tpoint.x > bounds.maxX + margin ||\n\t\t\tpoint.y > bounds.maxY + margin\n\t\t)\n\t}\n\n\ttransform(transform: MatModel): Geometry2d {\n\t\treturn new TransformedGeometry2d(this, transform)\n\t}\n\n\tprivate _vertices: Vec[] | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget vertices(): Vec[] {\n\t\tif (!this._vertices) {\n\t\t\tthis._vertices = this.getVertices(Geometry2dFilters.EXCLUDE_LABELS)\n\t\t}\n\n\t\treturn this._vertices\n\t}\n\n\tgetBounds() {\n\t\treturn Box.FromPoints(this.vertices)\n\t}\n\n\tprivate _bounds: Box | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget bounds(): Box {\n\t\tif (!this._bounds) {\n\t\t\tthis._bounds = this.getBounds()\n\t\t}\n\t\treturn this._bounds\n\t}\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget center() {\n\t\treturn this.bounds.center\n\t}\n\n\tprivate _area: number | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget area() {\n\t\tif (!this._area) {\n\t\t\tthis._area = this.getArea()\n\t\t}\n\t\treturn this._area\n\t}\n\n\tgetArea() {\n\t\tif (!this.isClosed) {\n\t\t\treturn 0\n\t\t}\n\t\tconst { vertices } = this\n\t\tlet area = 0\n\t\tfor (let i = 0, n = vertices.length; i < n; i++) {\n\t\t\tconst curr = vertices[i]\n\t\t\tconst next = vertices[(i + 1) % n]\n\t\t\tarea += curr.x * next.y - next.x * curr.y\n\t\t}\n\t\treturn area / 2\n\t}\n\n\ttoSimpleSvgPath() {\n\t\tlet path = ''\n\n\t\tconst { vertices } = this\n\t\tconst n = vertices.length\n\n\t\tif (n === 0) return path\n\n\t\tpath += `M${vertices[0].x},${vertices[0].y}`\n\n\t\tfor (let i = 1; i < n; i++) {\n\t\t\tpath += `L${vertices[i].x},${vertices[i].y}`\n\t\t}\n\n\t\tif (this.isClosed) {\n\t\t\tpath += 'Z'\n\t\t}\n\n\t\treturn path\n\t}\n\n\tprivate _length?: number\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget length() {\n\t\tif (this._length) return this._length\n\t\tthis._length = this.getLength()\n\t\treturn this._length\n\t}\n\n\tgetLength() {\n\t\tconst { vertices } = this\n\t\tlet n1: Vec,\n\t\t\tp1 = vertices[0],\n\t\t\tlength = 0\n\t\tfor (let i = 1; i < vertices.length; i++) {\n\t\t\tn1 = vertices[i]\n\t\t\tlength += Vec.Dist2(p1, n1)\n\t\t\tp1 = n1\n\t\t}\n\t\treturn Math.sqrt(length)\n\t}\n\n\tabstract getSvgPathData(first: boolean): string\n}\n\n// =================================================================================================\n// Because Geometry2d.transform depends on TransformedGeometry2d, we need to define it here instead\n// of in its own files. This prevents a circular import error.\n// =================================================================================================\n\n/** @public */\nexport class TransformedGeometry2d extends Geometry2d {\n\tprivate readonly inverse: MatModel\n\tprivate readonly decomposed\n\n\tconstructor(\n\t\tprivate readonly geometry: Geometry2d,\n\t\tprivate readonly matrix: MatModel\n\t) {\n\t\tsuper(geometry)\n\t\tthis.inverse = Mat.Inverse(matrix)\n\t\tthis.decomposed = Mat.Decompose(matrix)\n\n\t\tassert(\n\t\t\tapproximately(this.decomposed.scaleX, this.decomposed.scaleY),\n\t\t\t'non-uniform scaling is not yet supported'\n\t\t)\n\t}\n\n\tgetVertices(filters: Geometry2dFilters): Vec[] {\n\t\treturn this.geometry.getVertices(filters).map((v) => Mat.applyToPoint(this.matrix, v))\n\t}\n\n\tnearestPoint(point: Vec, filters?: Geometry2dFilters): Vec {\n\t\treturn Mat.applyToPoint(\n\t\t\tthis.matrix,\n\t\t\tthis.geometry.nearestPoint(Mat.applyToPoint(this.inverse, point), filters)\n\t\t)\n\t}\n\n\toverride hitTestPoint(\n\t\tpoint: Vec,\n\t\tmargin = 0,\n\t\thitInside?: boolean,\n\t\tfilters?: Geometry2dFilters\n\t): boolean {\n\t\treturn this.geometry.hitTestPoint(\n\t\t\tMat.applyToPoint(this.inverse, point),\n\t\t\tmargin / this.decomposed.scaleX,\n\t\t\thitInside,\n\t\t\tfilters\n\t\t)\n\t}\n\n\toverride distanceToPoint(point: Vec, hitInside = false, filters?: Geometry2dFilters) {\n\t\treturn (\n\t\t\tthis.geometry.distanceToPoint(Mat.applyToPoint(this.inverse, point), hitInside, filters) *\n\t\t\tthis.decomposed.scaleX\n\t\t)\n\t}\n\n\toverride distanceToLineSegment(A: Vec, B: Vec, filters?: Geometry2dFilters) {\n\t\treturn (\n\t\t\tthis.geometry.distanceToLineSegment(\n\t\t\t\tMat.applyToPoint(this.inverse, A),\n\t\t\t\tMat.applyToPoint(this.inverse, B),\n\t\t\t\tfilters\n\t\t\t) * this.decomposed.scaleX\n\t\t)\n\t}\n\n\toverride hitTestLineSegment(A: Vec, B: Vec, distance = 0, filters?: Geometry2dFilters): boolean {\n\t\treturn this.geometry.hitTestLineSegment(\n\t\t\tMat.applyToPoint(this.inverse, A),\n\t\t\tMat.applyToPoint(this.inverse, B),\n\t\t\tdistance / this.decomposed.scaleX,\n\t\t\tfilters\n\t\t)\n\t}\n\n\toverride intersectLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {\n\t\treturn this.geometry.intersectLineSegment(\n\t\t\tMat.applyToPoint(this.inverse, A),\n\t\t\tMat.applyToPoint(this.inverse, B),\n\t\t\tfilters\n\t\t)\n\t}\n\n\toverride intersectCircle(center: VecLike, radius: number, filters?: Geometry2dFilters) {\n\t\treturn this.geometry.intersectCircle(\n\t\t\tMat.applyToPoint(this.inverse, center),\n\t\t\tradius / this.decomposed.scaleX,\n\t\t\tfilters\n\t\t)\n\t}\n\n\toverride intersectPolygon(polygon: VecLike[], filters?: Geometry2dFilters): VecLike[] {\n\t\treturn this.geometry.intersectPolygon(Mat.applyToPoints(this.inverse, polygon), filters)\n\t}\n\n\toverride intersectPolyline(polyline: VecLike[], filters?: Geometry2dFilters): VecLike[] {\n\t\treturn this.geometry.intersectPolyline(Mat.applyToPoints(this.inverse, polyline), filters)\n\t}\n\n\toverride transform(transform: MatModel): Geometry2d {\n\t\treturn new TransformedGeometry2d(this.geometry, Mat.Multiply(transform, this.matrix))\n\t}\n\n\tgetSvgPathData(): string {\n\t\tthrow new Error('Cannot get SVG path data for transformed geometry.')\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }