@tldraw/editor 3.8.0-canary.d0d3a3c316fd → 3.8.0-canary.d14754ca7629
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +114 -56
- package/dist-cjs/index.js +9 -8
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/config/TLSessionStateSnapshot.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +6 -3
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js +13 -0
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
- package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
- package/dist-cjs/lib/exports/StyleEmbedder.js.map +2 -2
- package/dist-cjs/lib/hooks/useDocumentEvents.js +1 -3
- package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +4 -0
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +3 -3
- package/dist-cjs/lib/options.js +1 -2
- package/dist-cjs/lib/options.js.map +2 -2
- package/dist-cjs/lib/utils/dom.js +6 -0
- package/dist-cjs/lib/utils/dom.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +114 -56
- package/dist-esm/index.mjs +3 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/config/TLSessionStateSnapshot.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +6 -3
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +13 -0
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
- package/dist-esm/lib/hooks/useDocumentEvents.mjs +2 -4
- package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +4 -0
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +3 -3
- package/dist-esm/lib/options.mjs +1 -2
- package/dist-esm/lib/options.mjs.map +2 -2
- package/dist-esm/lib/utils/dom.mjs +6 -0
- package/dist-esm/lib/utils/dom.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/package.json +20 -20
- package/src/index.ts +16 -1
- package/src/lib/config/TLSessionStateSnapshot.ts +3 -1
- package/src/lib/editor/Editor.ts +22 -20
- package/src/lib/editor/managers/SnapManager/BoundsSnaps.ts +4 -4
- package/src/lib/editor/shapes/ShapeUtil.ts +19 -0
- package/src/lib/editor/types/emit-types.ts +1 -0
- package/src/lib/editor/types/external-content.ts +104 -50
- package/src/lib/exports/StyleEmbedder.ts +1 -1
- package/src/lib/hooks/useDocumentEvents.ts +2 -11
- package/src/lib/hooks/usePassThroughWheelEvents.ts +7 -0
- package/src/lib/options.ts +3 -6
- package/src/lib/utils/dom.ts +12 -0
- package/src/version.ts +3 -3
|
@@ -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 { 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\nconst INPUTS = ['input', 'select', 'button', 'textarea']\n\nfunction areShortcutsDisabled(editor: Editor) {\n\tconst { activeElement } = document\n\n\treturn (\n\t\teditor.menus.hasOpenMenus() ||\n\t\t(activeElement &&\n\t\t\t(activeElement.getAttribute('contenteditable') ||\n\t\t\t\tINPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1))\n\t)\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAG1B,SAAS,gBAAgB,4BAA4B;
|
|
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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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -7,6 +7,10 @@ function usePassThroughWheelEvents(ref) {
|
|
|
7
7
|
useEffect(() => {
|
|
8
8
|
function onWheel(e) {
|
|
9
9
|
if (e.isSpecialRedispatchedEvent) return;
|
|
10
|
+
const elm2 = ref.current;
|
|
11
|
+
if (elm2 && elm2.scrollHeight > elm2.clientHeight) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
10
14
|
preventDefault(e);
|
|
11
15
|
const cvs = container.querySelector(".tl-canvas");
|
|
12
16
|
if (!cvs) return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/hooks/usePassThroughWheelEvents.ts"],
|
|
4
|
-
"sourcesContent": ["import { RefObject, useEffect } from 'react'\nimport { preventDefault } from '../utils/dom'\nimport { useContainer } from './useContainer'\n\n/** @public */\nexport function usePassThroughWheelEvents(ref: RefObject<HTMLElement>) {\n\tif (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')\n\n\tconst container = useContainer()\n\n\tuseEffect(() => {\n\t\tfunction onWheel(e: WheelEvent) {\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\t\t\tpreventDefault(e)\n\t\t\tconst cvs = container.querySelector('.tl-canvas')\n\t\t\tif (!cvs) return\n\t\t\tconst newEvent = new WheelEvent('wheel', e as any)\n\t\t\t;(newEvent as any).isSpecialRedispatchedEvent = true\n\t\t\tcvs.dispatchEvent(newEvent)\n\t\t}\n\n\t\tconst elm = ref.current\n\t\tif (!elm) return\n\n\t\telm.addEventListener('wheel', onWheel, { passive: false })\n\t\treturn () => {\n\t\t\telm.removeEventListener('wheel', onWheel)\n\t\t}\n\t}, [container, ref])\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAoB,iBAAiB;AACrC,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAGtB,SAAS,0BAA0B,KAA6B;AACtE,MAAI,CAAC,IAAK,OAAM,MAAM,gDAAgD;AAEtE,QAAM,YAAY,aAAa;AAE/B,YAAU,MAAM;AACf,aAAS,QAAQ,GAAe;AAC/B,UAAK,EAAU,2BAA4B;
|
|
6
|
-
"names": []
|
|
4
|
+
"sourcesContent": ["import { RefObject, useEffect } from 'react'\nimport { preventDefault } from '../utils/dom'\nimport { useContainer } from './useContainer'\n\n/** @public */\nexport function usePassThroughWheelEvents(ref: RefObject<HTMLElement>) {\n\tif (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')\n\n\tconst container = useContainer()\n\n\tuseEffect(() => {\n\t\tfunction onWheel(e: WheelEvent) {\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\n\t\t\t// if the element is scrollable, don't redispatch the event\n\t\t\tconst elm = ref.current\n\t\t\tif (elm && elm.scrollHeight > elm.clientHeight) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpreventDefault(e)\n\t\t\tconst cvs = container.querySelector('.tl-canvas')\n\t\t\tif (!cvs) return\n\t\t\tconst newEvent = new WheelEvent('wheel', e as any)\n\t\t\t;(newEvent as any).isSpecialRedispatchedEvent = true\n\t\t\tcvs.dispatchEvent(newEvent)\n\t\t}\n\n\t\tconst elm = ref.current\n\t\tif (!elm) return\n\n\t\telm.addEventListener('wheel', onWheel, { passive: false })\n\t\treturn () => {\n\t\t\telm.removeEventListener('wheel', onWheel)\n\t\t}\n\t}, [container, ref])\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAoB,iBAAiB;AACrC,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAGtB,SAAS,0BAA0B,KAA6B;AACtE,MAAI,CAAC,IAAK,OAAM,MAAM,gDAAgD;AAEtE,QAAM,YAAY,aAAa;AAE/B,YAAU,MAAM;AACf,aAAS,QAAQ,GAAe;AAC/B,UAAK,EAAU,2BAA4B;AAG3C,YAAMA,OAAM,IAAI;AAChB,UAAIA,QAAOA,KAAI,eAAeA,KAAI,cAAc;AAC/C;AAAA,MACD;AAEA,qBAAe,CAAC;AAChB,YAAM,MAAM,UAAU,cAAc,YAAY;AAChD,UAAI,CAAC,IAAK;AACV,YAAM,WAAW,IAAI,WAAW,SAAS,CAAQ;AAChD,MAAC,SAAiB,6BAA6B;AAChD,UAAI,cAAc,QAAQ;AAAA,IAC3B;AAEA,UAAM,MAAM,IAAI;AAChB,QAAI,CAAC,IAAK;AAEV,QAAI,iBAAiB,SAAS,SAAS,EAAE,SAAS,MAAM,CAAC;AACzD,WAAO,MAAM;AACZ,UAAI,oBAAoB,SAAS,OAAO;AAAA,IACzC;AAAA,EACD,GAAG,CAAC,WAAW,GAAG,CAAC;AACpB;",
|
|
6
|
+
"names": ["elm"]
|
|
7
7
|
}
|
package/dist-esm/lib/options.mjs
CHANGED
|
@@ -13,7 +13,6 @@ const defaultTldrawOptions = {
|
|
|
13
13
|
// 4 squared
|
|
14
14
|
defaultSvgPadding: 32,
|
|
15
15
|
cameraSlideFriction: 0.09,
|
|
16
|
-
maxPointsPerDrawShape: 500,
|
|
17
16
|
gridSteps: [
|
|
18
17
|
{ min: -1, mid: 0.15, step: 64 },
|
|
19
18
|
{ min: 0.05, mid: 0.375, step: 16 },
|
|
@@ -43,7 +42,7 @@ const defaultTldrawOptions = {
|
|
|
43
42
|
actionShortcutsLocation: "swap",
|
|
44
43
|
createTextOnCanvasDoubleClick: true,
|
|
45
44
|
exportProvider: Fragment,
|
|
46
|
-
|
|
45
|
+
enableToolbarKeyboardShortcuts: true
|
|
47
46
|
};
|
|
48
47
|
export {
|
|
49
48
|
defaultTldrawOptions
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/options.ts"],
|
|
4
|
-
"sourcesContent": ["import { ComponentType, Fragment } from 'react'\n\n/**\n * Options for configuring tldraw. For defaults, see {@link defaultTldrawOptions}.\n *\n * @example\n * ```tsx\n * const options: Partial<TldrawOptions> = {\n * maxPages: 3,\n * maxShapesPerPage: 1000,\n * }\n *\n * function MyTldrawComponent() {\n * return <Tldraw options={options} />\n * }\n * ```\n *\n * @public\n */\nexport interface TldrawOptions {\n\treadonly maxShapesPerPage: number\n\treadonly maxFilesAtOnce: number\n\treadonly maxPages: number\n\treadonly animationMediumMs: number\n\treadonly followChaseViewportSnap: number\n\treadonly doubleClickDurationMs: number\n\treadonly multiClickDurationMs: number\n\treadonly coarseDragDistanceSquared: number\n\treadonly dragDistanceSquared: number\n\treadonly defaultSvgPadding: number\n\treadonly cameraSlideFriction: number\n\treadonly
|
|
5
|
-
"mappings": "AAAA,SAAwB,gBAAgB;
|
|
4
|
+
"sourcesContent": ["import { ComponentType, Fragment } from 'react'\n\n/**\n * Options for configuring tldraw. For defaults, see {@link defaultTldrawOptions}.\n *\n * @example\n * ```tsx\n * const options: Partial<TldrawOptions> = {\n * maxPages: 3,\n * maxShapesPerPage: 1000,\n * }\n *\n * function MyTldrawComponent() {\n * return <Tldraw options={options} />\n * }\n * ```\n *\n * @public\n */\nexport interface TldrawOptions {\n\treadonly maxShapesPerPage: number\n\treadonly maxFilesAtOnce: number\n\treadonly maxPages: number\n\treadonly animationMediumMs: number\n\treadonly followChaseViewportSnap: number\n\treadonly doubleClickDurationMs: number\n\treadonly multiClickDurationMs: number\n\treadonly coarseDragDistanceSquared: number\n\treadonly dragDistanceSquared: number\n\treadonly defaultSvgPadding: number\n\treadonly cameraSlideFriction: number\n\treadonly gridSteps: readonly {\n\t\treadonly min: number\n\t\treadonly mid: number\n\t\treadonly step: number\n\t}[]\n\treadonly collaboratorInactiveTimeoutMs: number\n\treadonly collaboratorIdleTimeoutMs: number\n\treadonly collaboratorCheckIntervalMs: number\n\treadonly cameraMovingTimeoutMs: number\n\treadonly hitTestMargin: number\n\treadonly edgeScrollDelay: number\n\treadonly edgeScrollEaseDuration: number\n\treadonly edgeScrollSpeed: number\n\treadonly edgeScrollDistance: number\n\treadonly coarsePointerWidth: number\n\treadonly coarseHandleRadius: number\n\treadonly handleRadius: number\n\treadonly longPressDurationMs: number\n\treadonly textShadowLod: number\n\treadonly adjacentShapeMargin: number\n\treadonly flattenImageBoundsExpand: number\n\treadonly flattenImageBoundsPadding: number\n\treadonly laserDelayMs: number\n\treadonly maxExportDelayMs: number\n\t/**\n\t * How long should previews created by {@link Editor.createTemporaryAssetPreview} last before\n\t * they expire? Defaults to 3 minutes.\n\t */\n\treadonly temporaryAssetPreviewLifetimeMs: number\n\treadonly actionShortcutsLocation: 'menu' | 'toolbar' | 'swap'\n\treadonly createTextOnCanvasDoubleClick: boolean\n\t/**\n\t * The react provider to use when exporting an image. This is useful if your shapes depend on\n\t * external context providers. By default, this is `React.Fragment`.\n\t */\n\treadonly exportProvider: ComponentType<{ children: React.ReactNode }>\n\t/**\n\t * By default, the toolbar items are accessible via number shortcuts according to their order. To disable this, set this option to false.\n\t */\n\treadonly enableToolbarKeyboardShortcuts: boolean\n}\n\n/** @public */\nexport const defaultTldrawOptions = {\n\tmaxShapesPerPage: 4000,\n\tmaxFilesAtOnce: 100,\n\tmaxPages: 40,\n\tanimationMediumMs: 320,\n\tfollowChaseViewportSnap: 2,\n\tdoubleClickDurationMs: 450,\n\tmultiClickDurationMs: 200,\n\tcoarseDragDistanceSquared: 36, // 6 squared\n\tdragDistanceSquared: 16, // 4 squared\n\tdefaultSvgPadding: 32,\n\tcameraSlideFriction: 0.09,\n\tgridSteps: [\n\t\t{ min: -1, mid: 0.15, step: 64 },\n\t\t{ min: 0.05, mid: 0.375, step: 16 },\n\t\t{ min: 0.15, mid: 1, step: 4 },\n\t\t{ min: 0.7, mid: 2.5, step: 1 },\n\t],\n\tcollaboratorInactiveTimeoutMs: 60000,\n\tcollaboratorIdleTimeoutMs: 3000,\n\tcollaboratorCheckIntervalMs: 1200,\n\tcameraMovingTimeoutMs: 64,\n\thitTestMargin: 8,\n\tedgeScrollDelay: 200,\n\tedgeScrollEaseDuration: 200,\n\tedgeScrollSpeed: 25,\n\tedgeScrollDistance: 8,\n\tcoarsePointerWidth: 12,\n\tcoarseHandleRadius: 20,\n\thandleRadius: 12,\n\tlongPressDurationMs: 500,\n\ttextShadowLod: 0.35,\n\tadjacentShapeMargin: 10,\n\tflattenImageBoundsExpand: 64,\n\tflattenImageBoundsPadding: 16,\n\tlaserDelayMs: 1200,\n\tmaxExportDelayMs: 5000,\n\ttemporaryAssetPreviewLifetimeMs: 180000,\n\tactionShortcutsLocation: 'swap',\n\tcreateTextOnCanvasDoubleClick: true,\n\texportProvider: Fragment,\n\tenableToolbarKeyboardShortcuts: true,\n} as const satisfies TldrawOptions\n"],
|
|
5
|
+
"mappings": "AAAA,SAAwB,gBAAgB;AA0EjC,MAAM,uBAAuB;AAAA,EACnC,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,mBAAmB;AAAA,EACnB,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,2BAA2B;AAAA;AAAA,EAC3B,qBAAqB;AAAA;AAAA,EACrB,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,WAAW;AAAA,IACV,EAAE,KAAK,IAAI,KAAK,MAAM,MAAM,GAAG;AAAA,IAC/B,EAAE,KAAK,MAAM,KAAK,OAAO,MAAM,GAAG;AAAA,IAClC,EAAE,KAAK,MAAM,KAAK,GAAG,MAAM,EAAE;AAAA,IAC7B,EAAE,KAAK,KAAK,KAAK,KAAK,MAAM,EAAE;AAAA,EAC/B;AAAA,EACA,+BAA+B;AAAA,EAC/B,2BAA2B;AAAA,EAC3B,6BAA6B;AAAA,EAC7B,uBAAuB;AAAA,EACvB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,qBAAqB;AAAA,EACrB,0BAA0B;AAAA,EAC1B,2BAA2B;AAAA,EAC3B,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,iCAAiC;AAAA,EACjC,yBAAyB;AAAA,EACzB,+BAA+B;AAAA,EAC/B,gBAAgB;AAAA,EAChB,gCAAgC;AACjC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -40,7 +40,13 @@ const setStyleProperty = (elm, property, value) => {
|
|
|
40
40
|
if (!elm) return;
|
|
41
41
|
elm.style.setProperty(property, value);
|
|
42
42
|
};
|
|
43
|
+
const INPUTS = ["input", "select", "button", "textarea"];
|
|
44
|
+
function activeElementShouldCaptureKeys() {
|
|
45
|
+
const { activeElement } = document;
|
|
46
|
+
return !!(activeElement && (activeElement.getAttribute("contenteditable") || INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1));
|
|
47
|
+
}
|
|
43
48
|
export {
|
|
49
|
+
activeElementShouldCaptureKeys,
|
|
44
50
|
loopToHtmlElement,
|
|
45
51
|
preventDefault,
|
|
46
52
|
releasePointerCapture,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/utils/dom.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nThis is used to facilitate double clicking and pointer capture on elements.\n\nThe events in this file are possibly set on individual SVG elements, \nsuch as handles or corner handles, rather than on HTML elements or \nSVGSVGElements. Raw SVG elemnets do not support pointerCapture in \nmost cases, meaning that in order for pointer capture to work, we \nneed to crawl up the DOM tree to find the nearest HTML element. Then,\nin order for that element to also call the `onPointerUp` event from\nthis file, we need to manually set that event on that element and\nlater remove it when the pointerup occurs. This is a potential leak\nif the user clicks on a handle but the pointerup does not fire for\nwhatever reason.\n*/\n\nimport React from 'react'\nimport { debugFlags, pointerCaptureTrackingObject } from './debug-flags'\n\n/** @public */\nexport function loopToHtmlElement(elm: Element): HTMLElement {\n\tif (elm instanceof HTMLElement) return elm\n\tif (elm.parentElement) return loopToHtmlElement(elm.parentElement)\n\telse throw Error('Could not find a parent element of an HTML type!')\n}\n\n/**\n * This function calls `event.preventDefault()` for you. Why is that useful?\n *\n * Beacuase if you enable `window.preventDefaultLogging = true` it'll log out a message when it\n * happens. Because we use console.warn rather than (log) you'll get a stack trace in the inspector\n * telling you exactly where it happened. This is important because `e.preventDefault()` is the\n * source of many bugs, but unfortuantly it can't be avoided because it also stops a lot of default\n * behaviour which doesn't make sense in our UI\n *\n * @param event - To prevent default on\n * @public\n */\nexport function preventDefault(event: React.BaseSyntheticEvent | Event) {\n\tevent.preventDefault()\n\tif (debugFlags.logPreventDefaults.get()) {\n\t\tconsole.warn('preventDefault called on event:', event)\n\t}\n}\n\n/** @public */\nexport function setPointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\telement.setPointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\ttrackingObj.set(element, (trackingObj.get(element) ?? 0) + 1)\n\t\tconsole.warn('setPointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport function releasePointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\tif (!element.hasPointerCapture(event.pointerId)) {\n\t\treturn\n\t}\n\n\telement.releasePointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\tif (trackingObj.get(element) === 1) {\n\t\t\ttrackingObj.delete(element)\n\t\t} else if (trackingObj.has(element)) {\n\t\t\ttrackingObj.set(element, trackingObj.get(element)! - 1)\n\t\t} else {\n\t\t\tconsole.warn('Release without capture')\n\t\t}\n\t\tconsole.warn('releasePointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport const stopEventPropagation = (e: any) => e.stopPropagation()\n\n/** @internal */\nexport const setStyleProperty = (\n\telm: HTMLElement | null,\n\tproperty: string,\n\tvalue: string | number\n) => {\n\tif (!elm) return\n\telm.style.setProperty(property, value as string)\n}\n"],
|
|
5
|
-
"mappings": "AAgBA,SAAS,YAAY,oCAAoC;AAGlD,SAAS,kBAAkB,KAA2B;AAC5D,MAAI,eAAe,YAAa,QAAO;AACvC,MAAI,IAAI,cAAe,QAAO,kBAAkB,IAAI,aAAa;AAAA,MAC5D,OAAM,MAAM,kDAAkD;AACpE;AAcO,SAAS,eAAe,OAAyC;AACvE,QAAM,eAAe;AACrB,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,YAAQ,KAAK,mCAAmC,KAAK;AAAA,EACtD;AACD;AAGO,SAAS,kBACf,SACA,OACC;AACD,UAAQ,kBAAkB,MAAM,SAAS;AACzC,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,6BAA6B,IAAI;AACrD,gBAAY,IAAI,UAAU,YAAY,IAAI,OAAO,KAAK,KAAK,CAAC;AAC5D,YAAQ,KAAK,wCAAwC,SAAS,KAAK;AAAA,EACpE;AACD;AAGO,SAAS,sBACf,SACA,OACC;AACD,MAAI,CAAC,QAAQ,kBAAkB,MAAM,SAAS,GAAG;AAChD;AAAA,EACD;AAEA,UAAQ,sBAAsB,MAAM,SAAS;AAC7C,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,6BAA6B,IAAI;AACrD,QAAI,YAAY,IAAI,OAAO,MAAM,GAAG;AACnC,kBAAY,OAAO,OAAO;AAAA,IAC3B,WAAW,YAAY,IAAI,OAAO,GAAG;AACpC,kBAAY,IAAI,SAAS,YAAY,IAAI,OAAO,IAAK,CAAC;AAAA,IACvD,OAAO;AACN,cAAQ,KAAK,yBAAyB;AAAA,IACvC;AACA,YAAQ,KAAK,4CAA4C,SAAS,KAAK;AAAA,EACxE;AACD;AAGO,MAAM,uBAAuB,CAAC,MAAW,EAAE,gBAAgB;AAG3D,MAAM,mBAAmB,CAC/B,KACA,UACA,UACI;AACJ,MAAI,CAAC,IAAK;AACV,MAAI,MAAM,YAAY,UAAU,KAAe;AAChD;",
|
|
4
|
+
"sourcesContent": ["/*\nThis is used to facilitate double clicking and pointer capture on elements.\n\nThe events in this file are possibly set on individual SVG elements, \nsuch as handles or corner handles, rather than on HTML elements or \nSVGSVGElements. Raw SVG elemnets do not support pointerCapture in \nmost cases, meaning that in order for pointer capture to work, we \nneed to crawl up the DOM tree to find the nearest HTML element. Then,\nin order for that element to also call the `onPointerUp` event from\nthis file, we need to manually set that event on that element and\nlater remove it when the pointerup occurs. This is a potential leak\nif the user clicks on a handle but the pointerup does not fire for\nwhatever reason.\n*/\n\nimport React from 'react'\nimport { debugFlags, pointerCaptureTrackingObject } from './debug-flags'\n\n/** @public */\nexport function loopToHtmlElement(elm: Element): HTMLElement {\n\tif (elm instanceof HTMLElement) return elm\n\tif (elm.parentElement) return loopToHtmlElement(elm.parentElement)\n\telse throw Error('Could not find a parent element of an HTML type!')\n}\n\n/**\n * This function calls `event.preventDefault()` for you. Why is that useful?\n *\n * Beacuase if you enable `window.preventDefaultLogging = true` it'll log out a message when it\n * happens. Because we use console.warn rather than (log) you'll get a stack trace in the inspector\n * telling you exactly where it happened. This is important because `e.preventDefault()` is the\n * source of many bugs, but unfortuantly it can't be avoided because it also stops a lot of default\n * behaviour which doesn't make sense in our UI\n *\n * @param event - To prevent default on\n * @public\n */\nexport function preventDefault(event: React.BaseSyntheticEvent | Event) {\n\tevent.preventDefault()\n\tif (debugFlags.logPreventDefaults.get()) {\n\t\tconsole.warn('preventDefault called on event:', event)\n\t}\n}\n\n/** @public */\nexport function setPointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\telement.setPointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\ttrackingObj.set(element, (trackingObj.get(element) ?? 0) + 1)\n\t\tconsole.warn('setPointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport function releasePointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\tif (!element.hasPointerCapture(event.pointerId)) {\n\t\treturn\n\t}\n\n\telement.releasePointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\tif (trackingObj.get(element) === 1) {\n\t\t\ttrackingObj.delete(element)\n\t\t} else if (trackingObj.has(element)) {\n\t\t\ttrackingObj.set(element, trackingObj.get(element)! - 1)\n\t\t} else {\n\t\t\tconsole.warn('Release without capture')\n\t\t}\n\t\tconsole.warn('releasePointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport const stopEventPropagation = (e: any) => e.stopPropagation()\n\n/** @internal */\nexport const setStyleProperty = (\n\telm: HTMLElement | null,\n\tproperty: string,\n\tvalue: string | number\n) => {\n\tif (!elm) return\n\telm.style.setProperty(property, value as string)\n}\n\nconst INPUTS = ['input', 'select', 'button', 'textarea']\n\n/** @internal */\nexport function activeElementShouldCaptureKeys() {\n\tconst { activeElement } = document\n\treturn !!(\n\t\tactiveElement &&\n\t\t(activeElement.getAttribute('contenteditable') ||\n\t\t\tINPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1)\n\t)\n}\n"],
|
|
5
|
+
"mappings": "AAgBA,SAAS,YAAY,oCAAoC;AAGlD,SAAS,kBAAkB,KAA2B;AAC5D,MAAI,eAAe,YAAa,QAAO;AACvC,MAAI,IAAI,cAAe,QAAO,kBAAkB,IAAI,aAAa;AAAA,MAC5D,OAAM,MAAM,kDAAkD;AACpE;AAcO,SAAS,eAAe,OAAyC;AACvE,QAAM,eAAe;AACrB,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,YAAQ,KAAK,mCAAmC,KAAK;AAAA,EACtD;AACD;AAGO,SAAS,kBACf,SACA,OACC;AACD,UAAQ,kBAAkB,MAAM,SAAS;AACzC,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,6BAA6B,IAAI;AACrD,gBAAY,IAAI,UAAU,YAAY,IAAI,OAAO,KAAK,KAAK,CAAC;AAC5D,YAAQ,KAAK,wCAAwC,SAAS,KAAK;AAAA,EACpE;AACD;AAGO,SAAS,sBACf,SACA,OACC;AACD,MAAI,CAAC,QAAQ,kBAAkB,MAAM,SAAS,GAAG;AAChD;AAAA,EACD;AAEA,UAAQ,sBAAsB,MAAM,SAAS;AAC7C,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,6BAA6B,IAAI;AACrD,QAAI,YAAY,IAAI,OAAO,MAAM,GAAG;AACnC,kBAAY,OAAO,OAAO;AAAA,IAC3B,WAAW,YAAY,IAAI,OAAO,GAAG;AACpC,kBAAY,IAAI,SAAS,YAAY,IAAI,OAAO,IAAK,CAAC;AAAA,IACvD,OAAO;AACN,cAAQ,KAAK,yBAAyB;AAAA,IACvC;AACA,YAAQ,KAAK,4CAA4C,SAAS,KAAK;AAAA,EACxE;AACD;AAGO,MAAM,uBAAuB,CAAC,MAAW,EAAE,gBAAgB;AAG3D,MAAM,mBAAmB,CAC/B,KACA,UACA,UACI;AACJ,MAAI,CAAC,IAAK;AACV,MAAI,MAAM,YAAY,UAAU,KAAe;AAChD;AAEA,MAAM,SAAS,CAAC,SAAS,UAAU,UAAU,UAAU;AAGhD,SAAS,iCAAiC;AAChD,QAAM,EAAE,cAAc,IAAI;AAC1B,SAAO,CAAC,EACP,kBACC,cAAc,aAAa,iBAAiB,KAC5C,OAAO,QAAQ,cAAc,QAAQ,YAAY,CAAC,IAAI;AAEzD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/version.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const version = "3.8.0-canary.
|
|
1
|
+
const version = "3.8.0-canary.d14754ca7629";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2024-09-13T14:36:29.063Z",
|
|
4
|
-
minor: "2025-
|
|
5
|
-
patch: "2025-
|
|
4
|
+
minor: "2025-02-12T14:00:45.263Z",
|
|
5
|
+
patch: "2025-02-12T14:00:45.263Z"
|
|
6
6
|
};
|
|
7
7
|
export {
|
|
8
8
|
publishDates,
|
package/dist-esm/version.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/version.ts"],
|
|
4
|
-
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.8.0-canary.
|
|
4
|
+
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.8.0-canary.d14754ca7629'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-02-12T14:00:45.263Z',\n\tpatch: '2025-02-12T14:00:45.263Z',\n}\n"],
|
|
5
5
|
"mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tldraw/editor",
|
|
3
3
|
"description": "A tiny little drawing app (editor).",
|
|
4
|
-
"version": "3.8.0-canary.
|
|
4
|
+
"version": "3.8.0-canary.d14754ca7629",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -45,37 +45,37 @@
|
|
|
45
45
|
"lint": "yarn run -T tsx ../../internal/scripts/lint.ts"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@tldraw/state": "3.8.0-canary.
|
|
49
|
-
"@tldraw/state-react": "3.8.0-canary.
|
|
50
|
-
"@tldraw/store": "3.8.0-canary.
|
|
51
|
-
"@tldraw/tlschema": "3.8.0-canary.
|
|
52
|
-
"@tldraw/utils": "3.8.0-canary.
|
|
53
|
-
"@tldraw/validate": "3.8.0-canary.
|
|
54
|
-
"@types/core-js": "^2.5.
|
|
55
|
-
"@use-gesture/react": "^10.
|
|
48
|
+
"@tldraw/state": "3.8.0-canary.d14754ca7629",
|
|
49
|
+
"@tldraw/state-react": "3.8.0-canary.d14754ca7629",
|
|
50
|
+
"@tldraw/store": "3.8.0-canary.d14754ca7629",
|
|
51
|
+
"@tldraw/tlschema": "3.8.0-canary.d14754ca7629",
|
|
52
|
+
"@tldraw/utils": "3.8.0-canary.d14754ca7629",
|
|
53
|
+
"@tldraw/validate": "3.8.0-canary.d14754ca7629",
|
|
54
|
+
"@types/core-js": "^2.5.8",
|
|
55
|
+
"@use-gesture/react": "^10.3.1",
|
|
56
56
|
"canvas-size": "~2.0.0",
|
|
57
|
-
"classnames": "^2.
|
|
58
|
-
"core-js": "^3.
|
|
57
|
+
"classnames": "^2.5.1",
|
|
58
|
+
"core-js": "^3.40.0",
|
|
59
59
|
"eventemitter3": "^4.0.7",
|
|
60
60
|
"idb": "^7.1.1",
|
|
61
61
|
"is-plain-object": "^5.0.0",
|
|
62
62
|
"lodash.isequal": "^4.5.0"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
|
-
"react": "^18.2.0",
|
|
66
|
-
"react-dom": "^18.2.0"
|
|
65
|
+
"react": "^18.2.0 || ^19.0.0",
|
|
66
|
+
"react-dom": "^18.2.0 || ^19.0.0"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"@peculiar/webcrypto": "^1.
|
|
70
|
-
"@testing-library/jest-dom": "^5.
|
|
71
|
-
"@testing-library/react": "^15.0.
|
|
72
|
-
"@types/benchmark": "^2.1.
|
|
69
|
+
"@peculiar/webcrypto": "^1.5.0",
|
|
70
|
+
"@testing-library/jest-dom": "^5.17.0",
|
|
71
|
+
"@testing-library/react": "^15.0.7",
|
|
72
|
+
"@types/benchmark": "^2.1.5",
|
|
73
73
|
"@types/canvas-size": "^1.2.2",
|
|
74
|
-
"@types/wicg-file-system-access": "^2020.9.
|
|
74
|
+
"@types/wicg-file-system-access": "^2020.9.8",
|
|
75
75
|
"benchmark": "^2.1.4",
|
|
76
|
-
"fake-indexeddb": "^4.0.
|
|
76
|
+
"fake-indexeddb": "^4.0.2",
|
|
77
77
|
"jest-canvas-mock": "^2.5.2",
|
|
78
|
-
"jest-environment-jsdom": "^29.
|
|
78
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
79
79
|
"lazyrepo": "0.0.0-alpha.27",
|
|
80
80
|
"resize-observer-polyfill": "^1.5.1"
|
|
81
81
|
},
|
package/src/index.ts
CHANGED
|
@@ -240,9 +240,23 @@ export {
|
|
|
240
240
|
type UiEventType,
|
|
241
241
|
} from './lib/editor/types/event-types'
|
|
242
242
|
export {
|
|
243
|
-
type
|
|
243
|
+
type TLBaseExternalContent,
|
|
244
|
+
type TLEmbedExternalContent,
|
|
245
|
+
type TLErrorExternalContentSource,
|
|
246
|
+
type TLExcalidrawExternalContent,
|
|
247
|
+
type TLExcalidrawExternalContentSource,
|
|
248
|
+
type TLExternalAsset,
|
|
244
249
|
type TLExternalContent,
|
|
245
250
|
type TLExternalContentSource,
|
|
251
|
+
type TLFileExternalAsset,
|
|
252
|
+
type TLFilesExternalContent,
|
|
253
|
+
type TLSvgTextExternalContent,
|
|
254
|
+
type TLTextExternalContent,
|
|
255
|
+
type TLTextExternalContentSource,
|
|
256
|
+
type TLTldrawExternalContent,
|
|
257
|
+
type TLTldrawExternalContentSource,
|
|
258
|
+
type TLUrlExternalAsset,
|
|
259
|
+
type TLUrlExternalContent,
|
|
246
260
|
} from './lib/editor/types/external-content'
|
|
247
261
|
export {
|
|
248
262
|
type TLHistoryBatchOptions,
|
|
@@ -401,6 +415,7 @@ export {
|
|
|
401
415
|
type TLDeepLinkOptions,
|
|
402
416
|
} from './lib/utils/deepLinks'
|
|
403
417
|
export {
|
|
418
|
+
activeElementShouldCaptureKeys,
|
|
404
419
|
loopToHtmlElement,
|
|
405
420
|
preventDefault,
|
|
406
421
|
releasePointerCapture,
|
|
@@ -50,7 +50,9 @@ function iOS() {
|
|
|
50
50
|
* @public
|
|
51
51
|
*/
|
|
52
52
|
export const TAB_ID: string = window
|
|
53
|
-
? window[tabIdKey] ??
|
|
53
|
+
? (window[tabIdKey] ??
|
|
54
|
+
getFromSessionStorage(tabIdKey) ??
|
|
55
|
+
`TLDRAW_INSTANCE_STATE_V1_` + uniqueId())
|
|
54
56
|
: '<error>'
|
|
55
57
|
if (window) {
|
|
56
58
|
window[tabIdKey] = TAB_ID
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -155,7 +155,7 @@ import {
|
|
|
155
155
|
TLPointerEventInfo,
|
|
156
156
|
TLWheelEventInfo,
|
|
157
157
|
} from './types/event-types'
|
|
158
|
-
import {
|
|
158
|
+
import { TLExternalAsset, TLExternalContent } from './types/external-content'
|
|
159
159
|
import { TLHistoryBatchOptions } from './types/history-types'
|
|
160
160
|
import {
|
|
161
161
|
OptionalKeys,
|
|
@@ -1400,8 +1400,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1400
1400
|
*
|
|
1401
1401
|
* @example
|
|
1402
1402
|
* ```ts
|
|
1403
|
-
*
|
|
1404
|
-
*
|
|
1403
|
+
* editor.getStateDescendant('select')
|
|
1404
|
+
* editor.getStateDescendant('select.brushing')
|
|
1405
1405
|
* ```
|
|
1406
1406
|
*
|
|
1407
1407
|
* @param path - The descendant's path of state ids, separated by periods.
|
|
@@ -1681,7 +1681,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1681
1681
|
* @public
|
|
1682
1682
|
*/
|
|
1683
1683
|
isAncestorSelected(shape: TLShape | TLShapeId): boolean {
|
|
1684
|
-
const id = typeof shape === 'string' ? shape : shape?.id ?? null
|
|
1684
|
+
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
1685
1685
|
const _shape = this.getShape(id)
|
|
1686
1686
|
if (!_shape) return false
|
|
1687
1687
|
const selectedShapeIds = this.getSelectedShapeIds()
|
|
@@ -1938,7 +1938,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1938
1938
|
* @public
|
|
1939
1939
|
*/
|
|
1940
1940
|
setFocusedGroup(shape: TLShapeId | TLGroupShape | null): this {
|
|
1941
|
-
const id = typeof shape === 'string' ? shape : shape?.id ?? null
|
|
1941
|
+
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
1942
1942
|
|
|
1943
1943
|
if (id !== null) {
|
|
1944
1944
|
const shape = this.getShape(id)
|
|
@@ -2021,7 +2021,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2021
2021
|
* @public
|
|
2022
2022
|
*/
|
|
2023
2023
|
setEditingShape(shape: TLShapeId | TLShape | null): this {
|
|
2024
|
-
const id = typeof shape === 'string' ? shape : shape?.id ?? null
|
|
2024
|
+
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2025
2025
|
if (id !== this.getEditingShapeId()) {
|
|
2026
2026
|
if (id) {
|
|
2027
2027
|
const shape = this.getShape(id)
|
|
@@ -2082,7 +2082,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2082
2082
|
* @public
|
|
2083
2083
|
*/
|
|
2084
2084
|
setHoveredShape(shape: TLShapeId | TLShape | null): this {
|
|
2085
|
-
const id = typeof shape === 'string' ? shape : shape?.id ?? null
|
|
2085
|
+
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2086
2086
|
if (id === this.getHoveredShapeId()) return this
|
|
2087
2087
|
this.run(
|
|
2088
2088
|
() => {
|
|
@@ -2231,7 +2231,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2231
2231
|
* @public
|
|
2232
2232
|
*/
|
|
2233
2233
|
setCroppingShape(shape: TLShapeId | TLShape | null): this {
|
|
2234
|
-
const id = typeof shape === 'string' ? shape : shape?.id ?? null
|
|
2234
|
+
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2235
2235
|
if (id !== this.getCroppingShapeId()) {
|
|
2236
2236
|
this.run(
|
|
2237
2237
|
() => {
|
|
@@ -7975,10 +7975,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7975
7975
|
|
|
7976
7976
|
/** @internal */
|
|
7977
7977
|
externalAssetContentHandlers: {
|
|
7978
|
-
[K in
|
|
7979
|
-
[Key in K]:
|
|
7980
|
-
| null
|
|
7981
|
-
| ((info: TLExternalAssetContent & { type: Key }) => Promise<TLAsset | undefined>)
|
|
7978
|
+
[K in TLExternalAsset['type']]: {
|
|
7979
|
+
[Key in K]: null | ((info: TLExternalAsset & { type: Key }) => Promise<TLAsset | undefined>)
|
|
7982
7980
|
}[K]
|
|
7983
7981
|
} = {
|
|
7984
7982
|
file: null,
|
|
@@ -8007,9 +8005,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8007
8005
|
*
|
|
8008
8006
|
* @public
|
|
8009
8007
|
*/
|
|
8010
|
-
registerExternalAssetHandler<T extends
|
|
8008
|
+
registerExternalAssetHandler<T extends TLExternalAsset['type']>(
|
|
8011
8009
|
type: T,
|
|
8012
|
-
handler: null | ((info:
|
|
8010
|
+
handler: null | ((info: TLExternalAsset & { type: T }) => Promise<TLAsset>)
|
|
8013
8011
|
): this {
|
|
8014
8012
|
this.externalAssetContentHandlers[type] = handler as any
|
|
8015
8013
|
return this
|
|
@@ -8077,18 +8075,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8077
8075
|
* @param info - Info about the external content.
|
|
8078
8076
|
* @returns The asset.
|
|
8079
8077
|
*/
|
|
8080
|
-
async getAssetForExternalContent(info:
|
|
8078
|
+
async getAssetForExternalContent(info: TLExternalAsset): Promise<TLAsset | undefined> {
|
|
8081
8079
|
return await this.externalAssetContentHandlers[info.type]?.(info as any)
|
|
8082
8080
|
}
|
|
8083
8081
|
|
|
8084
|
-
hasExternalAssetHandler(type:
|
|
8082
|
+
hasExternalAssetHandler(type: TLExternalAsset['type']): boolean {
|
|
8085
8083
|
return !!this.externalAssetContentHandlers[type]
|
|
8086
8084
|
}
|
|
8087
8085
|
|
|
8088
8086
|
/** @internal */
|
|
8089
8087
|
externalContentHandlers: {
|
|
8090
8088
|
[K in TLExternalContent<any>['type']]: {
|
|
8091
|
-
[Key in K]: null | ((info: TLExternalContent<any
|
|
8089
|
+
[Key in K]: null | ((info: Extract<TLExternalContent<any>, { type: Key }>) => void)
|
|
8092
8090
|
}[K]
|
|
8093
8091
|
} = {
|
|
8094
8092
|
text: null,
|
|
@@ -8096,6 +8094,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8096
8094
|
embed: null,
|
|
8097
8095
|
'svg-text': null,
|
|
8098
8096
|
url: null,
|
|
8097
|
+
tldraw: null,
|
|
8098
|
+
excalidraw: null,
|
|
8099
8099
|
}
|
|
8100
8100
|
|
|
8101
8101
|
/**
|
|
@@ -8123,7 +8123,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8123
8123
|
| null
|
|
8124
8124
|
| ((
|
|
8125
8125
|
info: T extends TLExternalContent<E>['type']
|
|
8126
|
-
? TLExternalContent<E
|
|
8126
|
+
? Extract<TLExternalContent<E>, { type: T }>
|
|
8127
8127
|
: TLExternalContent<E>
|
|
8128
8128
|
) => void)
|
|
8129
8129
|
): this {
|
|
@@ -8803,8 +8803,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8803
8803
|
// If our pointer moved only because we're following some other user, then don't
|
|
8804
8804
|
// update our last activity timestamp; otherwise, update it to the current timestamp.
|
|
8805
8805
|
info.type === 'pointer' && info.pointerId === INTERNAL_POINTER_IDS.CAMERA_MOVE
|
|
8806
|
-
? this.store.unsafeGetWithoutCapture(TLPOINTER_ID)?.lastActivityTimestamp ??
|
|
8807
|
-
this._tickManager.now
|
|
8806
|
+
? (this.store.unsafeGetWithoutCapture(TLPOINTER_ID)?.lastActivityTimestamp ??
|
|
8807
|
+
this._tickManager.now)
|
|
8808
8808
|
: this._tickManager.now,
|
|
8809
8809
|
meta: {},
|
|
8810
8810
|
},
|
|
@@ -9369,6 +9369,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9369
9369
|
// todo: replace with new readonly mode?
|
|
9370
9370
|
if (this.getCrashingError()) return this
|
|
9371
9371
|
|
|
9372
|
+
this.emit('before-event', info)
|
|
9373
|
+
|
|
9372
9374
|
const { inputs } = this
|
|
9373
9375
|
const { type } = info
|
|
9374
9376
|
|
|
@@ -390,8 +390,8 @@ export class BoundsSnaps {
|
|
|
390
390
|
|
|
391
391
|
// at the same time, calculate how far we need to nudge the shape to 'snap' to the target point(s)
|
|
392
392
|
const nudge = new Vec(
|
|
393
|
-
lockedAxis === 'x' ? 0 : nearestSnapsX[0]?.nudge ?? 0,
|
|
394
|
-
lockedAxis === 'y' ? 0 : nearestSnapsY[0]?.nudge ?? 0
|
|
393
|
+
lockedAxis === 'x' ? 0 : (nearestSnapsX[0]?.nudge ?? 0),
|
|
394
|
+
lockedAxis === 'y' ? 0 : (nearestSnapsY[0]?.nudge ?? 0)
|
|
395
395
|
)
|
|
396
396
|
|
|
397
397
|
// ok we've figured out how much the box should be nudged, now let's find all the snap points
|
|
@@ -504,8 +504,8 @@ export class BoundsSnaps {
|
|
|
504
504
|
|
|
505
505
|
// at the same time, calculate how far we need to nudge the shape to 'snap' to the target point(s)
|
|
506
506
|
const nudge = new Vec(
|
|
507
|
-
isXLocked ? 0 : nearestSnapsX[0]?.nudge ?? 0,
|
|
508
|
-
isYLocked ? 0 : nearestSnapsY[0]?.nudge ?? 0
|
|
507
|
+
isXLocked ? 0 : (nearestSnapsX[0]?.nudge ?? 0),
|
|
508
|
+
isYLocked ? 0 : (nearestSnapsY[0]?.nudge ?? 0)
|
|
509
509
|
)
|
|
510
510
|
|
|
511
511
|
if (isAspectRatioLocked && isSelectionCorner(handle) && nudge.len() !== 0) {
|
|
@@ -53,8 +53,27 @@ export interface TLShapeUtilCanvasSvgDef {
|
|
|
53
53
|
|
|
54
54
|
/** @public */
|
|
55
55
|
export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
56
|
+
/** Configure this shape utils {@link ShapeUtil.options | `options`}. */
|
|
57
|
+
static configure<T extends TLShapeUtilConstructor<any, any>>(
|
|
58
|
+
this: T,
|
|
59
|
+
options: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never
|
|
60
|
+
): T {
|
|
61
|
+
// @ts-expect-error -- typescript has no idea what's going on here but it's fine
|
|
62
|
+
return class extends this {
|
|
63
|
+
// @ts-expect-error
|
|
64
|
+
options = { ...this.options, ...options }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
56
68
|
constructor(public editor: Editor) {}
|
|
57
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Options for this shape util. If you're implementing a custom shape util, you can override
|
|
72
|
+
* this to provide customization options for your shape. If using an existing shape util, you
|
|
73
|
+
* can customizing this by calling {@link ShapeUtil.configure}.
|
|
74
|
+
*/
|
|
75
|
+
options = {}
|
|
76
|
+
|
|
58
77
|
/**
|
|
59
78
|
* Props allow you to define the shape's properties in a way that the editor can understand.
|
|
60
79
|
* This has two main uses:
|