@tldraw/editor 3.12.0-canary.5654926a0c2e → 3.12.0-canary.5fcdc0f29d81
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 +3 -0
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/components/Shape.js +10 -14
- package/dist-cjs/lib/components/Shape.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +9 -2
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +1 -1
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FontManager.js +1 -1
- package/dist-cjs/lib/editor/managers/FontManager.js.map +2 -2
- package/dist-cjs/lib/editor/tools/StateNode.js +4 -1
- package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +12 -7
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +3 -3
- package/dist-cjs/lib/primitives/geometry/Group2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
- package/dist-cjs/lib/utils/debug-flags.js +2 -1
- package/dist-cjs/lib/utils/debug-flags.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 +3 -0
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/components/Shape.mjs +11 -15
- package/dist-esm/lib/components/Shape.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +9 -2
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +1 -1
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FontManager.mjs +1 -1
- package/dist-esm/lib/editor/managers/FontManager.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs +4 -1
- package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +12 -7
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +3 -3
- package/dist-esm/lib/primitives/geometry/Group2d.mjs +1 -1
- package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
- package/dist-esm/lib/utils/debug-flags.mjs +2 -1
- package/dist-esm/lib/utils/debug-flags.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +17 -11
- package/package.json +7 -7
- package/src/lib/components/Shape.tsx +15 -19
- package/src/lib/editor/Editor.ts +12 -3
- package/src/lib/editor/derivations/notVisibleShapes.ts +1 -1
- package/src/lib/editor/managers/FontManager.ts +1 -1
- package/src/lib/editor/tools/StateNode.ts +6 -1
- package/src/lib/hooks/useCanvasEvents.ts +14 -7
- package/src/lib/primitives/geometry/Group2d.ts +1 -1
- package/src/lib/utils/debug-flags.ts +1 -0
- package/src/version.ts +3 -3
|
@@ -16,7 +16,7 @@ const notVisibleShapes = (editor) => {
|
|
|
16
16
|
});
|
|
17
17
|
return notVisibleShapes2;
|
|
18
18
|
}
|
|
19
|
-
return computed("
|
|
19
|
+
return computed("notVisibleShapes", (prevValue) => {
|
|
20
20
|
if (isUninitialized(prevValue)) {
|
|
21
21
|
return fromScratch(editor);
|
|
22
22
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/editor/derivations/notVisibleShapes.ts"],
|
|
4
|
-
"sourcesContent": ["import { computed, isUninitialized } from '@tldraw/state'\nimport { TLShapeId } from '@tldraw/tlschema'\nimport { Box } from '../../primitives/Box'\nimport { Editor } from '../Editor'\n\nfunction isShapeNotVisible(editor: Editor, id: TLShapeId, viewportPageBounds: Box): boolean {\n\tconst maskedPageBounds = editor.getShapeMaskedPageBounds(id)\n\t// if the shape is fully outside of its parent's clipping bounds...\n\tif (maskedPageBounds === undefined) return true\n\n\t// if the shape is fully outside of the viewport page bounds...\n\treturn !viewportPageBounds.includes(maskedPageBounds)\n}\n\n/**\n * Incremental derivation of not visible shapes.\n * Non visible shapes are shapes outside of the viewport page bounds and shapes outside of parent's clipping bounds.\n *\n * @param editor - Instance of the tldraw Editor.\n * @returns Incremental derivation of non visible shapes.\n */\nexport const notVisibleShapes = (editor: Editor) => {\n\tfunction fromScratch(editor: Editor): Set<TLShapeId> {\n\t\tconst shapes = editor.getCurrentPageShapeIds()\n\t\tconst viewportPageBounds = editor.getViewportPageBounds()\n\t\tconst notVisibleShapes = new Set<TLShapeId>()\n\t\tshapes.forEach((id) => {\n\t\t\tif (isShapeNotVisible(editor, id, viewportPageBounds)) {\n\t\t\t\tnotVisibleShapes.add(id)\n\t\t\t}\n\t\t})\n\t\treturn notVisibleShapes\n\t}\n\treturn computed<Set<TLShapeId>>('
|
|
5
|
-
"mappings": "AAAA,SAAS,UAAU,uBAAuB;AAK1C,SAAS,kBAAkB,QAAgB,IAAe,oBAAkC;AAC3F,QAAM,mBAAmB,OAAO,yBAAyB,EAAE;AAE3D,MAAI,qBAAqB,OAAW,QAAO;AAG3C,SAAO,CAAC,mBAAmB,SAAS,gBAAgB;AACrD;AASO,MAAM,mBAAmB,CAAC,WAAmB;AACnD,WAAS,YAAYA,SAAgC;AACpD,UAAM,SAASA,QAAO,uBAAuB;AAC7C,UAAM,qBAAqBA,QAAO,sBAAsB;AACxD,UAAMC,oBAAmB,oBAAI,IAAe;AAC5C,WAAO,QAAQ,CAAC,OAAO;AACtB,UAAI,kBAAkBD,SAAQ,IAAI,kBAAkB,GAAG;AACtD,QAAAC,kBAAiB,IAAI,EAAE;AAAA,MACxB;AAAA,IACD,CAAC;AACD,WAAOA;AAAA,EACR;AACA,SAAO,SAAyB,
|
|
4
|
+
"sourcesContent": ["import { computed, isUninitialized } from '@tldraw/state'\nimport { TLShapeId } from '@tldraw/tlschema'\nimport { Box } from '../../primitives/Box'\nimport { Editor } from '../Editor'\n\nfunction isShapeNotVisible(editor: Editor, id: TLShapeId, viewportPageBounds: Box): boolean {\n\tconst maskedPageBounds = editor.getShapeMaskedPageBounds(id)\n\t// if the shape is fully outside of its parent's clipping bounds...\n\tif (maskedPageBounds === undefined) return true\n\n\t// if the shape is fully outside of the viewport page bounds...\n\treturn !viewportPageBounds.includes(maskedPageBounds)\n}\n\n/**\n * Incremental derivation of not visible shapes.\n * Non visible shapes are shapes outside of the viewport page bounds and shapes outside of parent's clipping bounds.\n *\n * @param editor - Instance of the tldraw Editor.\n * @returns Incremental derivation of non visible shapes.\n */\nexport const notVisibleShapes = (editor: Editor) => {\n\tfunction fromScratch(editor: Editor): Set<TLShapeId> {\n\t\tconst shapes = editor.getCurrentPageShapeIds()\n\t\tconst viewportPageBounds = editor.getViewportPageBounds()\n\t\tconst notVisibleShapes = new Set<TLShapeId>()\n\t\tshapes.forEach((id) => {\n\t\t\tif (isShapeNotVisible(editor, id, viewportPageBounds)) {\n\t\t\t\tnotVisibleShapes.add(id)\n\t\t\t}\n\t\t})\n\t\treturn notVisibleShapes\n\t}\n\treturn computed<Set<TLShapeId>>('notVisibleShapes', (prevValue) => {\n\t\tif (isUninitialized(prevValue)) {\n\t\t\treturn fromScratch(editor)\n\t\t}\n\n\t\tconst nextValue = fromScratch(editor)\n\n\t\tif (prevValue.size !== nextValue.size) return nextValue\n\t\tfor (const prev of prevValue) {\n\t\t\tif (!nextValue.has(prev)) {\n\t\t\t\treturn nextValue\n\t\t\t}\n\t\t}\n\t\treturn prevValue\n\t})\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,UAAU,uBAAuB;AAK1C,SAAS,kBAAkB,QAAgB,IAAe,oBAAkC;AAC3F,QAAM,mBAAmB,OAAO,yBAAyB,EAAE;AAE3D,MAAI,qBAAqB,OAAW,QAAO;AAG3C,SAAO,CAAC,mBAAmB,SAAS,gBAAgB;AACrD;AASO,MAAM,mBAAmB,CAAC,WAAmB;AACnD,WAAS,YAAYA,SAAgC;AACpD,UAAM,SAASA,QAAO,uBAAuB;AAC7C,UAAM,qBAAqBA,QAAO,sBAAsB;AACxD,UAAMC,oBAAmB,oBAAI,IAAe;AAC5C,WAAO,QAAQ,CAAC,OAAO;AACtB,UAAI,kBAAkBD,SAAQ,IAAI,kBAAkB,GAAG;AACtD,QAAAC,kBAAiB,IAAI,EAAE;AAAA,MACxB;AAAA,IACD,CAAC;AACD,WAAOA;AAAA,EACR;AACA,SAAO,SAAyB,oBAAoB,CAAC,cAAc;AAClE,QAAI,gBAAgB,SAAS,GAAG;AAC/B,aAAO,YAAY,MAAM;AAAA,IAC1B;AAEA,UAAM,YAAY,YAAY,MAAM;AAEpC,QAAI,UAAU,SAAS,UAAU,KAAM,QAAO;AAC9C,eAAW,QAAQ,WAAW;AAC7B,UAAI,CAAC,UAAU,IAAI,IAAI,GAAG;AACzB,eAAO;AAAA,MACR;AAAA,IACD;AACA,WAAO;AAAA,EACR,CAAC;AACF;",
|
|
6
6
|
"names": ["editor", "notVisibleShapes"]
|
|
7
7
|
}
|
|
@@ -17,7 +17,7 @@ class FontManager {
|
|
|
17
17
|
const shapeUtil = this.editor.getShapeUtil(shape);
|
|
18
18
|
return shapeUtil.getFontFaces(shape);
|
|
19
19
|
},
|
|
20
|
-
{ areResultsEqual: areArraysShallowEqual }
|
|
20
|
+
{ areResultsEqual: areArraysShallowEqual, areRecordsEqual: (a, b) => a.props === b.props }
|
|
21
21
|
);
|
|
22
22
|
this.shapeFontLoadStateCache = editor.store.createCache(
|
|
23
23
|
(id) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/editor/managers/FontManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { computed, EMPTY_ARRAY, transact } from '@tldraw/state'\nimport { AtomMap } from '@tldraw/store'\nimport { TLShape, TLShapeId } from '@tldraw/tlschema'\nimport {\n\tareArraysShallowEqual,\n\tcompact,\n\tFileHelpers,\n\tmapObjectMapValues,\n\tobjectMapEntries,\n} from '@tldraw/utils'\nimport { Editor } from '../Editor'\n\n/**\n * Represents the `src` property of a {@link TLFontFace}.\n * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src | `src`} for details of the properties here.\n * @public\n */\nexport interface TLFontFaceSource {\n\t/**\n\t * A URL from which to load the font. If the value here is a key in\n\t * {@link tldraw#TLEditorAssetUrls.fonts}, the value from there will be used instead.\n\t */\n\turl: string\n\tformat?: string\n\ttech?: string\n}\n\n/**\n * A font face that can be used in the editor. The properties of this are largely the same as the\n * ones in the\n * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face | css `@font-face` rule}.\n * @public\n */\nexport interface TLFontFace {\n\t/**\n\t * How this font can be referred to in CSS.\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-family | `font-family`}.\n\t */\n\treadonly family: string\n\t/**\n\t * The source of the font. This\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src | `src`}.\n\t */\n\treadonly src: TLFontFaceSource\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/ascent-override | `ascent-override`}.\n\t */\n\treadonly ascentOverride?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/descent-override | `descent-override`}.\n\t */\n\treadonly descentOverride?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-stretch | `font-stretch`}.\n\t */\n\treadonly stretch?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-style | `font-style`}.\n\t */\n\treadonly style?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-weight | `font-weight`}.\n\t */\n\treadonly weight?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-feature-settings | `font-feature-settings`}.\n\t */\n\treadonly featureSettings?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/line-gap-override | `line-gap-override`}.\n\t */\n\treadonly lineGapOverride?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range | `unicode-range`}.\n\t */\n\treadonly unicodeRange?: string\n}\n\ninterface FontState {\n\treadonly state: 'loading' | 'ready' | 'error'\n\treadonly instance: FontFace\n\treadonly loadingPromise: Promise<void>\n}\n\n/** @public */\nexport class FontManager {\n\tconstructor(\n\t\tprivate readonly editor: Editor,\n\t\tprivate readonly assetUrls?: { [key: string]: string | undefined }\n\t) {\n\t\tthis.shapeFontFacesCache = editor.store.createComputedCache(\n\t\t\t'shape font faces',\n\t\t\t(shape: TLShape) => {\n\t\t\t\tconst shapeUtil = this.editor.getShapeUtil(shape)\n\t\t\t\treturn shapeUtil.getFontFaces(shape)\n\t\t\t},\n\t\t\t{ areResultsEqual: areArraysShallowEqual }\n\t\t)\n\n\t\tthis.shapeFontLoadStateCache = editor.store.createCache<(FontState | null)[], TLShape>(\n\t\t\t(id: TLShapeId) => {\n\t\t\t\tconst fontFacesComputed = computed('font faces', () => this.getShapeFontFaces(id))\n\t\t\t\treturn computed(\n\t\t\t\t\t'font load state',\n\t\t\t\t\t() => {\n\t\t\t\t\t\tconst states = fontFacesComputed.get().map((face) => this.getFontState(face))\n\t\t\t\t\t\treturn states\n\t\t\t\t\t},\n\t\t\t\t\t{ isEqual: areArraysShallowEqual }\n\t\t\t\t)\n\t\t\t}\n\t\t)\n\t}\n\n\tprivate readonly shapeFontFacesCache\n\tprivate readonly shapeFontLoadStateCache\n\n\tgetShapeFontFaces(shape: TLShape | TLShapeId): TLFontFace[] {\n\t\tconst shapeId = typeof shape === 'string' ? shape : shape.id\n\t\treturn this.shapeFontFacesCache.get(shapeId) ?? EMPTY_ARRAY\n\t}\n\n\ttrackFontsForShape(shape: TLShape | TLShapeId) {\n\t\tconst shapeId = typeof shape === 'string' ? shape : shape.id\n\t\tthis.shapeFontLoadStateCache.get(shapeId)\n\t}\n\n\tasync loadRequiredFontsForCurrentPage(limit = Infinity) {\n\t\tconst neededFonts = new Set<TLFontFace>()\n\t\tfor (const shapeId of this.editor.getCurrentPageShapeIds()) {\n\t\t\tfor (const font of this.getShapeFontFaces(this.editor.getShape(shapeId)!)) {\n\t\t\t\tneededFonts.add(font)\n\t\t\t}\n\t\t}\n\n\t\tif (neededFonts.size > limit) {\n\t\t\treturn\n\t\t}\n\n\t\tconst promises = Array.from(neededFonts, (font) => this.ensureFontIsLoaded(font))\n\t\tawait Promise.all(promises)\n\t}\n\n\tprivate readonly fontStates = new AtomMap<TLFontFace, FontState>('font states')\n\tprivate getFontState(font: TLFontFace): FontState | null {\n\t\treturn this.fontStates.get(font) ?? null\n\t}\n\n\tensureFontIsLoaded(font: TLFontFace): Promise<void> {\n\t\tconst existingState = this.getFontState(font)\n\t\tif (existingState) return existingState.loadingPromise\n\n\t\tconst instance = this.findOrCreateFontFace(font)\n\t\tconst state: FontState = {\n\t\t\tstate: 'loading',\n\t\t\tinstance,\n\t\t\tloadingPromise: instance\n\t\t\t\t.load()\n\t\t\t\t.then(() => {\n\t\t\t\t\tdocument.fonts.add(instance)\n\t\t\t\t\tthis.fontStates.update(font, (s) => ({ ...s, state: 'ready' }))\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tconsole.error(err)\n\t\t\t\t\tthis.fontStates.update(font, (s) => ({ ...s, state: 'error' }))\n\t\t\t\t}),\n\t\t}\n\n\t\tthis.fontStates.set(font, state)\n\t\treturn state.loadingPromise\n\t}\n\n\tprivate fontsToLoad = new Set<TLFontFace>()\n\trequestFonts(fonts: TLFontFace[]) {\n\t\tif (!this.fontsToLoad.size) {\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tif (this.editor.isDisposed) return\n\t\t\t\tconst toLoad = this.fontsToLoad\n\t\t\t\tthis.fontsToLoad = new Set()\n\t\t\t\ttransact(() => {\n\t\t\t\t\tfor (const font of toLoad) {\n\t\t\t\t\t\tthis.ensureFontIsLoaded(font)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t\tfor (const font of fonts) {\n\t\t\tthis.fontsToLoad.add(font)\n\t\t}\n\t}\n\n\tprivate findOrCreateFontFace(font: TLFontFace) {\n\t\tfor (const existing of document.fonts) {\n\t\t\tif (\n\t\t\t\texisting.family === font.family &&\n\t\t\t\tobjectMapEntries(defaultFontFaceDescriptors).every(\n\t\t\t\t\t([key, defaultValue]) => existing[key] === (font[key] ?? defaultValue)\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\treturn existing\n\t\t\t}\n\t\t}\n\n\t\tconst url = this.assetUrls?.[font.src.url] ?? font.src.url\n\t\tconst instance = new FontFace(font.family, `url(${JSON.stringify(url)})`, {\n\t\t\t...mapObjectMapValues(defaultFontFaceDescriptors, (key) => font[key]),\n\t\t\tdisplay: 'swap',\n\t\t})\n\n\t\tdocument.fonts.add(instance)\n\n\t\treturn instance\n\t}\n\n\tasync toEmbeddedCssDeclaration(font: TLFontFace) {\n\t\tconst url = this.assetUrls?.[font.src.url] ?? font.src.url\n\t\tconst dataUrl = await FileHelpers.urlToDataUrl(url)\n\n\t\tconst src = compact([\n\t\t\t`url(\"${dataUrl}\")`,\n\t\t\tfont.src.format ? `format(${font.src.format})` : null,\n\t\t\tfont.src.tech ? `tech(${font.src.tech})` : null,\n\t\t]).join(' ')\n\t\treturn compact([\n\t\t\t`@font-face {`,\n\t\t\t` font-family: \"${font.family}\";`,\n\t\t\tfont.ascentOverride ? ` ascent-override: ${font.ascentOverride};` : null,\n\t\t\tfont.descentOverride ? ` descent-override: ${font.descentOverride};` : null,\n\t\t\tfont.stretch ? ` font-stretch: ${font.stretch};` : null,\n\t\t\tfont.style ? ` font-style: ${font.style};` : null,\n\t\t\tfont.weight ? ` font-weight: ${font.weight};` : null,\n\t\t\tfont.featureSettings ? ` font-feature-settings: ${font.featureSettings};` : null,\n\t\t\tfont.lineGapOverride ? ` line-gap-override: ${font.lineGapOverride};` : null,\n\t\t\tfont.unicodeRange ? ` unicode-range: ${font.unicodeRange};` : null,\n\t\t\t` src: ${src};`,\n\t\t\t`}`,\n\t\t]).join('\\n')\n\t}\n}\n\n// From https://drafts.csswg.org/css-font-loading/#fontface-interface\nconst defaultFontFaceDescriptors = {\n\tstyle: 'normal',\n\tweight: 'normal',\n\tstretch: 'normal',\n\tunicodeRange: 'U+0-10FFFF',\n\tfeatureSettings: 'normal',\n\tascentOverride: 'normal',\n\tdescentOverride: 'normal',\n\tlineGapOverride: 'normal',\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,UAAU,aAAa,gBAAgB;AAChD,SAAS,eAAe;AAExB;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AA4EA,MAAM,YAAY;AAAA,EACxB,YACkB,QACA,WAChB;AAFgB;AACA;AAEjB,SAAK,sBAAsB,OAAO,MAAM;AAAA,MACvC;AAAA,MACA,CAAC,UAAmB;AACnB,cAAM,YAAY,KAAK,OAAO,aAAa,KAAK;AAChD,eAAO,UAAU,aAAa,KAAK;AAAA,MACpC;AAAA,MACA,EAAE,iBAAiB,
|
|
4
|
+
"sourcesContent": ["import { computed, EMPTY_ARRAY, transact } from '@tldraw/state'\nimport { AtomMap } from '@tldraw/store'\nimport { TLShape, TLShapeId } from '@tldraw/tlschema'\nimport {\n\tareArraysShallowEqual,\n\tcompact,\n\tFileHelpers,\n\tmapObjectMapValues,\n\tobjectMapEntries,\n} from '@tldraw/utils'\nimport { Editor } from '../Editor'\n\n/**\n * Represents the `src` property of a {@link TLFontFace}.\n * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src | `src`} for details of the properties here.\n * @public\n */\nexport interface TLFontFaceSource {\n\t/**\n\t * A URL from which to load the font. If the value here is a key in\n\t * {@link tldraw#TLEditorAssetUrls.fonts}, the value from there will be used instead.\n\t */\n\turl: string\n\tformat?: string\n\ttech?: string\n}\n\n/**\n * A font face that can be used in the editor. The properties of this are largely the same as the\n * ones in the\n * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face | css `@font-face` rule}.\n * @public\n */\nexport interface TLFontFace {\n\t/**\n\t * How this font can be referred to in CSS.\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-family | `font-family`}.\n\t */\n\treadonly family: string\n\t/**\n\t * The source of the font. This\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src | `src`}.\n\t */\n\treadonly src: TLFontFaceSource\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/ascent-override | `ascent-override`}.\n\t */\n\treadonly ascentOverride?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/descent-override | `descent-override`}.\n\t */\n\treadonly descentOverride?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-stretch | `font-stretch`}.\n\t */\n\treadonly stretch?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-style | `font-style`}.\n\t */\n\treadonly style?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-weight | `font-weight`}.\n\t */\n\treadonly weight?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-feature-settings | `font-feature-settings`}.\n\t */\n\treadonly featureSettings?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/line-gap-override | `line-gap-override`}.\n\t */\n\treadonly lineGapOverride?: string\n\t/**\n\t * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range | `unicode-range`}.\n\t */\n\treadonly unicodeRange?: string\n}\n\ninterface FontState {\n\treadonly state: 'loading' | 'ready' | 'error'\n\treadonly instance: FontFace\n\treadonly loadingPromise: Promise<void>\n}\n\n/** @public */\nexport class FontManager {\n\tconstructor(\n\t\tprivate readonly editor: Editor,\n\t\tprivate readonly assetUrls?: { [key: string]: string | undefined }\n\t) {\n\t\tthis.shapeFontFacesCache = editor.store.createComputedCache(\n\t\t\t'shape font faces',\n\t\t\t(shape: TLShape) => {\n\t\t\t\tconst shapeUtil = this.editor.getShapeUtil(shape)\n\t\t\t\treturn shapeUtil.getFontFaces(shape)\n\t\t\t},\n\t\t\t{ areResultsEqual: areArraysShallowEqual, areRecordsEqual: (a, b) => a.props === b.props }\n\t\t)\n\n\t\tthis.shapeFontLoadStateCache = editor.store.createCache<(FontState | null)[], TLShape>(\n\t\t\t(id: TLShapeId) => {\n\t\t\t\tconst fontFacesComputed = computed('font faces', () => this.getShapeFontFaces(id))\n\t\t\t\treturn computed(\n\t\t\t\t\t'font load state',\n\t\t\t\t\t() => {\n\t\t\t\t\t\tconst states = fontFacesComputed.get().map((face) => this.getFontState(face))\n\t\t\t\t\t\treturn states\n\t\t\t\t\t},\n\t\t\t\t\t{ isEqual: areArraysShallowEqual }\n\t\t\t\t)\n\t\t\t}\n\t\t)\n\t}\n\n\tprivate readonly shapeFontFacesCache\n\tprivate readonly shapeFontLoadStateCache\n\n\tgetShapeFontFaces(shape: TLShape | TLShapeId): TLFontFace[] {\n\t\tconst shapeId = typeof shape === 'string' ? shape : shape.id\n\t\treturn this.shapeFontFacesCache.get(shapeId) ?? EMPTY_ARRAY\n\t}\n\n\ttrackFontsForShape(shape: TLShape | TLShapeId) {\n\t\tconst shapeId = typeof shape === 'string' ? shape : shape.id\n\t\tthis.shapeFontLoadStateCache.get(shapeId)\n\t}\n\n\tasync loadRequiredFontsForCurrentPage(limit = Infinity) {\n\t\tconst neededFonts = new Set<TLFontFace>()\n\t\tfor (const shapeId of this.editor.getCurrentPageShapeIds()) {\n\t\t\tfor (const font of this.getShapeFontFaces(this.editor.getShape(shapeId)!)) {\n\t\t\t\tneededFonts.add(font)\n\t\t\t}\n\t\t}\n\n\t\tif (neededFonts.size > limit) {\n\t\t\treturn\n\t\t}\n\n\t\tconst promises = Array.from(neededFonts, (font) => this.ensureFontIsLoaded(font))\n\t\tawait Promise.all(promises)\n\t}\n\n\tprivate readonly fontStates = new AtomMap<TLFontFace, FontState>('font states')\n\tprivate getFontState(font: TLFontFace): FontState | null {\n\t\treturn this.fontStates.get(font) ?? null\n\t}\n\n\tensureFontIsLoaded(font: TLFontFace): Promise<void> {\n\t\tconst existingState = this.getFontState(font)\n\t\tif (existingState) return existingState.loadingPromise\n\n\t\tconst instance = this.findOrCreateFontFace(font)\n\t\tconst state: FontState = {\n\t\t\tstate: 'loading',\n\t\t\tinstance,\n\t\t\tloadingPromise: instance\n\t\t\t\t.load()\n\t\t\t\t.then(() => {\n\t\t\t\t\tdocument.fonts.add(instance)\n\t\t\t\t\tthis.fontStates.update(font, (s) => ({ ...s, state: 'ready' }))\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tconsole.error(err)\n\t\t\t\t\tthis.fontStates.update(font, (s) => ({ ...s, state: 'error' }))\n\t\t\t\t}),\n\t\t}\n\n\t\tthis.fontStates.set(font, state)\n\t\treturn state.loadingPromise\n\t}\n\n\tprivate fontsToLoad = new Set<TLFontFace>()\n\trequestFonts(fonts: TLFontFace[]) {\n\t\tif (!this.fontsToLoad.size) {\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tif (this.editor.isDisposed) return\n\t\t\t\tconst toLoad = this.fontsToLoad\n\t\t\t\tthis.fontsToLoad = new Set()\n\t\t\t\ttransact(() => {\n\t\t\t\t\tfor (const font of toLoad) {\n\t\t\t\t\t\tthis.ensureFontIsLoaded(font)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t\tfor (const font of fonts) {\n\t\t\tthis.fontsToLoad.add(font)\n\t\t}\n\t}\n\n\tprivate findOrCreateFontFace(font: TLFontFace) {\n\t\tfor (const existing of document.fonts) {\n\t\t\tif (\n\t\t\t\texisting.family === font.family &&\n\t\t\t\tobjectMapEntries(defaultFontFaceDescriptors).every(\n\t\t\t\t\t([key, defaultValue]) => existing[key] === (font[key] ?? defaultValue)\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\treturn existing\n\t\t\t}\n\t\t}\n\n\t\tconst url = this.assetUrls?.[font.src.url] ?? font.src.url\n\t\tconst instance = new FontFace(font.family, `url(${JSON.stringify(url)})`, {\n\t\t\t...mapObjectMapValues(defaultFontFaceDescriptors, (key) => font[key]),\n\t\t\tdisplay: 'swap',\n\t\t})\n\n\t\tdocument.fonts.add(instance)\n\n\t\treturn instance\n\t}\n\n\tasync toEmbeddedCssDeclaration(font: TLFontFace) {\n\t\tconst url = this.assetUrls?.[font.src.url] ?? font.src.url\n\t\tconst dataUrl = await FileHelpers.urlToDataUrl(url)\n\n\t\tconst src = compact([\n\t\t\t`url(\"${dataUrl}\")`,\n\t\t\tfont.src.format ? `format(${font.src.format})` : null,\n\t\t\tfont.src.tech ? `tech(${font.src.tech})` : null,\n\t\t]).join(' ')\n\t\treturn compact([\n\t\t\t`@font-face {`,\n\t\t\t` font-family: \"${font.family}\";`,\n\t\t\tfont.ascentOverride ? ` ascent-override: ${font.ascentOverride};` : null,\n\t\t\tfont.descentOverride ? ` descent-override: ${font.descentOverride};` : null,\n\t\t\tfont.stretch ? ` font-stretch: ${font.stretch};` : null,\n\t\t\tfont.style ? ` font-style: ${font.style};` : null,\n\t\t\tfont.weight ? ` font-weight: ${font.weight};` : null,\n\t\t\tfont.featureSettings ? ` font-feature-settings: ${font.featureSettings};` : null,\n\t\t\tfont.lineGapOverride ? ` line-gap-override: ${font.lineGapOverride};` : null,\n\t\t\tfont.unicodeRange ? ` unicode-range: ${font.unicodeRange};` : null,\n\t\t\t` src: ${src};`,\n\t\t\t`}`,\n\t\t]).join('\\n')\n\t}\n}\n\n// From https://drafts.csswg.org/css-font-loading/#fontface-interface\nconst defaultFontFaceDescriptors = {\n\tstyle: 'normal',\n\tweight: 'normal',\n\tstretch: 'normal',\n\tunicodeRange: 'U+0-10FFFF',\n\tfeatureSettings: 'normal',\n\tascentOverride: 'normal',\n\tdescentOverride: 'normal',\n\tlineGapOverride: 'normal',\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -20,7 +20,7 @@ const STATE_NODES_TO_MEASURE = [
|
|
|
20
20
|
class StateNode {
|
|
21
21
|
constructor(editor, parent) {
|
|
22
22
|
this.editor = editor;
|
|
23
|
-
const { id, children, initial, isLockable } = this.constructor;
|
|
23
|
+
const { id, children, initial, isLockable, useCoalescedEvents } = this.constructor;
|
|
24
24
|
this.id = id;
|
|
25
25
|
this._isActive = atom("toolIsActive" + this.id, false);
|
|
26
26
|
this._current = atom("toolState" + this.id, void 0);
|
|
@@ -51,6 +51,7 @@ class StateNode {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
this.isLockable = isLockable;
|
|
54
|
+
this.useCoalescedEvents = useCoalescedEvents;
|
|
54
55
|
this.performanceTracker = new PerformanceTracker();
|
|
55
56
|
}
|
|
56
57
|
performanceTracker;
|
|
@@ -58,12 +59,14 @@ class StateNode {
|
|
|
58
59
|
static initial;
|
|
59
60
|
static children;
|
|
60
61
|
static isLockable = true;
|
|
62
|
+
static useCoalescedEvents = false;
|
|
61
63
|
id;
|
|
62
64
|
type;
|
|
63
65
|
shapeType;
|
|
64
66
|
initial;
|
|
65
67
|
children;
|
|
66
68
|
isLockable;
|
|
69
|
+
useCoalescedEvents;
|
|
67
70
|
parent;
|
|
68
71
|
/**
|
|
69
72
|
* This node's path of active state nodes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/editor/tools/StateNode.ts"],
|
|
4
|
-
"sourcesContent": ["import { Atom, Computed, atom, computed } from '@tldraw/state'\nimport { PerformanceTracker } from '@tldraw/utils'\nimport { debugFlags } from '../../utils/debug-flags'\nimport type { Editor } from '../Editor'\nimport {\n\tEVENT_NAME_MAP,\n\tTLCancelEventInfo,\n\tTLClickEventInfo,\n\tTLCompleteEventInfo,\n\tTLEventHandlers,\n\tTLEventInfo,\n\tTLInterruptEventInfo,\n\tTLKeyboardEventInfo,\n\tTLPinchEventInfo,\n\tTLPointerEventInfo,\n\tTLTickEventInfo,\n\tTLWheelEventInfo,\n} from '../types/event-types'\n\nconst STATE_NODES_TO_MEASURE = [\n\t'brushing',\n\t'cropping',\n\t'dragging',\n\t'dragging_handle',\n\t'drawing',\n\t'erasing',\n\t'lasering',\n\t'resizing',\n\t'rotating',\n\t'scribble_brushing',\n\t'translating',\n]\n\n/** @public */\nexport interface TLStateNodeConstructor {\n\tnew (editor: Editor, parent?: StateNode): StateNode\n\tid: string\n\tinitial?: string\n\tchildren?(): TLStateNodeConstructor[]\n\tisLockable: boolean\n}\n\n/** @public */\nexport abstract class StateNode implements Partial<TLEventHandlers> {\n\tperformanceTracker: PerformanceTracker\n\tconstructor(\n\t\tpublic editor: Editor,\n\t\tparent?: StateNode\n\t) {\n\t\tconst { id, children, initial, isLockable } = this.constructor as TLStateNodeConstructor\n\n\t\tthis.id = id\n\t\tthis._isActive = atom<boolean>('toolIsActive' + this.id, false)\n\t\tthis._current = atom<StateNode | undefined>('toolState' + this.id, undefined)\n\n\t\tthis._path = computed('toolPath' + this.id, () => {\n\t\t\tconst current = this.getCurrent()\n\t\t\treturn this.id + (current ? `.${current.getPath()}` : '')\n\t\t})\n\n\t\tthis.parent = parent ?? ({} as any)\n\n\t\tif (this.parent) {\n\t\t\tif (children && initial) {\n\t\t\t\tthis.type = 'branch'\n\t\t\t\tthis.initial = initial\n\t\t\t\tthis.children = Object.fromEntries(\n\t\t\t\t\tchildren().map((Ctor) => [Ctor.id, new Ctor(this.editor, this)])\n\t\t\t\t)\n\t\t\t\tthis._current.set(this.children[this.initial])\n\t\t\t} else {\n\t\t\t\tthis.type = 'leaf'\n\t\t\t}\n\t\t} else {\n\t\t\tthis.type = 'root'\n\n\t\t\tif (children && initial) {\n\t\t\t\tthis.initial = initial\n\t\t\t\tthis.children = Object.fromEntries(\n\t\t\t\t\tchildren().map((Ctor) => [Ctor.id, new Ctor(this.editor, this)])\n\t\t\t\t)\n\t\t\t\tthis._current.set(this.children[this.initial])\n\t\t\t}\n\t\t}\n\t\tthis.isLockable = isLockable\n\t\tthis.performanceTracker = new PerformanceTracker()\n\t}\n\n\tstatic id: string\n\tstatic initial?: string\n\tstatic children?: () => TLStateNodeConstructor[]\n\tstatic isLockable = true\n\n\tid: string\n\ttype: 'branch' | 'leaf' | 'root'\n\tshapeType?: string\n\tinitial?: string\n\tchildren?: Record<string, StateNode>\n\tisLockable: boolean\n\tparent: StateNode\n\n\t/**\n\t * This node's path of active state nodes\n\t *\n\t * @public\n\t */\n\tgetPath() {\n\t\treturn this._path.get()\n\t}\n\t_path: Computed<string>\n\n\t/**\n\t * This node's current active child node, if any.\n\t *\n\t * @public\n\t */\n\tgetCurrent() {\n\t\treturn this._current.get()\n\t}\n\tprivate _current: Atom<StateNode | undefined>\n\n\t/**\n\t * Whether this node is active.\n\t *\n\t * @public\n\t */\n\tgetIsActive() {\n\t\treturn this._isActive.get()\n\t}\n\tprivate _isActive: Atom<boolean>\n\n\t/**\n\t * Transition to a new active child state node.\n\t *\n\t * @example\n\t * ```ts\n\t * parentState.transition('childStateA')\n\t * parentState.transition('childStateB', { myData: 4 })\n\t *```\n\t *\n\t * @param id - The id of the child state node to transition to.\n\t * @param info - Any data to pass to the `onEnter` and `onExit` handlers.\n\t *\n\t * @public\n\t */\n\ttransition(id: string, info: any = {}) {\n\t\tconst path = id.split('.')\n\n\t\tlet currState = this as StateNode\n\n\t\tfor (let i = 0; i < path.length; i++) {\n\t\t\tconst id = path[i]\n\t\t\tconst prevChildState = currState.getCurrent()\n\t\t\tconst nextChildState = currState.children?.[id]\n\n\t\t\tif (!nextChildState) {\n\t\t\t\tthrow Error(`${currState.id} - no child state exists with the id ${id}.`)\n\t\t\t}\n\n\t\t\tif (prevChildState?.id !== nextChildState.id) {\n\t\t\t\tprevChildState?.exit(info, id)\n\t\t\t\tcurrState._current.set(nextChildState)\n\t\t\t\tnextChildState.enter(info, prevChildState?.id || 'initial')\n\t\t\t\tif (!nextChildState.getIsActive()) break\n\t\t\t}\n\n\t\t\tcurrState = nextChildState\n\t\t}\n\n\t\treturn this\n\t}\n\n\thandleEvent(info: Exclude<TLEventInfo, TLPinchEventInfo>) {\n\t\tconst cbName = EVENT_NAME_MAP[info.name]\n\t\tconst currentActiveChild = this._current.__unsafe__getWithoutCapture()\n\n\t\tthis[cbName]?.(info as any)\n\t\tif (\n\t\t\tthis._isActive.__unsafe__getWithoutCapture() &&\n\t\t\tcurrentActiveChild &&\n\t\t\tcurrentActiveChild === this._current.__unsafe__getWithoutCapture()\n\t\t) {\n\t\t\tcurrentActiveChild.handleEvent(info)\n\t\t}\n\t}\n\n\t// todo: move this logic into transition\n\tenter(info: any, from: string) {\n\t\tif (debugFlags.measurePerformance.get() && STATE_NODES_TO_MEASURE.includes(this.id)) {\n\t\t\tthis.performanceTracker.start(this.id)\n\t\t}\n\n\t\tthis._isActive.set(true)\n\t\tthis.onEnter?.(info, from)\n\n\t\tif (this.children && this.initial && this.getIsActive()) {\n\t\t\tconst initial = this.children[this.initial]\n\t\t\tthis._current.set(initial)\n\t\t\tinitial.enter(info, from)\n\t\t}\n\t}\n\n\t// todo: move this logic into transition\n\texit(info: any, from: string) {\n\t\tif (debugFlags.measurePerformance.get() && this.performanceTracker.isStarted()) {\n\t\t\tthis.performanceTracker.stop()\n\t\t}\n\t\tthis._isActive.set(false)\n\t\tthis.onExit?.(info, from)\n\n\t\tif (!this.getIsActive()) {\n\t\t\tthis.getCurrent()?.exit(info, from)\n\t\t}\n\t}\n\n\t/**\n\t * This is a hack / escape hatch that will tell the editor to\n\t * report a different state as active (in `getCurrentToolId()`) when\n\t * this state is active. This is usually used when a tool transitions\n\t * to a child of a different state for a certain interaction and then\n\t * returns to the original tool when that interaction completes; and\n\t * where we would want to show the original tool as active in the UI.\n\t *\n\t * @public\n\t */\n\t_currentToolIdMask = atom('curent tool id mask', undefined as string | undefined)\n\n\tgetCurrentToolIdMask() {\n\t\treturn this._currentToolIdMask.get()\n\t}\n\n\tsetCurrentToolIdMask(id: string | undefined) {\n\t\tthis._currentToolIdMask.set(id)\n\t}\n\n\tonWheel?(info: TLWheelEventInfo): void\n\tonPointerDown?(info: TLPointerEventInfo): void\n\tonPointerMove?(info: TLPointerEventInfo): void\n\tonLongPress?(info: TLPointerEventInfo): void\n\tonPointerUp?(info: TLPointerEventInfo): void\n\tonDoubleClick?(info: TLClickEventInfo): void\n\tonTripleClick?(info: TLClickEventInfo): void\n\tonQuadrupleClick?(info: TLClickEventInfo): void\n\tonRightClick?(info: TLPointerEventInfo): void\n\tonMiddleClick?(info: TLPointerEventInfo): void\n\tonKeyDown?(info: TLKeyboardEventInfo): void\n\tonKeyUp?(info: TLKeyboardEventInfo): void\n\tonKeyRepeat?(info: TLKeyboardEventInfo): void\n\tonCancel?(info: TLCancelEventInfo): void\n\tonComplete?(info: TLCompleteEventInfo): void\n\tonInterrupt?(info: TLInterruptEventInfo): void\n\tonTick?(info: TLTickEventInfo): void\n\n\tonEnter?(info: any, from: string): void\n\tonExit?(info: any, to: string): void\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAyB,MAAM,gBAAgB;AAC/C,SAAS,0BAA0B;AACnC,SAAS,kBAAkB;AAE3B;AAAA,EACC;AAAA,OAYM;AAEP,MAAM,yBAAyB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;
|
|
4
|
+
"sourcesContent": ["import { Atom, Computed, atom, computed } from '@tldraw/state'\nimport { PerformanceTracker } from '@tldraw/utils'\nimport { debugFlags } from '../../utils/debug-flags'\nimport type { Editor } from '../Editor'\nimport {\n\tEVENT_NAME_MAP,\n\tTLCancelEventInfo,\n\tTLClickEventInfo,\n\tTLCompleteEventInfo,\n\tTLEventHandlers,\n\tTLEventInfo,\n\tTLInterruptEventInfo,\n\tTLKeyboardEventInfo,\n\tTLPinchEventInfo,\n\tTLPointerEventInfo,\n\tTLTickEventInfo,\n\tTLWheelEventInfo,\n} from '../types/event-types'\n\nconst STATE_NODES_TO_MEASURE = [\n\t'brushing',\n\t'cropping',\n\t'dragging',\n\t'dragging_handle',\n\t'drawing',\n\t'erasing',\n\t'lasering',\n\t'resizing',\n\t'rotating',\n\t'scribble_brushing',\n\t'translating',\n]\n\n/** @public */\nexport interface TLStateNodeConstructor {\n\tnew (editor: Editor, parent?: StateNode): StateNode\n\tid: string\n\tinitial?: string\n\tchildren?(): TLStateNodeConstructor[]\n\tisLockable: boolean\n\tuseCoalescedEvents: boolean\n}\n\n/** @public */\nexport abstract class StateNode implements Partial<TLEventHandlers> {\n\tperformanceTracker: PerformanceTracker\n\tconstructor(\n\t\tpublic editor: Editor,\n\t\tparent?: StateNode\n\t) {\n\t\tconst { id, children, initial, isLockable, useCoalescedEvents } = this\n\t\t\t.constructor as TLStateNodeConstructor\n\n\t\tthis.id = id\n\t\tthis._isActive = atom<boolean>('toolIsActive' + this.id, false)\n\t\tthis._current = atom<StateNode | undefined>('toolState' + this.id, undefined)\n\n\t\tthis._path = computed('toolPath' + this.id, () => {\n\t\t\tconst current = this.getCurrent()\n\t\t\treturn this.id + (current ? `.${current.getPath()}` : '')\n\t\t})\n\n\t\tthis.parent = parent ?? ({} as any)\n\n\t\tif (this.parent) {\n\t\t\tif (children && initial) {\n\t\t\t\tthis.type = 'branch'\n\t\t\t\tthis.initial = initial\n\t\t\t\tthis.children = Object.fromEntries(\n\t\t\t\t\tchildren().map((Ctor) => [Ctor.id, new Ctor(this.editor, this)])\n\t\t\t\t)\n\t\t\t\tthis._current.set(this.children[this.initial])\n\t\t\t} else {\n\t\t\t\tthis.type = 'leaf'\n\t\t\t}\n\t\t} else {\n\t\t\tthis.type = 'root'\n\n\t\t\tif (children && initial) {\n\t\t\t\tthis.initial = initial\n\t\t\t\tthis.children = Object.fromEntries(\n\t\t\t\t\tchildren().map((Ctor) => [Ctor.id, new Ctor(this.editor, this)])\n\t\t\t\t)\n\t\t\t\tthis._current.set(this.children[this.initial])\n\t\t\t}\n\t\t}\n\t\tthis.isLockable = isLockable\n\t\tthis.useCoalescedEvents = useCoalescedEvents\n\t\tthis.performanceTracker = new PerformanceTracker()\n\t}\n\n\tstatic id: string\n\tstatic initial?: string\n\tstatic children?: () => TLStateNodeConstructor[]\n\tstatic isLockable = true\n\tstatic useCoalescedEvents = false\n\n\tid: string\n\ttype: 'branch' | 'leaf' | 'root'\n\tshapeType?: string\n\tinitial?: string\n\tchildren?: Record<string, StateNode>\n\tisLockable: boolean\n\tuseCoalescedEvents: boolean\n\tparent: StateNode\n\n\t/**\n\t * This node's path of active state nodes\n\t *\n\t * @public\n\t */\n\tgetPath() {\n\t\treturn this._path.get()\n\t}\n\t_path: Computed<string>\n\n\t/**\n\t * This node's current active child node, if any.\n\t *\n\t * @public\n\t */\n\tgetCurrent() {\n\t\treturn this._current.get()\n\t}\n\tprivate _current: Atom<StateNode | undefined>\n\n\t/**\n\t * Whether this node is active.\n\t *\n\t * @public\n\t */\n\tgetIsActive() {\n\t\treturn this._isActive.get()\n\t}\n\tprivate _isActive: Atom<boolean>\n\n\t/**\n\t * Transition to a new active child state node.\n\t *\n\t * @example\n\t * ```ts\n\t * parentState.transition('childStateA')\n\t * parentState.transition('childStateB', { myData: 4 })\n\t *```\n\t *\n\t * @param id - The id of the child state node to transition to.\n\t * @param info - Any data to pass to the `onEnter` and `onExit` handlers.\n\t *\n\t * @public\n\t */\n\ttransition(id: string, info: any = {}) {\n\t\tconst path = id.split('.')\n\n\t\tlet currState = this as StateNode\n\n\t\tfor (let i = 0; i < path.length; i++) {\n\t\t\tconst id = path[i]\n\t\t\tconst prevChildState = currState.getCurrent()\n\t\t\tconst nextChildState = currState.children?.[id]\n\n\t\t\tif (!nextChildState) {\n\t\t\t\tthrow Error(`${currState.id} - no child state exists with the id ${id}.`)\n\t\t\t}\n\n\t\t\tif (prevChildState?.id !== nextChildState.id) {\n\t\t\t\tprevChildState?.exit(info, id)\n\t\t\t\tcurrState._current.set(nextChildState)\n\t\t\t\tnextChildState.enter(info, prevChildState?.id || 'initial')\n\t\t\t\tif (!nextChildState.getIsActive()) break\n\t\t\t}\n\n\t\t\tcurrState = nextChildState\n\t\t}\n\n\t\treturn this\n\t}\n\n\thandleEvent(info: Exclude<TLEventInfo, TLPinchEventInfo>) {\n\t\tconst cbName = EVENT_NAME_MAP[info.name]\n\t\tconst currentActiveChild = this._current.__unsafe__getWithoutCapture()\n\n\t\tthis[cbName]?.(info as any)\n\t\tif (\n\t\t\tthis._isActive.__unsafe__getWithoutCapture() &&\n\t\t\tcurrentActiveChild &&\n\t\t\tcurrentActiveChild === this._current.__unsafe__getWithoutCapture()\n\t\t) {\n\t\t\tcurrentActiveChild.handleEvent(info)\n\t\t}\n\t}\n\n\t// todo: move this logic into transition\n\tenter(info: any, from: string) {\n\t\tif (debugFlags.measurePerformance.get() && STATE_NODES_TO_MEASURE.includes(this.id)) {\n\t\t\tthis.performanceTracker.start(this.id)\n\t\t}\n\n\t\tthis._isActive.set(true)\n\t\tthis.onEnter?.(info, from)\n\n\t\tif (this.children && this.initial && this.getIsActive()) {\n\t\t\tconst initial = this.children[this.initial]\n\t\t\tthis._current.set(initial)\n\t\t\tinitial.enter(info, from)\n\t\t}\n\t}\n\n\t// todo: move this logic into transition\n\texit(info: any, from: string) {\n\t\tif (debugFlags.measurePerformance.get() && this.performanceTracker.isStarted()) {\n\t\t\tthis.performanceTracker.stop()\n\t\t}\n\t\tthis._isActive.set(false)\n\t\tthis.onExit?.(info, from)\n\n\t\tif (!this.getIsActive()) {\n\t\t\tthis.getCurrent()?.exit(info, from)\n\t\t}\n\t}\n\n\t/**\n\t * This is a hack / escape hatch that will tell the editor to\n\t * report a different state as active (in `getCurrentToolId()`) when\n\t * this state is active. This is usually used when a tool transitions\n\t * to a child of a different state for a certain interaction and then\n\t * returns to the original tool when that interaction completes; and\n\t * where we would want to show the original tool as active in the UI.\n\t *\n\t * @public\n\t */\n\t_currentToolIdMask = atom('curent tool id mask', undefined as string | undefined)\n\n\tgetCurrentToolIdMask() {\n\t\treturn this._currentToolIdMask.get()\n\t}\n\n\tsetCurrentToolIdMask(id: string | undefined) {\n\t\tthis._currentToolIdMask.set(id)\n\t}\n\n\tonWheel?(info: TLWheelEventInfo): void\n\tonPointerDown?(info: TLPointerEventInfo): void\n\tonPointerMove?(info: TLPointerEventInfo): void\n\tonLongPress?(info: TLPointerEventInfo): void\n\tonPointerUp?(info: TLPointerEventInfo): void\n\tonDoubleClick?(info: TLClickEventInfo): void\n\tonTripleClick?(info: TLClickEventInfo): void\n\tonQuadrupleClick?(info: TLClickEventInfo): void\n\tonRightClick?(info: TLPointerEventInfo): void\n\tonMiddleClick?(info: TLPointerEventInfo): void\n\tonKeyDown?(info: TLKeyboardEventInfo): void\n\tonKeyUp?(info: TLKeyboardEventInfo): void\n\tonKeyRepeat?(info: TLKeyboardEventInfo): void\n\tonCancel?(info: TLCancelEventInfo): void\n\tonComplete?(info: TLCompleteEventInfo): void\n\tonInterrupt?(info: TLInterruptEventInfo): void\n\tonTick?(info: TLTickEventInfo): void\n\n\tonEnter?(info: any, from: string): void\n\tonExit?(info: any, to: string): void\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["id"]
|
|
7
7
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useValue } from "@tldraw/state-react";
|
|
1
2
|
import { useMemo } from "react";
|
|
2
3
|
import { RIGHT_MOUSE_BUTTON } from "../constants.mjs";
|
|
3
4
|
import {
|
|
@@ -10,6 +11,7 @@ import { getPointerInfo } from "../utils/getPointerInfo.mjs";
|
|
|
10
11
|
import { useEditor } from "./useEditor.mjs";
|
|
11
12
|
function useCanvasEvents() {
|
|
12
13
|
const editor = useEditor();
|
|
14
|
+
const currentTool = useValue("current tool", () => editor.getCurrentTool(), [editor]);
|
|
13
15
|
const events = useMemo(
|
|
14
16
|
function canvasEvents() {
|
|
15
17
|
let lastX, lastY;
|
|
@@ -38,12 +40,15 @@ function useCanvasEvents() {
|
|
|
38
40
|
if (e.clientX === lastX && e.clientY === lastY) return;
|
|
39
41
|
lastX = e.clientX;
|
|
40
42
|
lastY = e.clientY;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
const events2 = currentTool.useCoalescedEvents ? e.nativeEvent.getCoalescedEvents() : [e];
|
|
44
|
+
for (const singleEvent of events2) {
|
|
45
|
+
editor.dispatch({
|
|
46
|
+
type: "pointer",
|
|
47
|
+
target: "canvas",
|
|
48
|
+
name: "pointer_move",
|
|
49
|
+
...getPointerInfo(singleEvent)
|
|
50
|
+
});
|
|
51
|
+
}
|
|
47
52
|
}
|
|
48
53
|
function onPointerUp(e) {
|
|
49
54
|
if (e.isKilled) return;
|
|
@@ -129,7 +134,7 @@ function useCanvasEvents() {
|
|
|
129
134
|
onClick
|
|
130
135
|
};
|
|
131
136
|
},
|
|
132
|
-
[editor]
|
|
137
|
+
[editor, currentTool]
|
|
133
138
|
);
|
|
134
139
|
return events;
|
|
135
140
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/hooks/useCanvasEvents.ts"],
|
|
4
|
-
"sourcesContent": ["import React, { useMemo } from 'react'\nimport { RIGHT_MOUSE_BUTTON } from '../constants'\nimport {\n\tpreventDefault,\n\treleasePointerCapture,\n\tsetPointerCapture,\n\tstopEventPropagation,\n} from '../utils/dom'\nimport { getPointerInfo } from '../utils/getPointerInfo'\nimport { useEditor } from './useEditor'\n\nexport function useCanvasEvents() {\n\tconst editor = useEditor()\n\n\tconst events = useMemo(\n\t\tfunction canvasEvents() {\n\t\t\t// Track the last screen point\n\t\t\tlet lastX: number, lastY: number\n\n\t\t\tfunction onPointerDown(e: React.PointerEvent) {\n\t\t\t\tif ((e as any).isKilled) return\n\n\t\t\t\tif (e.button === RIGHT_MOUSE_BUTTON) {\n\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\t\tname: 'right_click',\n\t\t\t\t\t\t...getPointerInfo(e),\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (e.button !== 0 && e.button !== 1 && e.button !== 5) return\n\n\t\t\t\tsetPointerCapture(e.currentTarget, e)\n\n\t\t\t\teditor.dispatch({\n\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\tname: 'pointer_down',\n\t\t\t\t\t...getPointerInfo(e),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tfunction onPointerMove(e: React.PointerEvent) {\n\t\t\t\tif ((e as any).isKilled) return\n\n\t\t\t\tif (e.clientX === lastX && e.clientY === lastY) return\n\t\t\t\tlastX = e.clientX\n\t\t\t\tlastY = e.clientY\n\n\t\t\t\teditor.dispatch({\n\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\tname: 'pointer_move',\n\t\t\t\t\t...getPointerInfo(
|
|
5
|
-
"mappings": "AAAA,SAAgB,eAAe;AAC/B,SAAS,0BAA0B;AACnC;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,sBAAsB;AAC/B,SAAS,iBAAiB;AAEnB,SAAS,kBAAkB;AACjC,QAAM,SAAS,UAAU;
|
|
6
|
-
"names": []
|
|
4
|
+
"sourcesContent": ["import { useValue } from '@tldraw/state-react'\nimport React, { useMemo } from 'react'\nimport { RIGHT_MOUSE_BUTTON } from '../constants'\nimport {\n\tpreventDefault,\n\treleasePointerCapture,\n\tsetPointerCapture,\n\tstopEventPropagation,\n} from '../utils/dom'\nimport { getPointerInfo } from '../utils/getPointerInfo'\nimport { useEditor } from './useEditor'\n\nexport function useCanvasEvents() {\n\tconst editor = useEditor()\n\tconst currentTool = useValue('current tool', () => editor.getCurrentTool(), [editor])\n\n\tconst events = useMemo(\n\t\tfunction canvasEvents() {\n\t\t\t// Track the last screen point\n\t\t\tlet lastX: number, lastY: number\n\n\t\t\tfunction onPointerDown(e: React.PointerEvent) {\n\t\t\t\tif ((e as any).isKilled) return\n\n\t\t\t\tif (e.button === RIGHT_MOUSE_BUTTON) {\n\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\t\tname: 'right_click',\n\t\t\t\t\t\t...getPointerInfo(e),\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (e.button !== 0 && e.button !== 1 && e.button !== 5) return\n\n\t\t\t\tsetPointerCapture(e.currentTarget, e)\n\n\t\t\t\teditor.dispatch({\n\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\tname: 'pointer_down',\n\t\t\t\t\t...getPointerInfo(e),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tfunction onPointerMove(e: React.PointerEvent) {\n\t\t\t\tif ((e as any).isKilled) return\n\n\t\t\t\tif (e.clientX === lastX && e.clientY === lastY) return\n\t\t\t\tlastX = e.clientX\n\t\t\t\tlastY = e.clientY\n\n\t\t\t\t// For tools that benefit from a higher fidelity of events,\n\t\t\t\t// we dispatch the coalesced events.\n\t\t\t\tconst events = currentTool.useCoalescedEvents ? e.nativeEvent.getCoalescedEvents() : [e]\n\t\t\t\tfor (const singleEvent of events) {\n\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\t\tname: 'pointer_move',\n\t\t\t\t\t\t...getPointerInfo(singleEvent),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction onPointerUp(e: React.PointerEvent) {\n\t\t\t\tif ((e as any).isKilled) return\n\t\t\t\tif (e.button !== 0 && e.button !== 1 && e.button !== 2 && e.button !== 5) return\n\t\t\t\tlastX = e.clientX\n\t\t\t\tlastY = e.clientY\n\n\t\t\t\treleasePointerCapture(e.currentTarget, e)\n\n\t\t\t\teditor.dispatch({\n\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\tname: 'pointer_up',\n\t\t\t\t\t...getPointerInfo(e),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tfunction onPointerEnter(e: React.PointerEvent) {\n\t\t\t\tif ((e as any).isKilled) return\n\t\t\t\tif (editor.getInstanceState().isPenMode && e.pointerType !== 'pen') return\n\t\t\t\tconst canHover = e.pointerType === 'mouse' || e.pointerType === 'pen'\n\t\t\t\teditor.updateInstanceState({ isHoveringCanvas: canHover ? true : null })\n\t\t\t}\n\n\t\t\tfunction onPointerLeave(e: React.PointerEvent) {\n\t\t\t\tif ((e as any).isKilled) return\n\t\t\t\tif (editor.getInstanceState().isPenMode && e.pointerType !== 'pen') return\n\t\t\t\tconst canHover = e.pointerType === 'mouse' || e.pointerType === 'pen'\n\t\t\t\teditor.updateInstanceState({ isHoveringCanvas: canHover ? false : null })\n\t\t\t}\n\n\t\t\tfunction onTouchStart(e: React.TouchEvent) {\n\t\t\t\t;(e as any).isKilled = true\n\t\t\t\tpreventDefault(e)\n\t\t\t}\n\n\t\t\tfunction onTouchEnd(e: React.TouchEvent) {\n\t\t\t\t;(e as any).isKilled = true\n\t\t\t\t// check that e.target is an HTMLElement\n\t\t\t\tif (!(e.target instanceof HTMLElement)) return\n\n\t\t\t\tif (\n\t\t\t\t\te.target.tagName !== 'A' &&\n\t\t\t\t\te.target.tagName !== 'TEXTAREA' &&\n\t\t\t\t\te.target.isContentEditable &&\n\t\t\t\t\t// When in EditingShape state, we are actually clicking on a 'DIV'\n\t\t\t\t\t// not A/TEXTAREA/contenteditable element yet. So, to preserve cursor position\n\t\t\t\t\t// for edit mode on mobile we need to not preventDefault.\n\t\t\t\t\t// TODO: Find out if we still need this preventDefault in general though.\n\t\t\t\t\t!(editor.getEditingShape() && e.target.className.includes('tl-text-content'))\n\t\t\t\t) {\n\t\t\t\t\tpreventDefault(e)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction onDragOver(e: React.DragEvent<Element>) {\n\t\t\t\tpreventDefault(e)\n\t\t\t}\n\n\t\t\tasync function onDrop(e: React.DragEvent<Element>) {\n\t\t\t\tpreventDefault(e)\n\t\t\t\tstopEventPropagation(e)\n\n\t\t\t\tif (e.dataTransfer?.files?.length) {\n\t\t\t\t\tconst files = Array.from(e.dataTransfer.files)\n\n\t\t\t\t\tawait editor.putExternalContent({\n\t\t\t\t\t\ttype: 'files',\n\t\t\t\t\t\tfiles,\n\t\t\t\t\t\tpoint: editor.screenToPage({ x: e.clientX, y: e.clientY }),\n\t\t\t\t\t\tignoreParent: false,\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst url = e.dataTransfer.getData('url')\n\t\t\t\tif (url) {\n\t\t\t\t\tawait editor.putExternalContent({\n\t\t\t\t\t\ttype: 'url',\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tpoint: editor.screenToPage({ x: e.clientX, y: e.clientY }),\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction onClick(e: React.MouseEvent) {\n\t\t\t\tstopEventPropagation(e)\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tonPointerDown,\n\t\t\t\tonPointerMove,\n\t\t\t\tonPointerUp,\n\t\t\t\tonPointerEnter,\n\t\t\t\tonPointerLeave,\n\t\t\t\tonDragOver,\n\t\t\t\tonDrop,\n\t\t\t\tonTouchStart,\n\t\t\t\tonTouchEnd,\n\t\t\t\tonClick,\n\t\t\t}\n\t\t},\n\t\t[editor, currentTool]\n\t)\n\n\treturn events\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["events"]
|
|
7
7
|
}
|
|
@@ -73,7 +73,7 @@ class Group2d extends Geometry2d {
|
|
|
73
73
|
return this.children.reduce((a, c) => c.isLabel ? a : a + c.length, 0);
|
|
74
74
|
}
|
|
75
75
|
getSvgPathData() {
|
|
76
|
-
return this.children.map((c
|
|
76
|
+
return this.children.map((c) => c.isLabel ? "" : c.getSvgPathData(true)).join(" ");
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/primitives/geometry/Group2d.ts"],
|
|
4
|
-
"sourcesContent": ["import { Box } from '../Box'\nimport { Vec } from '../Vec'\nimport { Geometry2d, Geometry2dOptions } from './Geometry2d'\n\n/** @public */\nexport class Group2d extends Geometry2d {\n\tchildren: Geometry2d[] = []\n\tignoredChildren: Geometry2d[] = []\n\n\tconstructor(\n\t\tconfig: Omit<Geometry2dOptions, 'isClosed' | 'isFilled'> & {\n\t\t\tchildren: Geometry2d[]\n\t\t}\n\t) {\n\t\tsuper({ ...config, isClosed: true, isFilled: false })\n\n\t\tfor (const child of config.children) {\n\t\t\tif (child.ignore) {\n\t\t\t\tthis.ignoredChildren.push(child)\n\t\t\t} else {\n\t\t\t\tthis.children.push(child)\n\t\t\t}\n\t\t}\n\n\t\tif (this.children.length === 0) throw Error('Group2d must have at least one child')\n\t}\n\n\toverride getVertices(): Vec[] {\n\t\treturn this.children.filter((c) => !c.isLabel).flatMap((c) => c.vertices)\n\t}\n\n\toverride nearestPoint(point: Vec): Vec {\n\t\tlet dist = Infinity\n\t\tlet nearest: Vec | undefined\n\n\t\tconst { children } = this\n\n\t\tif (children.length === 0) {\n\t\t\tthrow Error('no children')\n\t\t}\n\n\t\tlet p: Vec\n\t\tlet d: number\n\t\tfor (const child of children) {\n\t\t\tp = child.nearestPoint(point)\n\t\t\td = Vec.Dist2(p, point)\n\t\t\tif (d < dist) {\n\t\t\t\tdist = d\n\t\t\t\tnearest = p\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn nearest\n\t}\n\n\toverride distanceToPoint(point: Vec, hitInside = false) {\n\t\treturn Math.min(...this.children.map((c, i) => c.distanceToPoint(point, hitInside || i > 0)))\n\t}\n\n\toverride hitTestPoint(point: Vec, margin: number, hitInside: boolean): boolean {\n\t\treturn !!this.children\n\t\t\t.filter((c) => !c.isLabel)\n\t\t\t.find((c) => c.hitTestPoint(point, margin, hitInside))\n\t}\n\n\toverride hitTestLineSegment(A: Vec, B: Vec, zoom: number): boolean {\n\t\treturn !!this.children.filter((c) => !c.isLabel).find((c) => c.hitTestLineSegment(A, B, zoom))\n\t}\n\n\tgetArea() {\n\t\t// todo: this is a temporary solution, assuming that the first child defines the group size; we would want to flatten the group and then find the area of the hull polygon\n\t\treturn this.children[0].area\n\t}\n\n\ttoSimpleSvgPath() {\n\t\tlet path = ''\n\t\tfor (const child of this.children) {\n\t\t\tpath += child.toSimpleSvgPath()\n\t\t}\n\n\t\tconst corners = Box.FromPoints(this.vertices).corners\n\t\t// draw just a few pixels around each corner, e.g. an L shape for the bottom left\n\n\t\tfor (let i = 0, n = corners.length; i < n; i++) {\n\t\t\tconst corner = corners[i]\n\t\t\tconst prevCorner = corners[(i - 1 + n) % n]\n\t\t\tconst prevDist = corner.dist(prevCorner)\n\t\t\tconst nextCorner = corners[(i + 1) % n]\n\t\t\tconst nextDist = corner.dist(nextCorner)\n\n\t\t\tconst A = corner.clone().lrp(prevCorner, 4 / prevDist)\n\t\t\tconst B = corner\n\t\t\tconst C = corner.clone().lrp(nextCorner, 4 / nextDist)\n\n\t\t\tpath += `M${A.x},${A.y} L${B.x},${B.y} L${C.x},${C.y} `\n\t\t}\n\t\treturn path\n\t}\n\n\tgetLength(): number {\n\t\treturn this.children.reduce((a, c) => (c.isLabel ? a : a + c.length), 0)\n\t}\n\n\tgetSvgPathData(): string {\n\t\treturn this.children.map((c
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { Box } from '../Box'\nimport { Vec } from '../Vec'\nimport { Geometry2d, Geometry2dOptions } from './Geometry2d'\n\n/** @public */\nexport class Group2d extends Geometry2d {\n\tchildren: Geometry2d[] = []\n\tignoredChildren: Geometry2d[] = []\n\n\tconstructor(\n\t\tconfig: Omit<Geometry2dOptions, 'isClosed' | 'isFilled'> & {\n\t\t\tchildren: Geometry2d[]\n\t\t}\n\t) {\n\t\tsuper({ ...config, isClosed: true, isFilled: false })\n\n\t\tfor (const child of config.children) {\n\t\t\tif (child.ignore) {\n\t\t\t\tthis.ignoredChildren.push(child)\n\t\t\t} else {\n\t\t\t\tthis.children.push(child)\n\t\t\t}\n\t\t}\n\n\t\tif (this.children.length === 0) throw Error('Group2d must have at least one child')\n\t}\n\n\toverride getVertices(): Vec[] {\n\t\treturn this.children.filter((c) => !c.isLabel).flatMap((c) => c.vertices)\n\t}\n\n\toverride nearestPoint(point: Vec): Vec {\n\t\tlet dist = Infinity\n\t\tlet nearest: Vec | undefined\n\n\t\tconst { children } = this\n\n\t\tif (children.length === 0) {\n\t\t\tthrow Error('no children')\n\t\t}\n\n\t\tlet p: Vec\n\t\tlet d: number\n\t\tfor (const child of children) {\n\t\t\tp = child.nearestPoint(point)\n\t\t\td = Vec.Dist2(p, point)\n\t\t\tif (d < dist) {\n\t\t\t\tdist = d\n\t\t\t\tnearest = p\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn nearest\n\t}\n\n\toverride distanceToPoint(point: Vec, hitInside = false) {\n\t\treturn Math.min(...this.children.map((c, i) => c.distanceToPoint(point, hitInside || i > 0)))\n\t}\n\n\toverride hitTestPoint(point: Vec, margin: number, hitInside: boolean): boolean {\n\t\treturn !!this.children\n\t\t\t.filter((c) => !c.isLabel)\n\t\t\t.find((c) => c.hitTestPoint(point, margin, hitInside))\n\t}\n\n\toverride hitTestLineSegment(A: Vec, B: Vec, zoom: number): boolean {\n\t\treturn !!this.children.filter((c) => !c.isLabel).find((c) => c.hitTestLineSegment(A, B, zoom))\n\t}\n\n\tgetArea() {\n\t\t// todo: this is a temporary solution, assuming that the first child defines the group size; we would want to flatten the group and then find the area of the hull polygon\n\t\treturn this.children[0].area\n\t}\n\n\ttoSimpleSvgPath() {\n\t\tlet path = ''\n\t\tfor (const child of this.children) {\n\t\t\tpath += child.toSimpleSvgPath()\n\t\t}\n\n\t\tconst corners = Box.FromPoints(this.vertices).corners\n\t\t// draw just a few pixels around each corner, e.g. an L shape for the bottom left\n\n\t\tfor (let i = 0, n = corners.length; i < n; i++) {\n\t\t\tconst corner = corners[i]\n\t\t\tconst prevCorner = corners[(i - 1 + n) % n]\n\t\t\tconst prevDist = corner.dist(prevCorner)\n\t\t\tconst nextCorner = corners[(i + 1) % n]\n\t\t\tconst nextDist = corner.dist(nextCorner)\n\n\t\t\tconst A = corner.clone().lrp(prevCorner, 4 / prevDist)\n\t\t\tconst B = corner\n\t\t\tconst C = corner.clone().lrp(nextCorner, 4 / nextDist)\n\n\t\t\tpath += `M${A.x},${A.y} L${B.x},${B.y} L${C.x},${C.y} `\n\t\t}\n\t\treturn path\n\t}\n\n\tgetLength(): number {\n\t\treturn this.children.reduce((a, c) => (c.isLabel ? a : a + c.length), 0)\n\t}\n\n\tgetSvgPathData(): string {\n\t\treturn this.children.map((c) => (c.isLabel ? '' : c.getSvgPathData(true))).join(' ')\n\t}\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,WAAW;AACpB,SAAS,WAAW;AACpB,SAAS,kBAAqC;AAGvC,MAAM,gBAAgB,WAAW;AAAA,EACvC,WAAyB,CAAC;AAAA,EAC1B,kBAAgC,CAAC;AAAA,EAEjC,YACC,QAGC;AACD,UAAM,EAAE,GAAG,QAAQ,UAAU,MAAM,UAAU,MAAM,CAAC;AAEpD,eAAW,SAAS,OAAO,UAAU;AACpC,UAAI,MAAM,QAAQ;AACjB,aAAK,gBAAgB,KAAK,KAAK;AAAA,MAChC,OAAO;AACN,aAAK,SAAS,KAAK,KAAK;AAAA,MACzB;AAAA,IACD;AAEA,QAAI,KAAK,SAAS,WAAW,EAAG,OAAM,MAAM,sCAAsC;AAAA,EACnF;AAAA,EAES,cAAqB;AAC7B,WAAO,KAAK,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ;AAAA,EACzE;AAAA,EAES,aAAa,OAAiB;AACtC,QAAI,OAAO;AACX,QAAI;AAEJ,UAAM,EAAE,SAAS,IAAI;AAErB,QAAI,SAAS,WAAW,GAAG;AAC1B,YAAM,MAAM,aAAa;AAAA,IAC1B;AAEA,QAAI;AACJ,QAAI;AACJ,eAAW,SAAS,UAAU;AAC7B,UAAI,MAAM,aAAa,KAAK;AAC5B,UAAI,IAAI,MAAM,GAAG,KAAK;AACtB,UAAI,IAAI,MAAM;AACb,eAAO;AACP,kBAAU;AAAA,MACX;AAAA,IACD;AACA,QAAI,CAAC,QAAS,OAAM,MAAM,yBAAyB;AACnD,WAAO;AAAA,EACR;AAAA,EAES,gBAAgB,OAAY,YAAY,OAAO;AACvD,WAAO,KAAK,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,GAAG,MAAM,EAAE,gBAAgB,OAAO,aAAa,IAAI,CAAC,CAAC,CAAC;AAAA,EAC7F;AAAA,EAES,aAAa,OAAY,QAAgB,WAA6B;AAC9E,WAAO,CAAC,CAAC,KAAK,SACZ,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EACxB,KAAK,CAAC,MAAM,EAAE,aAAa,OAAO,QAAQ,SAAS,CAAC;AAAA,EACvD;AAAA,EAES,mBAAmB,GAAQ,GAAQ,MAAuB;AAClE,WAAO,CAAC,CAAC,KAAK,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,mBAAmB,GAAG,GAAG,IAAI,CAAC;AAAA,EAC9F;AAAA,EAEA,UAAU;AAET,WAAO,KAAK,SAAS,CAAC,EAAE;AAAA,EACzB;AAAA,EAEA,kBAAkB;AACjB,QAAI,OAAO;AACX,eAAW,SAAS,KAAK,UAAU;AAClC,cAAQ,MAAM,gBAAgB;AAAA,IAC/B;AAEA,UAAM,UAAU,IAAI,WAAW,KAAK,QAAQ,EAAE;AAG9C,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,IAAI,GAAG,KAAK;AAC/C,YAAM,SAAS,QAAQ,CAAC;AACxB,YAAM,aAAa,SAAS,IAAI,IAAI,KAAK,CAAC;AAC1C,YAAM,WAAW,OAAO,KAAK,UAAU;AACvC,YAAM,aAAa,SAAS,IAAI,KAAK,CAAC;AACtC,YAAM,WAAW,OAAO,KAAK,UAAU;AAEvC,YAAM,IAAI,OAAO,MAAM,EAAE,IAAI,YAAY,IAAI,QAAQ;AACrD,YAAM,IAAI;AACV,YAAM,IAAI,OAAO,MAAM,EAAE,IAAI,YAAY,IAAI,QAAQ;AAErD,cAAQ,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;AAAA,IACrD;AACA,WAAO;AAAA,EACR;AAAA,EAEA,YAAoB;AACnB,WAAO,KAAK,SAAS,OAAO,CAAC,GAAG,MAAO,EAAE,UAAU,IAAI,IAAI,EAAE,QAAS,CAAC;AAAA,EACxE;AAAA,EAEA,iBAAyB;AACxB,WAAO,KAAK,SAAS,IAAI,CAAC,MAAO,EAAE,UAAU,KAAK,EAAE,eAAe,IAAI,CAAE,EAAE,KAAK,GAAG;AAAA,EACpF;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -40,7 +40,8 @@ const debugFlags = {
|
|
|
40
40
|
forceSrgb: createDebugValue("forceSrgbColors", { defaults: { all: false } }),
|
|
41
41
|
debugGeometry: createDebugValue("debugGeometry", { defaults: { all: false } }),
|
|
42
42
|
hideShapes: createDebugValue("hideShapes", { defaults: { all: false } }),
|
|
43
|
-
editOnType: createDebugValue("editOnType", { defaults: { all: false } })
|
|
43
|
+
editOnType: createDebugValue("editOnType", { defaults: { all: false } }),
|
|
44
|
+
a11y: createDebugValue("a11y", { defaults: { all: false } })
|
|
44
45
|
};
|
|
45
46
|
if (typeof Element !== "undefined") {
|
|
46
47
|
const nativeElementRemoveChild = Element.prototype.removeChild;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/utils/debug-flags.ts"],
|
|
4
|
-
"sourcesContent": ["import { Atom, atom, react } from '@tldraw/state'\nimport { deleteFromSessionStorage, getFromSessionStorage, setInSessionStorage } from '@tldraw/utils'\n\n// --- 1. DEFINE ---\n//\n// Define your debug values and feature flags here. Use `createDebugValue` to\n// create an arbitrary value with defaults for production, staging, and\n// development. Use `createFeatureFlag` to create a boolean flag which will be\n// `true` by default in development and staging, and `false` in production.\n/** @internal */\nexport const featureFlags: Record<string, DebugFlag<boolean>> = {}\n\n/** @internal */\nexport const pointerCaptureTrackingObject = createDebugValue(\n\t'pointerCaptureTrackingObject',\n\t// ideally we wouldn't store this mutable value in an atom but it's not\n\t// a big deal for debug values\n\t{\n\t\tdefaults: { all: new Map<Element, number>() },\n\t\tshouldStoreForSession: false,\n\t}\n)\n\n/** @internal */\nexport const debugFlags = {\n\t// --- DEBUG VALUES ---\n\tlogPreventDefaults: createDebugValue('logPreventDefaults', {\n\t\tdefaults: { all: false },\n\t}),\n\tlogPointerCaptures: createDebugValue('logPointerCaptures', {\n\t\tdefaults: { all: false },\n\t}),\n\tlogElementRemoves: createDebugValue('logElementRemoves', {\n\t\tdefaults: { all: false },\n\t}),\n\tdebugSvg: createDebugValue('debugSvg', {\n\t\tdefaults: { all: false },\n\t}),\n\tshowFps: createDebugValue('showFps', {\n\t\tdefaults: { all: false },\n\t}),\n\tmeasurePerformance: createDebugValue('measurePerformance', { defaults: { all: false } }),\n\tthrowToBlob: createDebugValue('throwToBlob', {\n\t\tdefaults: { all: false },\n\t}),\n\treconnectOnPing: createDebugValue('reconnectOnPing', {\n\t\tdefaults: { all: false },\n\t}),\n\tdebugCursors: createDebugValue('debugCursors', {\n\t\tdefaults: { all: false },\n\t}),\n\tforceSrgb: createDebugValue('forceSrgbColors', { defaults: { all: false } }),\n\tdebugGeometry: createDebugValue('debugGeometry', { defaults: { all: false } }),\n\thideShapes: createDebugValue('hideShapes', { defaults: { all: false } }),\n\teditOnType: createDebugValue('editOnType', { defaults: { all: false } }),\n} as const\n\ndeclare global {\n\tinterface Window {\n\t\ttldrawLog(message: any): void\n\t}\n}\n\n// --- 2. USE ---\n// In normal code, read from debug flags directly by calling .value on them:\n// if (debugFlags.preventDefaultLogging.value) { ... }\n//\n// In react, wrap your reads in `useValue` (or your component in `track`)\n// so they react to changes:\n// const shouldLog = useValue(debugFlags.preventDefaultLogging)\n\n// --- 3. GET FUNKY ---\n// If you need to do fun stuff like monkey-patching in response to flag changes,\n// add that here. Make sure you wrap your code in `react` so it runs\n// automatically when values change!\n\nif (typeof Element !== 'undefined') {\n\tconst nativeElementRemoveChild = Element.prototype.removeChild\n\treact('element removal logging', () => {\n\t\tif (debugFlags.logElementRemoves.get()) {\n\t\t\tElement.prototype.removeChild = function <T extends Node>(this: any, child: Node): T {\n\t\t\t\tconsole.warn('[tldraw] removing child:', child)\n\t\t\t\treturn nativeElementRemoveChild.call(this, child) as T\n\t\t\t}\n\t\t} else {\n\t\t\tElement.prototype.removeChild = nativeElementRemoveChild\n\t\t}\n\t})\n}\n\n// --- IMPLEMENTATION ---\n// you probably don't need to read this if you're just using the debug values system\nfunction createDebugValue<T>(\n\tname: string,\n\t{\n\t\tdefaults,\n\t\tshouldStoreForSession = true,\n\t}: { defaults: DebugFlagDefaults<T>; shouldStoreForSession?: boolean }\n) {\n\treturn createDebugValueBase({\n\t\tname,\n\t\tdefaults,\n\t\tshouldStoreForSession,\n\t})\n}\n\n// function createFeatureFlag<T>(\n// \tname: string,\n// \t{\n// \t\tdefaults,\n// \t\tshouldStoreForSession = true,\n// \t}: { defaults: DebugFlagDefaults<T>; shouldStoreForSession?: boolean }\n// ) {\n// \treturn createDebugValueBase({\n// \t\tname,\n// \t\tdefaults,\n// \t\tshouldStoreForSession,\n// \t})\n// }\n\nfunction createDebugValueBase<T>(def: DebugFlagDef<T>): DebugFlag<T> {\n\tconst defaultValue = getDefaultValue(def)\n\tconst storedValue = def.shouldStoreForSession\n\t\t? (getStoredInitialValue(def.name) as T | null)\n\t\t: null\n\tconst valueAtom = atom(`debug:${def.name}`, storedValue ?? defaultValue)\n\n\tif (typeof window !== 'undefined') {\n\t\tif (def.shouldStoreForSession) {\n\t\t\treact(`debug:${def.name}`, () => {\n\t\t\t\tconst currentValue = valueAtom.get()\n\t\t\t\tif (currentValue === defaultValue) {\n\t\t\t\t\tdeleteFromSessionStorage(`tldraw_debug:${def.name}`)\n\t\t\t\t} else {\n\t\t\t\t\tsetInSessionStorage(`tldraw_debug:${def.name}`, JSON.stringify(currentValue))\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\tObject.defineProperty(window, `tldraw${def.name.replace(/^[a-z]/, (l) => l.toUpperCase())}`, {\n\t\t\tget() {\n\t\t\t\treturn valueAtom.get()\n\t\t\t},\n\t\t\tset(newValue) {\n\t\t\t\tvalueAtom.set(newValue)\n\t\t\t},\n\t\t\tconfigurable: true,\n\t\t})\n\t}\n\n\treturn Object.assign(valueAtom, def)\n}\n\nfunction getStoredInitialValue(name: string) {\n\ttry {\n\t\treturn JSON.parse(getFromSessionStorage(`tldraw_debug:${name}`) ?? 'null')\n\t} catch {\n\t\treturn null\n\t}\n}\n\n// process.env might not be defined, but we can't access it using optional\n// chaining because some bundlers search for `process.env.SOMETHING` as a string\n// and replace it with its value.\nfunction readEnv(fn: () => string | undefined) {\n\ttry {\n\t\treturn fn()\n\t} catch {\n\t\treturn null\n\t}\n}\n\nfunction getDefaultValue<T>(def: DebugFlagDef<T>): T {\n\tconst env =\n\t\treadEnv(() => process.env.TLDRAW_ENV) ??\n\t\treadEnv(() => process.env.VERCEL_PUBLIC_TLDRAW_ENV) ??\n\t\treadEnv(() => process.env.NEXT_PUBLIC_TLDRAW_ENV) ??\n\t\t// default to production because if we don't have one of these, this is probably a library use\n\t\t'production'\n\n\tswitch (env) {\n\t\tcase 'production':\n\t\t\treturn def.defaults.production ?? def.defaults.all\n\t\tcase 'preview':\n\t\tcase 'staging':\n\t\t\treturn def.defaults.staging ?? def.defaults.all\n\t\tdefault:\n\t\t\treturn def.defaults.development ?? def.defaults.all\n\t}\n}\n\n/** @internal */\nexport interface DebugFlagDefaults<T> {\n\tdevelopment?: T\n\tstaging?: T\n\tproduction?: T\n\tall: T\n}\n\n/** @internal */\nexport interface DebugFlagDef<T> {\n\tname: string\n\tdefaults: DebugFlagDefaults<T>\n\tshouldStoreForSession: boolean\n}\n\n/** @internal */\nexport type DebugFlag<T> = DebugFlagDef<T> & Atom<T>\n"],
|
|
5
|
-
"mappings": "AAAA,SAAe,MAAM,aAAa;AAClC,SAAS,0BAA0B,uBAAuB,2BAA2B;AAS9E,MAAM,eAAmD,CAAC;AAG1D,MAAM,+BAA+B;AAAA,EAC3C;AAAA;AAAA;AAAA,EAGA;AAAA,IACC,UAAU,EAAE,KAAK,oBAAI,IAAqB,EAAE;AAAA,IAC5C,uBAAuB;AAAA,EACxB;AACD;AAGO,MAAM,aAAa;AAAA;AAAA,EAEzB,oBAAoB,iBAAiB,sBAAsB;AAAA,IAC1D,UAAU,EAAE,KAAK,MAAM;AAAA,EACxB,CAAC;AAAA,EACD,oBAAoB,iBAAiB,sBAAsB;AAAA,IAC1D,UAAU,EAAE,KAAK,MAAM;AAAA,EACxB,CAAC;AAAA,EACD,mBAAmB,iBAAiB,qBAAqB;AAAA,IACxD,UAAU,EAAE,KAAK,MAAM;AAAA,EACxB,CAAC;AAAA,EACD,UAAU,iBAAiB,YAAY;AAAA,IACtC,UAAU,EAAE,KAAK,MAAM;AAAA,EACxB,CAAC;AAAA,EACD,SAAS,iBAAiB,WAAW;AAAA,IACpC,UAAU,EAAE,KAAK,MAAM;AAAA,EACxB,CAAC;AAAA,EACD,oBAAoB,iBAAiB,sBAAsB,EAAE,UAAU,EAAE,KAAK,MAAM,EAAE,CAAC;AAAA,EACvF,aAAa,iBAAiB,eAAe;AAAA,IAC5C,UAAU,EAAE,KAAK,MAAM;AAAA,EACxB,CAAC;AAAA,EACD,iBAAiB,iBAAiB,mBAAmB;AAAA,IACpD,UAAU,EAAE,KAAK,MAAM;AAAA,EACxB,CAAC;AAAA,EACD,cAAc,iBAAiB,gBAAgB;AAAA,IAC9C,UAAU,EAAE,KAAK,MAAM;AAAA,EACxB,CAAC;AAAA,EACD,WAAW,iBAAiB,mBAAmB,EAAE,UAAU,EAAE,KAAK,MAAM,EAAE,CAAC;AAAA,EAC3E,eAAe,iBAAiB,iBAAiB,EAAE,UAAU,EAAE,KAAK,MAAM,EAAE,CAAC;AAAA,EAC7E,YAAY,iBAAiB,cAAc,EAAE,UAAU,EAAE,KAAK,MAAM,EAAE,CAAC;AAAA,EACvE,YAAY,iBAAiB,cAAc,EAAE,UAAU,EAAE,KAAK,MAAM,EAAE,CAAC;
|
|
4
|
+
"sourcesContent": ["import { Atom, atom, react } from '@tldraw/state'\nimport { deleteFromSessionStorage, getFromSessionStorage, setInSessionStorage } from '@tldraw/utils'\n\n// --- 1. DEFINE ---\n//\n// Define your debug values and feature flags here. Use `createDebugValue` to\n// create an arbitrary value with defaults for production, staging, and\n// development. Use `createFeatureFlag` to create a boolean flag which will be\n// `true` by default in development and staging, and `false` in production.\n/** @internal */\nexport const featureFlags: Record<string, DebugFlag<boolean>> = {}\n\n/** @internal */\nexport const pointerCaptureTrackingObject = createDebugValue(\n\t'pointerCaptureTrackingObject',\n\t// ideally we wouldn't store this mutable value in an atom but it's not\n\t// a big deal for debug values\n\t{\n\t\tdefaults: { all: new Map<Element, number>() },\n\t\tshouldStoreForSession: false,\n\t}\n)\n\n/** @internal */\nexport const debugFlags = {\n\t// --- DEBUG VALUES ---\n\tlogPreventDefaults: createDebugValue('logPreventDefaults', {\n\t\tdefaults: { all: false },\n\t}),\n\tlogPointerCaptures: createDebugValue('logPointerCaptures', {\n\t\tdefaults: { all: false },\n\t}),\n\tlogElementRemoves: createDebugValue('logElementRemoves', {\n\t\tdefaults: { all: false },\n\t}),\n\tdebugSvg: createDebugValue('debugSvg', {\n\t\tdefaults: { all: false },\n\t}),\n\tshowFps: createDebugValue('showFps', {\n\t\tdefaults: { all: false },\n\t}),\n\tmeasurePerformance: createDebugValue('measurePerformance', { defaults: { all: false } }),\n\tthrowToBlob: createDebugValue('throwToBlob', {\n\t\tdefaults: { all: false },\n\t}),\n\treconnectOnPing: createDebugValue('reconnectOnPing', {\n\t\tdefaults: { all: false },\n\t}),\n\tdebugCursors: createDebugValue('debugCursors', {\n\t\tdefaults: { all: false },\n\t}),\n\tforceSrgb: createDebugValue('forceSrgbColors', { defaults: { all: false } }),\n\tdebugGeometry: createDebugValue('debugGeometry', { defaults: { all: false } }),\n\thideShapes: createDebugValue('hideShapes', { defaults: { all: false } }),\n\teditOnType: createDebugValue('editOnType', { defaults: { all: false } }),\n\ta11y: createDebugValue('a11y', { defaults: { all: false } }),\n} as const\n\ndeclare global {\n\tinterface Window {\n\t\ttldrawLog(message: any): void\n\t}\n}\n\n// --- 2. USE ---\n// In normal code, read from debug flags directly by calling .value on them:\n// if (debugFlags.preventDefaultLogging.value) { ... }\n//\n// In react, wrap your reads in `useValue` (or your component in `track`)\n// so they react to changes:\n// const shouldLog = useValue(debugFlags.preventDefaultLogging)\n\n// --- 3. GET FUNKY ---\n// If you need to do fun stuff like monkey-patching in response to flag changes,\n// add that here. Make sure you wrap your code in `react` so it runs\n// automatically when values change!\n\nif (typeof Element !== 'undefined') {\n\tconst nativeElementRemoveChild = Element.prototype.removeChild\n\treact('element removal logging', () => {\n\t\tif (debugFlags.logElementRemoves.get()) {\n\t\t\tElement.prototype.removeChild = function <T extends Node>(this: any, child: Node): T {\n\t\t\t\tconsole.warn('[tldraw] removing child:', child)\n\t\t\t\treturn nativeElementRemoveChild.call(this, child) as T\n\t\t\t}\n\t\t} else {\n\t\t\tElement.prototype.removeChild = nativeElementRemoveChild\n\t\t}\n\t})\n}\n\n// --- IMPLEMENTATION ---\n// you probably don't need to read this if you're just using the debug values system\nfunction createDebugValue<T>(\n\tname: string,\n\t{\n\t\tdefaults,\n\t\tshouldStoreForSession = true,\n\t}: { defaults: DebugFlagDefaults<T>; shouldStoreForSession?: boolean }\n) {\n\treturn createDebugValueBase({\n\t\tname,\n\t\tdefaults,\n\t\tshouldStoreForSession,\n\t})\n}\n\n// function createFeatureFlag<T>(\n// \tname: string,\n// \t{\n// \t\tdefaults,\n// \t\tshouldStoreForSession = true,\n// \t}: { defaults: DebugFlagDefaults<T>; shouldStoreForSession?: boolean }\n// ) {\n// \treturn createDebugValueBase({\n// \t\tname,\n// \t\tdefaults,\n// \t\tshouldStoreForSession,\n// \t})\n// }\n\nfunction createDebugValueBase<T>(def: DebugFlagDef<T>): DebugFlag<T> {\n\tconst defaultValue = getDefaultValue(def)\n\tconst storedValue = def.shouldStoreForSession\n\t\t? (getStoredInitialValue(def.name) as T | null)\n\t\t: null\n\tconst valueAtom = atom(`debug:${def.name}`, storedValue ?? defaultValue)\n\n\tif (typeof window !== 'undefined') {\n\t\tif (def.shouldStoreForSession) {\n\t\t\treact(`debug:${def.name}`, () => {\n\t\t\t\tconst currentValue = valueAtom.get()\n\t\t\t\tif (currentValue === defaultValue) {\n\t\t\t\t\tdeleteFromSessionStorage(`tldraw_debug:${def.name}`)\n\t\t\t\t} else {\n\t\t\t\t\tsetInSessionStorage(`tldraw_debug:${def.name}`, JSON.stringify(currentValue))\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\tObject.defineProperty(window, `tldraw${def.name.replace(/^[a-z]/, (l) => l.toUpperCase())}`, {\n\t\t\tget() {\n\t\t\t\treturn valueAtom.get()\n\t\t\t},\n\t\t\tset(newValue) {\n\t\t\t\tvalueAtom.set(newValue)\n\t\t\t},\n\t\t\tconfigurable: true,\n\t\t})\n\t}\n\n\treturn Object.assign(valueAtom, def)\n}\n\nfunction getStoredInitialValue(name: string) {\n\ttry {\n\t\treturn JSON.parse(getFromSessionStorage(`tldraw_debug:${name}`) ?? 'null')\n\t} catch {\n\t\treturn null\n\t}\n}\n\n// process.env might not be defined, but we can't access it using optional\n// chaining because some bundlers search for `process.env.SOMETHING` as a string\n// and replace it with its value.\nfunction readEnv(fn: () => string | undefined) {\n\ttry {\n\t\treturn fn()\n\t} catch {\n\t\treturn null\n\t}\n}\n\nfunction getDefaultValue<T>(def: DebugFlagDef<T>): T {\n\tconst env =\n\t\treadEnv(() => process.env.TLDRAW_ENV) ??\n\t\treadEnv(() => process.env.VERCEL_PUBLIC_TLDRAW_ENV) ??\n\t\treadEnv(() => process.env.NEXT_PUBLIC_TLDRAW_ENV) ??\n\t\t// default to production because if we don't have one of these, this is probably a library use\n\t\t'production'\n\n\tswitch (env) {\n\t\tcase 'production':\n\t\t\treturn def.defaults.production ?? def.defaults.all\n\t\tcase 'preview':\n\t\tcase 'staging':\n\t\t\treturn def.defaults.staging ?? def.defaults.all\n\t\tdefault:\n\t\t\treturn def.defaults.development ?? def.defaults.all\n\t}\n}\n\n/** @internal */\nexport interface DebugFlagDefaults<T> {\n\tdevelopment?: T\n\tstaging?: T\n\tproduction?: T\n\tall: T\n}\n\n/** @internal */\nexport interface DebugFlagDef<T> {\n\tname: string\n\tdefaults: DebugFlagDefaults<T>\n\tshouldStoreForSession: boolean\n}\n\n/** @internal */\nexport type DebugFlag<T> = DebugFlagDef<T> & Atom<T>\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/version.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const version = "3.12.0-canary.
|
|
1
|
+
const version = "3.12.0-canary.5fcdc0f29d81";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2024-09-13T14:36:29.063Z",
|
|
4
|
-
minor: "2025-03-
|
|
5
|
-
patch: "2025-03-
|
|
4
|
+
minor: "2025-03-28T16:17:55.091Z",
|
|
5
|
+
patch: "2025-03-28T16:17:55.091Z"
|
|
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.12.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.12.0-canary.5fcdc0f29d81'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-03-28T16:17:55.091Z',\n\tpatch: '2025-03-28T16:17:55.091Z',\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
|
@@ -897,16 +897,17 @@ input,
|
|
|
897
897
|
cursor: var(--tl-cursor-default);
|
|
898
898
|
}
|
|
899
899
|
|
|
900
|
+
.tl-rich-text[data-is-ready-for-editing='true'],
|
|
901
|
+
.tl-text-wrapper[data-is-ready-for-editing='true'] .tl-text-input {
|
|
902
|
+
cursor: var(--tl-cursor-text);
|
|
903
|
+
}
|
|
904
|
+
|
|
900
905
|
.tl-text-input::selection {
|
|
901
906
|
background: var(--color-selected);
|
|
902
907
|
color: var(--color-selected-contrast);
|
|
903
908
|
text-shadow: none;
|
|
904
909
|
}
|
|
905
910
|
|
|
906
|
-
.tl-rich-text[data-iseditinganything='true'] {
|
|
907
|
-
cursor: var(--tl-cursor-text);
|
|
908
|
-
}
|
|
909
|
-
|
|
910
911
|
.tl-rich-text .ProseMirror {
|
|
911
912
|
word-wrap: break-word;
|
|
912
913
|
overflow-wrap: break-word;
|
|
@@ -1421,13 +1422,17 @@ input,
|
|
|
1421
1422
|
}
|
|
1422
1423
|
|
|
1423
1424
|
.tl-frame-heading {
|
|
1425
|
+
--frame-padding-x: 6px;
|
|
1426
|
+
--frame-height: 24px;
|
|
1427
|
+
--frame-minimum-width: 32px;
|
|
1428
|
+
--frame-offset-width: 16px;
|
|
1424
1429
|
display: flex;
|
|
1425
1430
|
align-items: center;
|
|
1426
1431
|
position: absolute;
|
|
1427
1432
|
transform-origin: 0% 100%;
|
|
1428
1433
|
overflow: hidden;
|
|
1429
1434
|
max-width: 100%;
|
|
1430
|
-
min-width:
|
|
1435
|
+
min-width: var(--frame-minimum-width);
|
|
1431
1436
|
height: auto;
|
|
1432
1437
|
font-size: 12px;
|
|
1433
1438
|
padding-bottom: 4px;
|
|
@@ -1439,32 +1444,33 @@ input,
|
|
|
1439
1444
|
/* scale from bottom left corner so we can pin it to the top left corner of the frame */
|
|
1440
1445
|
transform-origin: 0% 100%;
|
|
1441
1446
|
display: flex;
|
|
1442
|
-
height:
|
|
1447
|
+
height: var(--frame-height);
|
|
1443
1448
|
width: 100%;
|
|
1444
1449
|
align-items: center;
|
|
1445
1450
|
border-radius: var(--radius-1);
|
|
1446
|
-
background-color: var(--color-background);
|
|
1447
1451
|
}
|
|
1448
1452
|
|
|
1449
1453
|
.tl-frame-label {
|
|
1450
1454
|
pointer-events: all;
|
|
1451
1455
|
overflow: hidden;
|
|
1452
1456
|
text-overflow: ellipsis;
|
|
1453
|
-
padding:
|
|
1457
|
+
padding: 0px var(--frame-padding-x);
|
|
1458
|
+
border-radius: var(--radius-1);
|
|
1454
1459
|
position: relative;
|
|
1455
1460
|
font-size: inherit;
|
|
1456
1461
|
white-space: pre;
|
|
1457
|
-
border: 1px solid transparent;
|
|
1458
1462
|
}
|
|
1459
1463
|
|
|
1460
1464
|
.tl-frame-label__editing {
|
|
1461
1465
|
color: transparent;
|
|
1462
1466
|
white-space: pre;
|
|
1463
1467
|
width: auto;
|
|
1468
|
+
min-width: var(--frame-minimum-width);
|
|
1469
|
+
height: 100%;
|
|
1464
1470
|
overflow: visible;
|
|
1465
1471
|
background-color: var(--color-panel);
|
|
1466
|
-
border-radius: var(--radius-1);
|
|
1467
1472
|
border-color: var(--color-selected);
|
|
1473
|
+
box-shadow: inset 0px 0px 0px 1.5px var(--color-selected);
|
|
1468
1474
|
}
|
|
1469
1475
|
|
|
1470
1476
|
.tl-frame-name-input {
|
|
@@ -1472,7 +1478,7 @@ input,
|
|
|
1472
1478
|
border: none;
|
|
1473
1479
|
background: none;
|
|
1474
1480
|
outline: none;
|
|
1475
|
-
padding:
|
|
1481
|
+
padding: 0px var(--frame-padding-x);
|
|
1476
1482
|
inset: 0px;
|
|
1477
1483
|
height: 100%;
|
|
1478
1484
|
width: 100%;
|
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.12.0-canary.
|
|
4
|
+
"version": "3.12.0-canary.5fcdc0f29d81",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"@tiptap/core": "^2.9.1",
|
|
49
49
|
"@tiptap/pm": "^2.9.1",
|
|
50
50
|
"@tiptap/react": "^2.9.1",
|
|
51
|
-
"@tldraw/state": "3.12.0-canary.
|
|
52
|
-
"@tldraw/state-react": "3.12.0-canary.
|
|
53
|
-
"@tldraw/store": "3.12.0-canary.
|
|
54
|
-
"@tldraw/tlschema": "3.12.0-canary.
|
|
55
|
-
"@tldraw/utils": "3.12.0-canary.
|
|
56
|
-
"@tldraw/validate": "3.12.0-canary.
|
|
51
|
+
"@tldraw/state": "3.12.0-canary.5fcdc0f29d81",
|
|
52
|
+
"@tldraw/state-react": "3.12.0-canary.5fcdc0f29d81",
|
|
53
|
+
"@tldraw/store": "3.12.0-canary.5fcdc0f29d81",
|
|
54
|
+
"@tldraw/tlschema": "3.12.0-canary.5fcdc0f29d81",
|
|
55
|
+
"@tldraw/utils": "3.12.0-canary.5fcdc0f29d81",
|
|
56
|
+
"@tldraw/validate": "3.12.0-canary.5fcdc0f29d81",
|
|
57
57
|
"@types/core-js": "^2.5.8",
|
|
58
58
|
"@use-gesture/react": "^10.3.1",
|
|
59
59
|
"classnames": "^2.5.1",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { react } from '@tldraw/state'
|
|
2
2
|
import { useQuickReactor, useStateTracking } from '@tldraw/state-react'
|
|
3
3
|
import { TLShape, TLShapeId } from '@tldraw/tlschema'
|
|
4
|
-
import { memo, useCallback, useEffect, useRef } from 'react'
|
|
4
|
+
import { memo, useCallback, useEffect, useLayoutEffect, useRef } from 'react'
|
|
5
5
|
import { ShapeUtil } from '../editor/shapes/ShapeUtil'
|
|
6
6
|
import { useEditor } from '../hooks/useEditor'
|
|
7
7
|
import { useEditorComponents } from '../hooks/useEditorComponents'
|
|
@@ -44,10 +44,10 @@ export const Shape = memo(function Shape({
|
|
|
44
44
|
|
|
45
45
|
useEffect(() => {
|
|
46
46
|
return react('load fonts', () => {
|
|
47
|
-
const fonts = editor.fonts.getShapeFontFaces(
|
|
47
|
+
const fonts = editor.fonts.getShapeFontFaces(id)
|
|
48
48
|
editor.fonts.requestFonts(fonts)
|
|
49
49
|
})
|
|
50
|
-
}, [editor,
|
|
50
|
+
}, [editor, id])
|
|
51
51
|
|
|
52
52
|
const memoizedStuffRef = useRef({
|
|
53
53
|
transform: '',
|
|
@@ -104,22 +104,18 @@ export const Shape = memo(function Shape({
|
|
|
104
104
|
)
|
|
105
105
|
|
|
106
106
|
// This stuff changes pretty infrequently, so we can change them together
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
setStyleProperty(bgContainer, 'z-index', backgroundIndex)
|
|
120
|
-
},
|
|
121
|
-
[opacity, index, backgroundIndex]
|
|
122
|
-
)
|
|
107
|
+
useLayoutEffect(() => {
|
|
108
|
+
const container = containerRef.current
|
|
109
|
+
const bgContainer = bgContainerRef.current
|
|
110
|
+
|
|
111
|
+
// Opacity
|
|
112
|
+
setStyleProperty(container, 'opacity', opacity)
|
|
113
|
+
setStyleProperty(bgContainer, 'opacity', opacity)
|
|
114
|
+
|
|
115
|
+
// Z-Index
|
|
116
|
+
setStyleProperty(container, 'z-index', index)
|
|
117
|
+
setStyleProperty(bgContainer, 'z-index', backgroundIndex)
|
|
118
|
+
}, [opacity, index, backgroundIndex])
|
|
123
119
|
|
|
124
120
|
useQuickReactor(
|
|
125
121
|
'set display',
|