@tldraw/editor 3.8.0-canary.da7502dc0e6b → 3.8.0-canary.dcbfd1934840
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 +145 -50
- package/dist-cjs/index.js +11 -8
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/config/TLSessionStateSnapshot.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +27 -16
- 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/managers/TextManager.js +1 -0
- package/dist-cjs/lib/editor/managers/TextManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/shared/resizeScaled.js +66 -0
- package/dist-cjs/lib/editor/shapes/shared/resizeScaled.js.map +7 -0
- 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 +2 -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 +145 -50
- package/dist-esm/index.mjs +5 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/config/TLSessionStateSnapshot.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +27 -16
- 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/managers/TextManager.mjs +1 -0
- package/dist-esm/lib/editor/managers/TextManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/shared/resizeScaled.mjs +46 -0
- package/dist-esm/lib/editor/shapes/shared/resizeScaled.mjs.map +7 -0
- 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 +2 -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/editor.css +2 -1
- package/package.json +20 -20
- package/src/index.ts +16 -1
- package/src/lib/components/default-components/DefaultCanvas.tsx +1 -1
- package/src/lib/config/TLSessionStateSnapshot.ts +3 -1
- package/src/lib/editor/Editor.ts +59 -32
- package/src/lib/editor/managers/SnapManager/BoundsSnaps.ts +4 -4
- package/src/lib/editor/managers/TextManager.ts +1 -0
- package/src/lib/editor/shapes/ShapeUtil.ts +30 -1
- package/src/lib/editor/shapes/shared/resizeScaled.ts +61 -0
- package/src/lib/editor/types/emit-types.ts +1 -0
- package/src/lib/editor/types/external-content.ts +90 -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 +5 -2
- 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/exports/StyleEmbedder.ts"],
|
|
4
|
-
"sourcesContent": ["import { assertExists, objectMapValues, uniqueId } from '@tldraw/utils'\nimport { FontEmbedder } from './FontEmbedder'\nimport {\n\telementStyle,\n\tgetComputedStyle,\n\tgetRenderedChildNodes,\n\tgetRenderedChildren,\n} from './domUtils'\nimport { resourceToDataUrl } from './fetchCache'\nimport { isPropertyInherited, parseCssValueUrls, shouldIncludeCssProperty } from './parseCss'\n\ntype Styles = { [K in string]?: string }\ntype ReadonlyStyles = { readonly [K in string]?: string }\nconst NO_STYLES = {} as const\n\ninterface ElementStyleInfo {\n\tself: Styles\n\tbefore: Styles | undefined\n\tafter: Styles | undefined\n}\n\nexport class StyleEmbedder {\n\tconstructor(private readonly root: Element) {}\n\tprivate readonly styles = new Map<Element, ElementStyleInfo>()\n\treadonly fonts = new FontEmbedder()\n\n\treadRootElementStyles(rootElement: Element) {\n\t\t// when reading a root, we always apply _all_ the styles, even if they match the defaults\n\t\tthis.readElementStyles(rootElement, {\n\t\t\tshouldRespectDefaults: false,\n\t\t\tshouldSkipInheritedParentStyles: false,\n\t\t})\n\n\t\tconst children = Array.from(getRenderedChildren(rootElement))\n\t\twhile (children.length) {\n\t\t\tconst child = children.pop()!\n\t\t\tchildren.push(...getRenderedChildren(child))\n\n\t\t\t// when reading children, we don't apply styles that match the defaults for that\n\t\t\t// element, or that would be inherited from the parent\n\t\t\tthis.readElementStyles(child, {\n\t\t\t\tshouldRespectDefaults: true,\n\t\t\t\tshouldSkipInheritedParentStyles: true,\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate readElementStyles(\n\t\telement: Element,\n\t\t{ shouldRespectDefaults = true, shouldSkipInheritedParentStyles = true }\n\t) {\n\t\tconst defaultStyles = shouldRespectDefaults\n\t\t\t? getDefaultStylesForTagName(element.tagName.toLowerCase())\n\t\t\t: NO_STYLES\n\n\t\tconst parentStyles = shouldSkipInheritedParentStyles\n\t\t\t? this.styles.get(element.parentElement as Element)?.self ?? NO_STYLES\n\t\t\t: NO_STYLES\n\n\t\tconst info: ElementStyleInfo = {\n\t\t\tself: styleFromElement(element, { defaultStyles, parentStyles }),\n\t\t\tbefore: styleFromPseudoElement(element, '::before'),\n\t\t\tafter: styleFromPseudoElement(element, '::after'),\n\t\t}\n\t\tthis.styles.set(element, info)\n\t}\n\n\tfetchResources() {\n\t\tconst promises: Promise<void>[] = []\n\n\t\tfor (const info of this.styles.values()) {\n\t\t\tfor (const styles of objectMapValues(info)) {\n\t\t\t\tif (!styles) continue\n\t\t\t\tfor (const [property, value] of Object.entries(styles)) {\n\t\t\t\t\tif (!value) continue\n\t\t\t\t\tif (property === 'font-family') {\n\t\t\t\t\t\tthis.fonts.onFontFamilyValue(value)\n\t\t\t\t\t}\n\n\t\t\t\t\tconst urlMatches = parseCssValueUrls(value)\n\t\t\t\t\tif (urlMatches.length === 0) continue\n\n\t\t\t\t\tpromises.push(\n\t\t\t\t\t\t...urlMatches.map(async ({ url, original }) => {\n\t\t\t\t\t\t\tconst dataUrl = (await resourceToDataUrl(url)) ?? 'data:'\n\t\t\t\t\t\t\tstyles[property] = value.replace(original, `url(\"${dataUrl}\")`)\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn Promise.all(promises)\n\t}\n\n\t// custom elements are tricky. if we serialize the dom as-is, the custom elements wont have\n\t// their shadow-dom contents serialized. after we've read all the styles, we need to unwrap the\n\t// contents of each custom elements shadow dom directly into the parent element itself.\n\tunwrapCustomElements() {\n\t\tconst visited = new Set<Node>()\n\n\t\tconst visit = (element: Element, clonedParent: Element | null) => {\n\t\t\tif (visited.has(element)) return\n\t\t\tvisited.add(element)\n\n\t\t\tconst shadowRoot = element.shadowRoot\n\n\t\t\tif (shadowRoot) {\n\t\t\t\tconst clonedCustomEl = document.createElement('div')\n\t\t\t\tthis.styles.set(clonedCustomEl, this.styles.get(element)!)\n\n\t\t\t\tclonedCustomEl.setAttribute('data-tl-custom-element', element.tagName)\n\t\t\t\t;(clonedParent ?? element.parentElement!).appendChild(clonedCustomEl)\n\n\t\t\t\tfor (const child of shadowRoot.childNodes) {\n\t\t\t\t\tif (child instanceof Element) {\n\t\t\t\t\t\tvisit(child, clonedCustomEl)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclonedCustomEl.appendChild(child.cloneNode(true))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\telement.remove()\n\t\t\t} else if (clonedParent) {\n\t\t\t\tif (element.tagName.toLowerCase() === 'style') {\n\t\t\t\t\t// we don't clone style tags at that would break the style scoping. instead we\n\t\t\t\t\t// rely on the computed styles we've already read\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst clonedEl = element.cloneNode(false) as Element\n\t\t\t\tthis.styles.set(clonedEl, this.styles.get(element)!)\n\n\t\t\t\tclonedParent.appendChild(clonedEl)\n\n\t\t\t\tfor (const child of getRenderedChildNodes(element)) {\n\t\t\t\t\tif (child instanceof Element) {\n\t\t\t\t\t\tvisit(child, clonedEl)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclonedEl.appendChild(child.cloneNode(true))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const element of this.styles.keys()) {\n\t\t\tvisit(element, null)\n\t\t}\n\t}\n\n\tembedStyles(): string {\n\t\tlet css = ''\n\n\t\tfor (const [element, info] of this.styles) {\n\t\t\tif (info.after || info.before) {\n\t\t\t\tconst className = `pseudo-${uniqueId()}`\n\t\t\t\telement.classList.add(className)\n\n\t\t\t\tif (info.before) {\n\t\t\t\t\tcss += `.${className}::before {${formatCss(info.before)}}\\n`\n\t\t\t\t}\n\t\t\t\tif (info.after) {\n\t\t\t\t\tcss += `.${className}::after {${formatCss(info.after)}}\\n`\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst style = elementStyle(element)\n\t\t\tfor (const [property, value] of Object.entries(info.self)) {\n\t\t\t\tif (!value) continue\n\t\t\t\tstyle.setProperty(property, value)\n\t\t\t}\n\n\t\t\t// in HTML, font-kerning: auto is equivalent to font-kerning: normal. But in SVG, it's\n\t\t\t// none. We set it to normal here to match the HTML behavior, as otherwise this can\n\t\t\t// cause rendering differences.\n\t\t\tif (style.fontKerning === 'auto') {\n\t\t\t\tstyle.fontKerning = 'normal'\n\t\t\t}\n\t\t}\n\n\t\treturn css\n\t}\n\n\tasync getFontFaceCss() {\n\t\treturn await this.fonts.createCss()\n\t}\n\n\tdispose() {\n\t\tdestroyDefaultStyleFrame()\n\t}\n}\n\ninterface ReadStyleOpts {\n\tdefaultStyles: ReadonlyStyles\n\tparentStyles: ReadonlyStyles\n}\n\nfunction styleFromElement(element: Element, { defaultStyles, parentStyles }: ReadStyleOpts) {\n\t// `computedStyleMap` produces a more accurate representation of the styles, but it's not\n\t// supported in firefox at the time of writing. So we fall back to `getComputedStyle` if it's\n\t// not available.\n\tif (element.computedStyleMap) {\n\t\treturn styleFromComputedStyleMap(element.computedStyleMap(), { defaultStyles, parentStyles })\n\t}\n\treturn styleFromComputedStyle(getComputedStyle(element), { defaultStyles, parentStyles })\n}\n\nfunction styleFromPseudoElement(element: Element, pseudo: string) {\n\t// the equivalent of `computedStyleMap` for pseudo-elements isn't even fully specced out yet, so\n\t// for those we have to use `getComputedStyle` in all browsers.\n\tconst style = getComputedStyle(element, pseudo)\n\n\tconst content = style.getPropertyValue('content')\n\tif (content === '' || content === 'none') {\n\t\treturn undefined\n\t}\n\n\treturn styleFromComputedStyle(style, { defaultStyles: NO_STYLES, parentStyles: NO_STYLES })\n}\n\nfunction styleFromComputedStyleMap(\n\tstyle: StylePropertyMapReadOnly,\n\t{ defaultStyles, parentStyles }: ReadStyleOpts\n) {\n\tconst styles: Record<string, string> = {}\n\tfor (const property of style.keys()) {\n\t\tif (!shouldIncludeCssProperty(property)) continue\n\n\t\tconst value = style.get(property)!.toString()\n\n\t\tif (defaultStyles[property] === value) continue\n\t\tif (parentStyles[property] === value && isPropertyInherited(property)) continue\n\n\t\tstyles[property] = value\n\t}\n\n\treturn styles\n}\n\nfunction styleFromComputedStyle(\n\tstyle: CSSStyleDeclaration,\n\t{ defaultStyles, parentStyles }: ReadStyleOpts\n) {\n\tconst styles: Record<string, string> = {}\n\tfor (const property of style) {\n\t\tif (!shouldIncludeCssProperty(property)) continue\n\n\t\tconst value = style.getPropertyValue(property)\n\n\t\tif (defaultStyles[property] === value) continue\n\t\tif (parentStyles[property] === value && isPropertyInherited(property)) continue\n\n\t\tstyles[property] = value\n\t}\n\treturn styles\n}\n\nfunction formatCss(style: ReadonlyStyles) {\n\tlet cssText = ''\n\tfor (const [property, value] of Object.entries(style)) {\n\t\tcssText += `${property}: ${value};`\n\t}\n\treturn cssText\n}\n\n// when we're figuring out the default values for a tag, we need read them from a separate document\n// so they're not affected by the current document's styles\nlet defaultStyleFrame:\n\t| { iframe: HTMLIFrameElement; foreignObject: SVGForeignObjectElement; document: Document }\n\t| undefined\nconst defaultStylesByTagName: Record<string, ReadonlyStyles> = {}\nfunction getDefaultStyleFrame() {\n\tif (!defaultStyleFrame) {\n\t\tconst frame = document.createElement('iframe')\n\t\tframe.style.display = 'none'\n\t\tdocument.body.appendChild(frame)\n\t\tconst frameDocument = assertExists(frame.contentDocument, 'frame must have a document')\n\t\tconst svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n\t\tconst foreignObject = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject')\n\t\tsvg.appendChild(foreignObject)\n\t\tframeDocument.body.appendChild(svg)\n\t\tdefaultStyleFrame = { iframe: frame, foreignObject, document: frameDocument }\n\t}\n\treturn defaultStyleFrame\n}\n\nfunction destroyDefaultStyleFrame() {\n\tif (defaultStyleFrame) {\n\t\tdocument.body.removeChild(defaultStyleFrame.iframe)\n\t\tdefaultStyleFrame = undefined\n\t}\n}\n\nconst defaultStyleReadOptions: ReadStyleOpts = { defaultStyles: NO_STYLES, parentStyles: NO_STYLES }\nfunction getDefaultStylesForTagName(tagName: string) {\n\tlet existing = defaultStylesByTagName[tagName]\n\tif (!existing) {\n\t\tconst { foreignObject, document } = getDefaultStyleFrame()\n\t\tconst element = document.createElement(tagName)\n\t\tforeignObject.appendChild(element)\n\t\texisting = element.computedStyleMap\n\t\t\t? styleFromComputedStyleMap(element.computedStyleMap(), defaultStyleReadOptions)\n\t\t\t: styleFromComputedStyle(getComputedStyle(element), defaultStyleReadOptions)\n\t\tforeignObject.removeChild(element)\n\t\tdefaultStylesByTagName[tagName] = existing\n\t}\n\treturn existing\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,cAAc,iBAAiB,gBAAgB;AACxD,SAAS,oBAAoB;AAC7B;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,yBAAyB;AAClC,SAAS,qBAAqB,mBAAmB,gCAAgC;AAIjF,MAAM,YAAY,CAAC;AAQZ,MAAM,cAAc;AAAA,EAC1B,YAA6B,MAAe;AAAf;AAAA,EAAgB;AAAA,EAC5B,SAAS,oBAAI,IAA+B;AAAA,EACpD,QAAQ,IAAI,aAAa;AAAA,EAElC,sBAAsB,aAAsB;AAE3C,SAAK,kBAAkB,aAAa;AAAA,MACnC,uBAAuB;AAAA,MACvB,iCAAiC;AAAA,IAClC,CAAC;AAED,UAAM,WAAW,MAAM,KAAK,oBAAoB,WAAW,CAAC;AAC5D,WAAO,SAAS,QAAQ;AACvB,YAAM,QAAQ,SAAS,IAAI;AAC3B,eAAS,KAAK,GAAG,oBAAoB,KAAK,CAAC;AAI3C,WAAK,kBAAkB,OAAO;AAAA,QAC7B,uBAAuB;AAAA,QACvB,iCAAiC;AAAA,MAClC,CAAC;AAAA,IACF;AAAA,EACD;AAAA,EAEQ,kBACP,SACA,EAAE,wBAAwB,MAAM,kCAAkC,KAAK,GACtE;AACD,UAAM,gBAAgB,wBACnB,2BAA2B,QAAQ,QAAQ,YAAY,CAAC,IACxD;AAEH,UAAM,eAAe,
|
|
4
|
+
"sourcesContent": ["import { assertExists, objectMapValues, uniqueId } from '@tldraw/utils'\nimport { FontEmbedder } from './FontEmbedder'\nimport {\n\telementStyle,\n\tgetComputedStyle,\n\tgetRenderedChildNodes,\n\tgetRenderedChildren,\n} from './domUtils'\nimport { resourceToDataUrl } from './fetchCache'\nimport { isPropertyInherited, parseCssValueUrls, shouldIncludeCssProperty } from './parseCss'\n\ntype Styles = { [K in string]?: string }\ntype ReadonlyStyles = { readonly [K in string]?: string }\nconst NO_STYLES = {} as const\n\ninterface ElementStyleInfo {\n\tself: Styles\n\tbefore: Styles | undefined\n\tafter: Styles | undefined\n}\n\nexport class StyleEmbedder {\n\tconstructor(private readonly root: Element) {}\n\tprivate readonly styles = new Map<Element, ElementStyleInfo>()\n\treadonly fonts = new FontEmbedder()\n\n\treadRootElementStyles(rootElement: Element) {\n\t\t// when reading a root, we always apply _all_ the styles, even if they match the defaults\n\t\tthis.readElementStyles(rootElement, {\n\t\t\tshouldRespectDefaults: false,\n\t\t\tshouldSkipInheritedParentStyles: false,\n\t\t})\n\n\t\tconst children = Array.from(getRenderedChildren(rootElement))\n\t\twhile (children.length) {\n\t\t\tconst child = children.pop()!\n\t\t\tchildren.push(...getRenderedChildren(child))\n\n\t\t\t// when reading children, we don't apply styles that match the defaults for that\n\t\t\t// element, or that would be inherited from the parent\n\t\t\tthis.readElementStyles(child, {\n\t\t\t\tshouldRespectDefaults: true,\n\t\t\t\tshouldSkipInheritedParentStyles: true,\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate readElementStyles(\n\t\telement: Element,\n\t\t{ shouldRespectDefaults = true, shouldSkipInheritedParentStyles = true }\n\t) {\n\t\tconst defaultStyles = shouldRespectDefaults\n\t\t\t? getDefaultStylesForTagName(element.tagName.toLowerCase())\n\t\t\t: NO_STYLES\n\n\t\tconst parentStyles = shouldSkipInheritedParentStyles\n\t\t\t? (this.styles.get(element.parentElement as Element)?.self ?? NO_STYLES)\n\t\t\t: NO_STYLES\n\n\t\tconst info: ElementStyleInfo = {\n\t\t\tself: styleFromElement(element, { defaultStyles, parentStyles }),\n\t\t\tbefore: styleFromPseudoElement(element, '::before'),\n\t\t\tafter: styleFromPseudoElement(element, '::after'),\n\t\t}\n\t\tthis.styles.set(element, info)\n\t}\n\n\tfetchResources() {\n\t\tconst promises: Promise<void>[] = []\n\n\t\tfor (const info of this.styles.values()) {\n\t\t\tfor (const styles of objectMapValues(info)) {\n\t\t\t\tif (!styles) continue\n\t\t\t\tfor (const [property, value] of Object.entries(styles)) {\n\t\t\t\t\tif (!value) continue\n\t\t\t\t\tif (property === 'font-family') {\n\t\t\t\t\t\tthis.fonts.onFontFamilyValue(value)\n\t\t\t\t\t}\n\n\t\t\t\t\tconst urlMatches = parseCssValueUrls(value)\n\t\t\t\t\tif (urlMatches.length === 0) continue\n\n\t\t\t\t\tpromises.push(\n\t\t\t\t\t\t...urlMatches.map(async ({ url, original }) => {\n\t\t\t\t\t\t\tconst dataUrl = (await resourceToDataUrl(url)) ?? 'data:'\n\t\t\t\t\t\t\tstyles[property] = value.replace(original, `url(\"${dataUrl}\")`)\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn Promise.all(promises)\n\t}\n\n\t// custom elements are tricky. if we serialize the dom as-is, the custom elements wont have\n\t// their shadow-dom contents serialized. after we've read all the styles, we need to unwrap the\n\t// contents of each custom elements shadow dom directly into the parent element itself.\n\tunwrapCustomElements() {\n\t\tconst visited = new Set<Node>()\n\n\t\tconst visit = (element: Element, clonedParent: Element | null) => {\n\t\t\tif (visited.has(element)) return\n\t\t\tvisited.add(element)\n\n\t\t\tconst shadowRoot = element.shadowRoot\n\n\t\t\tif (shadowRoot) {\n\t\t\t\tconst clonedCustomEl = document.createElement('div')\n\t\t\t\tthis.styles.set(clonedCustomEl, this.styles.get(element)!)\n\n\t\t\t\tclonedCustomEl.setAttribute('data-tl-custom-element', element.tagName)\n\t\t\t\t;(clonedParent ?? element.parentElement!).appendChild(clonedCustomEl)\n\n\t\t\t\tfor (const child of shadowRoot.childNodes) {\n\t\t\t\t\tif (child instanceof Element) {\n\t\t\t\t\t\tvisit(child, clonedCustomEl)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclonedCustomEl.appendChild(child.cloneNode(true))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\telement.remove()\n\t\t\t} else if (clonedParent) {\n\t\t\t\tif (element.tagName.toLowerCase() === 'style') {\n\t\t\t\t\t// we don't clone style tags at that would break the style scoping. instead we\n\t\t\t\t\t// rely on the computed styles we've already read\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst clonedEl = element.cloneNode(false) as Element\n\t\t\t\tthis.styles.set(clonedEl, this.styles.get(element)!)\n\n\t\t\t\tclonedParent.appendChild(clonedEl)\n\n\t\t\t\tfor (const child of getRenderedChildNodes(element)) {\n\t\t\t\t\tif (child instanceof Element) {\n\t\t\t\t\t\tvisit(child, clonedEl)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclonedEl.appendChild(child.cloneNode(true))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const element of this.styles.keys()) {\n\t\t\tvisit(element, null)\n\t\t}\n\t}\n\n\tembedStyles(): string {\n\t\tlet css = ''\n\n\t\tfor (const [element, info] of this.styles) {\n\t\t\tif (info.after || info.before) {\n\t\t\t\tconst className = `pseudo-${uniqueId()}`\n\t\t\t\telement.classList.add(className)\n\n\t\t\t\tif (info.before) {\n\t\t\t\t\tcss += `.${className}::before {${formatCss(info.before)}}\\n`\n\t\t\t\t}\n\t\t\t\tif (info.after) {\n\t\t\t\t\tcss += `.${className}::after {${formatCss(info.after)}}\\n`\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst style = elementStyle(element)\n\t\t\tfor (const [property, value] of Object.entries(info.self)) {\n\t\t\t\tif (!value) continue\n\t\t\t\tstyle.setProperty(property, value)\n\t\t\t}\n\n\t\t\t// in HTML, font-kerning: auto is equivalent to font-kerning: normal. But in SVG, it's\n\t\t\t// none. We set it to normal here to match the HTML behavior, as otherwise this can\n\t\t\t// cause rendering differences.\n\t\t\tif (style.fontKerning === 'auto') {\n\t\t\t\tstyle.fontKerning = 'normal'\n\t\t\t}\n\t\t}\n\n\t\treturn css\n\t}\n\n\tasync getFontFaceCss() {\n\t\treturn await this.fonts.createCss()\n\t}\n\n\tdispose() {\n\t\tdestroyDefaultStyleFrame()\n\t}\n}\n\ninterface ReadStyleOpts {\n\tdefaultStyles: ReadonlyStyles\n\tparentStyles: ReadonlyStyles\n}\n\nfunction styleFromElement(element: Element, { defaultStyles, parentStyles }: ReadStyleOpts) {\n\t// `computedStyleMap` produces a more accurate representation of the styles, but it's not\n\t// supported in firefox at the time of writing. So we fall back to `getComputedStyle` if it's\n\t// not available.\n\tif (element.computedStyleMap) {\n\t\treturn styleFromComputedStyleMap(element.computedStyleMap(), { defaultStyles, parentStyles })\n\t}\n\treturn styleFromComputedStyle(getComputedStyle(element), { defaultStyles, parentStyles })\n}\n\nfunction styleFromPseudoElement(element: Element, pseudo: string) {\n\t// the equivalent of `computedStyleMap` for pseudo-elements isn't even fully specced out yet, so\n\t// for those we have to use `getComputedStyle` in all browsers.\n\tconst style = getComputedStyle(element, pseudo)\n\n\tconst content = style.getPropertyValue('content')\n\tif (content === '' || content === 'none') {\n\t\treturn undefined\n\t}\n\n\treturn styleFromComputedStyle(style, { defaultStyles: NO_STYLES, parentStyles: NO_STYLES })\n}\n\nfunction styleFromComputedStyleMap(\n\tstyle: StylePropertyMapReadOnly,\n\t{ defaultStyles, parentStyles }: ReadStyleOpts\n) {\n\tconst styles: Record<string, string> = {}\n\tfor (const property of style.keys()) {\n\t\tif (!shouldIncludeCssProperty(property)) continue\n\n\t\tconst value = style.get(property)!.toString()\n\n\t\tif (defaultStyles[property] === value) continue\n\t\tif (parentStyles[property] === value && isPropertyInherited(property)) continue\n\n\t\tstyles[property] = value\n\t}\n\n\treturn styles\n}\n\nfunction styleFromComputedStyle(\n\tstyle: CSSStyleDeclaration,\n\t{ defaultStyles, parentStyles }: ReadStyleOpts\n) {\n\tconst styles: Record<string, string> = {}\n\tfor (const property of style) {\n\t\tif (!shouldIncludeCssProperty(property)) continue\n\n\t\tconst value = style.getPropertyValue(property)\n\n\t\tif (defaultStyles[property] === value) continue\n\t\tif (parentStyles[property] === value && isPropertyInherited(property)) continue\n\n\t\tstyles[property] = value\n\t}\n\treturn styles\n}\n\nfunction formatCss(style: ReadonlyStyles) {\n\tlet cssText = ''\n\tfor (const [property, value] of Object.entries(style)) {\n\t\tcssText += `${property}: ${value};`\n\t}\n\treturn cssText\n}\n\n// when we're figuring out the default values for a tag, we need read them from a separate document\n// so they're not affected by the current document's styles\nlet defaultStyleFrame:\n\t| { iframe: HTMLIFrameElement; foreignObject: SVGForeignObjectElement; document: Document }\n\t| undefined\nconst defaultStylesByTagName: Record<string, ReadonlyStyles> = {}\nfunction getDefaultStyleFrame() {\n\tif (!defaultStyleFrame) {\n\t\tconst frame = document.createElement('iframe')\n\t\tframe.style.display = 'none'\n\t\tdocument.body.appendChild(frame)\n\t\tconst frameDocument = assertExists(frame.contentDocument, 'frame must have a document')\n\t\tconst svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n\t\tconst foreignObject = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject')\n\t\tsvg.appendChild(foreignObject)\n\t\tframeDocument.body.appendChild(svg)\n\t\tdefaultStyleFrame = { iframe: frame, foreignObject, document: frameDocument }\n\t}\n\treturn defaultStyleFrame\n}\n\nfunction destroyDefaultStyleFrame() {\n\tif (defaultStyleFrame) {\n\t\tdocument.body.removeChild(defaultStyleFrame.iframe)\n\t\tdefaultStyleFrame = undefined\n\t}\n}\n\nconst defaultStyleReadOptions: ReadStyleOpts = { defaultStyles: NO_STYLES, parentStyles: NO_STYLES }\nfunction getDefaultStylesForTagName(tagName: string) {\n\tlet existing = defaultStylesByTagName[tagName]\n\tif (!existing) {\n\t\tconst { foreignObject, document } = getDefaultStyleFrame()\n\t\tconst element = document.createElement(tagName)\n\t\tforeignObject.appendChild(element)\n\t\texisting = element.computedStyleMap\n\t\t\t? styleFromComputedStyleMap(element.computedStyleMap(), defaultStyleReadOptions)\n\t\t\t: styleFromComputedStyle(getComputedStyle(element), defaultStyleReadOptions)\n\t\tforeignObject.removeChild(element)\n\t\tdefaultStylesByTagName[tagName] = existing\n\t}\n\treturn existing\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["document"]
|
|
7
7
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useValue } from "@tldraw/state-react";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
|
-
import { preventDefault, stopEventPropagation } from "../utils/dom.mjs";
|
|
3
|
+
import { activeElementShouldCaptureKeys, preventDefault, stopEventPropagation } from "../utils/dom.mjs";
|
|
4
4
|
import { isAccelKey } from "../utils/keyboard.mjs";
|
|
5
5
|
import { useContainer } from "./useContainer.mjs";
|
|
6
6
|
import { useEditor } from "./useEditor.mjs";
|
|
@@ -178,10 +178,8 @@ function useDocumentEvents() {
|
|
|
178
178
|
};
|
|
179
179
|
}, [editor, container, isAppFocused]);
|
|
180
180
|
}
|
|
181
|
-
const INPUTS = ["input", "select", "button", "textarea"];
|
|
182
181
|
function areShortcutsDisabled(editor) {
|
|
183
|
-
|
|
184
|
-
return editor.menus.hasOpenMenus() || activeElement && (activeElement.getAttribute("contenteditable") || INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1);
|
|
182
|
+
return editor.menus.hasOpenMenus() || activeElementShouldCaptureKeys();
|
|
185
183
|
}
|
|
186
184
|
export {
|
|
187
185
|
useDocumentEvents
|
|
@@ -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 },
|
|
@@ -42,7 +41,8 @@ const defaultTldrawOptions = {
|
|
|
42
41
|
temporaryAssetPreviewLifetimeMs: 18e4,
|
|
43
42
|
actionShortcutsLocation: "swap",
|
|
44
43
|
createTextOnCanvasDoubleClick: true,
|
|
45
|
-
exportProvider: Fragment
|
|
44
|
+
exportProvider: Fragment,
|
|
45
|
+
enableToolbarKeyboardShortcuts: true
|
|
46
46
|
};
|
|
47
47
|
export {
|
|
48
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.dcbfd1934840";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2024-09-13T14:36:29.063Z",
|
|
4
|
-
minor: "2025-
|
|
5
|
-
patch: "2025-
|
|
4
|
+
minor: "2025-02-10T15:48:25.188Z",
|
|
5
|
+
patch: "2025-02-10T15:48:25.188Z"
|
|
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.dcbfd1934840'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-02-10T15:48:25.188Z',\n\tpatch: '2025-02-10T15:48:25.188Z',\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/editor.css
CHANGED
|
@@ -39,9 +39,10 @@
|
|
|
39
39
|
--layer-overlays-user-brush: 50;
|
|
40
40
|
--layer-overlays-user-indicator-selected: 60;
|
|
41
41
|
--layer-overlays-user-indicator-hovered: 70;
|
|
42
|
-
--layer-overlays-user-handles: 80;
|
|
43
42
|
--layer-overlays-user-snapline: 90;
|
|
44
43
|
--layer-overlays-selection-fg: 100;
|
|
44
|
+
/* User handles need to be above selection edges / corners, matters for sticky note clone handles */
|
|
45
|
+
--layer-overlays-user-handles: 105;
|
|
45
46
|
--layer-overlays-user-indicator-hint: 110;
|
|
46
47
|
--layer-overlays-collaborator-cursor-hint: 120;
|
|
47
48
|
--layer-overlays-collaborator-cursor: 130;
|
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.dcbfd1934840",
|
|
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.dcbfd1934840",
|
|
49
|
+
"@tldraw/state-react": "3.8.0-canary.dcbfd1934840",
|
|
50
|
+
"@tldraw/store": "3.8.0-canary.dcbfd1934840",
|
|
51
|
+
"@tldraw/tlschema": "3.8.0-canary.dcbfd1934840",
|
|
52
|
+
"@tldraw/utils": "3.8.0-canary.dcbfd1934840",
|
|
53
|
+
"@tldraw/validate": "3.8.0-canary.dcbfd1934840",
|
|
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
|
@@ -25,6 +25,7 @@ export {
|
|
|
25
25
|
useStateTracking,
|
|
26
26
|
useValue,
|
|
27
27
|
} from '@tldraw/state-react'
|
|
28
|
+
export { resizeScaled } from './lib/editor/shapes/shared/resizeScaled'
|
|
28
29
|
export { LocalIndexedDb, Table, type StoreName } from './lib/utils/sync/LocalIndexedDb'
|
|
29
30
|
// eslint-disable-next-line local/no-export-star
|
|
30
31
|
export * from '@tldraw/store'
|
|
@@ -182,6 +183,7 @@ export { UserPreferencesManager } from './lib/editor/managers/UserPreferencesMan
|
|
|
182
183
|
export { BaseBoxShapeUtil, type TLBaseBoxShape } from './lib/editor/shapes/BaseBoxShapeUtil'
|
|
183
184
|
export {
|
|
184
185
|
ShapeUtil,
|
|
186
|
+
type TLCropInfo,
|
|
185
187
|
type TLHandleDragInfo,
|
|
186
188
|
type TLResizeInfo,
|
|
187
189
|
type TLResizeMode,
|
|
@@ -238,9 +240,21 @@ export {
|
|
|
238
240
|
type UiEventType,
|
|
239
241
|
} from './lib/editor/types/event-types'
|
|
240
242
|
export {
|
|
241
|
-
type
|
|
243
|
+
type TLBaseExternalContent,
|
|
244
|
+
type TLEmbedExternalContent,
|
|
245
|
+
type TLErrorExternalContentSource,
|
|
246
|
+
type TLExcalidrawExternalContentSource,
|
|
247
|
+
type TLExternalAsset,
|
|
242
248
|
type TLExternalContent,
|
|
243
249
|
type TLExternalContentSource,
|
|
250
|
+
type TLFileExternalAsset,
|
|
251
|
+
type TLFilesExternalContent,
|
|
252
|
+
type TLSvgTextExternalContent,
|
|
253
|
+
type TLTextExternalContent,
|
|
254
|
+
type TLTextExternalContentSource,
|
|
255
|
+
type TLTldrawExternalContentSource,
|
|
256
|
+
type TLUrlExternalAsset,
|
|
257
|
+
type TLUrlExternalContent,
|
|
244
258
|
} from './lib/editor/types/external-content'
|
|
245
259
|
export {
|
|
246
260
|
type TLHistoryBatchOptions,
|
|
@@ -399,6 +413,7 @@ export {
|
|
|
399
413
|
type TLDeepLinkOptions,
|
|
400
414
|
} from './lib/utils/deepLinks'
|
|
401
415
|
export {
|
|
416
|
+
activeElementShouldCaptureKeys,
|
|
402
417
|
loopToHtmlElement,
|
|
403
418
|
preventDefault,
|
|
404
419
|
releasePointerCapture,
|
|
@@ -160,7 +160,6 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
|
|
160
160
|
<div className="tl-overlays">
|
|
161
161
|
<div ref={rHtmlLayer2} className="tl-html-layer">
|
|
162
162
|
{debugGeometry ? <GeometryDebuggingView /> : null}
|
|
163
|
-
<HandlesWrapper />
|
|
164
163
|
<BrushWrapper />
|
|
165
164
|
<ScribbleWrapper />
|
|
166
165
|
<ZoomBrushWrapper />
|
|
@@ -168,6 +167,7 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
|
|
168
167
|
<HintedShapeIndicator />
|
|
169
168
|
<SnapIndicatorWrapper />
|
|
170
169
|
<SelectionForegroundWrapper />
|
|
170
|
+
<HandlesWrapper />
|
|
171
171
|
<LiveCollaborators />
|
|
172
172
|
</div>
|
|
173
173
|
</div>
|
|
@@ -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
|