@tldraw/editor 3.8.0-canary.962422478a23 → 3.8.0-canary.98976dbbb3dd
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 +167 -12
- package/dist-cjs/index.js +7 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +2 -5
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/config/createTLStore.js +3 -1
- package/dist-cjs/lib/config/createTLStore.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +82 -19
- package/dist-cjs/lib/editor/Editor.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/SvgExportContext.js.map +2 -2
- package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
- package/dist-cjs/lib/exports/exportToSvg.js.map +2 -2
- package/dist-cjs/lib/exports/getSvgAsImage.js +83 -0
- package/dist-cjs/lib/exports/getSvgAsImage.js.map +7 -0
- package/dist-cjs/lib/exports/getSvgJsx.js +16 -3
- package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
- package/dist-cjs/lib/hooks/useLocalStore.js +1 -1
- package/dist-cjs/lib/hooks/useLocalStore.js.map +2 -2
- package/dist-cjs/lib/options.js +2 -1
- package/dist-cjs/lib/options.js.map +2 -2
- package/dist-cjs/lib/utils/browserCanvasMaxSize.js +75 -0
- package/dist-cjs/lib/utils/browserCanvasMaxSize.js.map +7 -0
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +167 -12
- package/dist-esm/index.mjs +7 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +2 -5
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/config/createTLStore.mjs +3 -1
- package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +82 -19
- package/dist-esm/lib/editor/Editor.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/editor/types/SvgExportContext.mjs.map +2 -2
- package/dist-esm/lib/exports/exportToSvg.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgAsImage.mjs +63 -0
- package/dist-esm/lib/exports/getSvgAsImage.mjs.map +7 -0
- package/dist-esm/lib/exports/getSvgJsx.mjs +16 -3
- package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
- package/dist-esm/lib/hooks/useLocalStore.mjs +1 -1
- package/dist-esm/lib/hooks/useLocalStore.mjs.map +2 -2
- package/dist-esm/lib/options.mjs +2 -1
- package/dist-esm/lib/options.mjs.map +2 -2
- package/dist-esm/lib/utils/browserCanvasMaxSize.mjs +45 -0
- package/dist-esm/lib/utils/browserCanvasMaxSize.mjs.map +7 -0
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +2 -1
- package/package.json +9 -7
- package/src/index.ts +6 -0
- package/src/lib/components/default-components/DefaultCanvas.tsx +2 -5
- package/src/lib/config/createTLStore.ts +3 -1
- package/src/lib/editor/Editor.ts +116 -26
- 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/SvgExportContext.tsx +21 -0
- package/src/lib/editor/types/misc-types.ts +55 -2
- package/src/lib/exports/exportToSvg.tsx +2 -2
- package/src/lib/exports/getSvgAsImage.ts +92 -0
- package/src/lib/exports/getSvgJsx.tsx +17 -2
- package/src/lib/hooks/useLocalStore.ts +1 -1
- package/src/lib/options.ts +6 -0
- package/src/lib/utils/browserCanvasMaxSize.ts +65 -0
- package/src/version.ts +3 -3
package/dist-esm/lib/options.mjs
CHANGED
|
@@ -42,7 +42,8 @@ const defaultTldrawOptions = {
|
|
|
42
42
|
temporaryAssetPreviewLifetimeMs: 18e4,
|
|
43
43
|
actionShortcutsLocation: "swap",
|
|
44
44
|
createTextOnCanvasDoubleClick: true,
|
|
45
|
-
exportProvider: Fragment
|
|
45
|
+
exportProvider: Fragment,
|
|
46
|
+
noteShapeResizeMode: "none"
|
|
46
47
|
};
|
|
47
48
|
export {
|
|
48
49
|
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 maxPointsPerDrawShape: 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}\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\tmaxPointsPerDrawShape: 500,\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} as const satisfies TldrawOptions\n"],
|
|
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 maxPointsPerDrawShape: 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 * How should the note shape resize? By default it does not resize (except automatically based on its text content),\n\t * but you can set it to be user-resizable using scale.\n\t */\n\treadonly noteShapeResizeMode: 'none' | 'scale'\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\tmaxPointsPerDrawShape: 500,\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\tnoteShapeResizeMode: 'none',\n} as const satisfies TldrawOptions\n"],
|
|
5
|
+
"mappings": "AAAA,SAAwB,gBAAgB;AA4EjC,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,uBAAuB;AAAA,EACvB,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,qBAAqB;AACtB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import canvasSize from "canvas-size";
|
|
2
|
+
let maxSizePromise = null;
|
|
3
|
+
function getBrowserCanvasMaxSize() {
|
|
4
|
+
if (!maxSizePromise) {
|
|
5
|
+
maxSizePromise = calculateBrowserCanvasMaxSize();
|
|
6
|
+
}
|
|
7
|
+
return maxSizePromise;
|
|
8
|
+
}
|
|
9
|
+
async function calculateBrowserCanvasMaxSize() {
|
|
10
|
+
const maxWidth = await canvasSize.maxWidth({ usePromise: true });
|
|
11
|
+
const maxHeight = await canvasSize.maxHeight({ usePromise: true });
|
|
12
|
+
const maxArea = await canvasSize.maxArea({ usePromise: true });
|
|
13
|
+
return {
|
|
14
|
+
maxWidth: maxWidth.width,
|
|
15
|
+
maxHeight: maxHeight.height,
|
|
16
|
+
maxArea: maxArea.width * maxArea.height
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const MAX_SAFE_CANVAS_DIMENSION = 8192;
|
|
20
|
+
const MAX_SAFE_CANVAS_AREA = 4096 * 4096;
|
|
21
|
+
async function clampToBrowserMaxCanvasSize(width, height) {
|
|
22
|
+
if (width <= MAX_SAFE_CANVAS_DIMENSION && height <= MAX_SAFE_CANVAS_DIMENSION && width * height <= MAX_SAFE_CANVAS_AREA) {
|
|
23
|
+
return [width, height];
|
|
24
|
+
}
|
|
25
|
+
const { maxWidth, maxHeight, maxArea } = await getBrowserCanvasMaxSize();
|
|
26
|
+
const aspectRatio = width / height;
|
|
27
|
+
if (width > maxWidth) {
|
|
28
|
+
width = maxWidth;
|
|
29
|
+
height = width / aspectRatio;
|
|
30
|
+
}
|
|
31
|
+
if (height > maxHeight) {
|
|
32
|
+
height = maxHeight;
|
|
33
|
+
width = height * aspectRatio;
|
|
34
|
+
}
|
|
35
|
+
if (width * height > maxArea) {
|
|
36
|
+
const ratio = Math.sqrt(maxArea / (width * height));
|
|
37
|
+
width *= ratio;
|
|
38
|
+
height *= ratio;
|
|
39
|
+
}
|
|
40
|
+
return [width, height];
|
|
41
|
+
}
|
|
42
|
+
export {
|
|
43
|
+
clampToBrowserMaxCanvasSize
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=browserCanvasMaxSize.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/utils/browserCanvasMaxSize.ts"],
|
|
4
|
+
"sourcesContent": ["import canvasSize from 'canvas-size'\n\n/** @internal */\nexport interface CanvasMaxSize {\n\tmaxWidth: number\n\tmaxHeight: number\n\tmaxArea: number\n}\n\nlet maxSizePromise: Promise<CanvasMaxSize> | null = null\n\nfunction getBrowserCanvasMaxSize() {\n\tif (!maxSizePromise) {\n\t\tmaxSizePromise = calculateBrowserCanvasMaxSize()\n\t}\n\n\treturn maxSizePromise\n}\n\nasync function calculateBrowserCanvasMaxSize(): Promise<CanvasMaxSize> {\n\tconst maxWidth = await canvasSize.maxWidth({ usePromise: true })\n\tconst maxHeight = await canvasSize.maxHeight({ usePromise: true })\n\tconst maxArea = await canvasSize.maxArea({ usePromise: true })\n\treturn {\n\t\tmaxWidth: maxWidth.width,\n\t\tmaxHeight: maxHeight.height,\n\t\tmaxArea: maxArea.width * maxArea.height,\n\t}\n}\n\n// https://github.com/jhildenbiddle/canvas-size?tab=readme-ov-file#test-results\nconst MAX_SAFE_CANVAS_DIMENSION = 8192\nconst MAX_SAFE_CANVAS_AREA = 4096 * 4096\n\n/** @internal */\nexport async function clampToBrowserMaxCanvasSize(width: number, height: number) {\n\tif (\n\t\twidth <= MAX_SAFE_CANVAS_DIMENSION &&\n\t\theight <= MAX_SAFE_CANVAS_DIMENSION &&\n\t\twidth * height <= MAX_SAFE_CANVAS_AREA\n\t) {\n\t\treturn [width, height]\n\t}\n\n\tconst { maxWidth, maxHeight, maxArea } = await getBrowserCanvasMaxSize()\n\tconst aspectRatio = width / height\n\n\tif (width > maxWidth) {\n\t\twidth = maxWidth\n\t\theight = width / aspectRatio\n\t}\n\n\tif (height > maxHeight) {\n\t\theight = maxHeight\n\t\twidth = height * aspectRatio\n\t}\n\n\tif (width * height > maxArea) {\n\t\tconst ratio = Math.sqrt(maxArea / (width * height))\n\t\twidth *= ratio\n\t\theight *= ratio\n\t}\n\n\treturn [width, height]\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,gBAAgB;AASvB,IAAI,iBAAgD;AAEpD,SAAS,0BAA0B;AAClC,MAAI,CAAC,gBAAgB;AACpB,qBAAiB,8BAA8B;AAAA,EAChD;AAEA,SAAO;AACR;AAEA,eAAe,gCAAwD;AACtE,QAAM,WAAW,MAAM,WAAW,SAAS,EAAE,YAAY,KAAK,CAAC;AAC/D,QAAM,YAAY,MAAM,WAAW,UAAU,EAAE,YAAY,KAAK,CAAC;AACjE,QAAM,UAAU,MAAM,WAAW,QAAQ,EAAE,YAAY,KAAK,CAAC;AAC7D,SAAO;AAAA,IACN,UAAU,SAAS;AAAA,IACnB,WAAW,UAAU;AAAA,IACrB,SAAS,QAAQ,QAAQ,QAAQ;AAAA,EAClC;AACD;AAGA,MAAM,4BAA4B;AAClC,MAAM,uBAAuB,OAAO;AAGpC,eAAsB,4BAA4B,OAAe,QAAgB;AAChF,MACC,SAAS,6BACT,UAAU,6BACV,QAAQ,UAAU,sBACjB;AACD,WAAO,CAAC,OAAO,MAAM;AAAA,EACtB;AAEA,QAAM,EAAE,UAAU,WAAW,QAAQ,IAAI,MAAM,wBAAwB;AACvE,QAAM,cAAc,QAAQ;AAE5B,MAAI,QAAQ,UAAU;AACrB,YAAQ;AACR,aAAS,QAAQ;AAAA,EAClB;AAEA,MAAI,SAAS,WAAW;AACvB,aAAS;AACT,YAAQ,SAAS;AAAA,EAClB;AAEA,MAAI,QAAQ,SAAS,SAAS;AAC7B,UAAM,QAAQ,KAAK,KAAK,WAAW,QAAQ,OAAO;AAClD,aAAS;AACT,cAAU;AAAA,EACX;AAEA,SAAO,CAAC,OAAO,MAAM;AACtB;",
|
|
6
|
+
"names": []
|
|
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.98976dbbb3dd";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2024-09-13T14:36:29.063Z",
|
|
4
|
-
minor: "2025-01-
|
|
5
|
-
patch: "2025-01-
|
|
4
|
+
minor: "2025-01-29T12:08:39.683Z",
|
|
5
|
+
patch: "2025-01-29T12:08:39.683Z"
|
|
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.98976dbbb3dd'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-01-29T12:08:39.683Z',\n\tpatch: '2025-01-29T12:08:39.683Z',\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.98976dbbb3dd",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -45,14 +45,15 @@
|
|
|
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.
|
|
48
|
+
"@tldraw/state": "3.8.0-canary.98976dbbb3dd",
|
|
49
|
+
"@tldraw/state-react": "3.8.0-canary.98976dbbb3dd",
|
|
50
|
+
"@tldraw/store": "3.8.0-canary.98976dbbb3dd",
|
|
51
|
+
"@tldraw/tlschema": "3.8.0-canary.98976dbbb3dd",
|
|
52
|
+
"@tldraw/utils": "3.8.0-canary.98976dbbb3dd",
|
|
53
|
+
"@tldraw/validate": "3.8.0-canary.98976dbbb3dd",
|
|
54
54
|
"@types/core-js": "^2.5.5",
|
|
55
55
|
"@use-gesture/react": "^10.2.27",
|
|
56
|
+
"canvas-size": "~2.0.0",
|
|
56
57
|
"classnames": "^2.3.2",
|
|
57
58
|
"core-js": "^3.31.1",
|
|
58
59
|
"eventemitter3": "^4.0.7",
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
"@testing-library/jest-dom": "^5.16.5",
|
|
70
71
|
"@testing-library/react": "^15.0.6",
|
|
71
72
|
"@types/benchmark": "^2.1.2",
|
|
73
|
+
"@types/canvas-size": "^1.2.2",
|
|
72
74
|
"@types/wicg-file-system-access": "^2020.9.5",
|
|
73
75
|
"benchmark": "^2.1.4",
|
|
74
76
|
"fake-indexeddb": "^4.0.0",
|
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,
|
|
@@ -254,10 +256,13 @@ export {
|
|
|
254
256
|
type TLCameraConstraints,
|
|
255
257
|
type TLCameraMoveOptions,
|
|
256
258
|
type TLCameraOptions,
|
|
259
|
+
type TLExportType,
|
|
257
260
|
type TLImageExportOptions,
|
|
261
|
+
type TLSvgExportOptions,
|
|
258
262
|
type TLSvgOptions,
|
|
259
263
|
} from './lib/editor/types/misc-types'
|
|
260
264
|
export { type TLResizeHandle, type TLSelectionHandle } from './lib/editor/types/selection-types'
|
|
265
|
+
export { getSvgAsImage } from './lib/exports/getSvgAsImage'
|
|
261
266
|
export { tlenv } from './lib/globals/environment'
|
|
262
267
|
export { tlmenus } from './lib/globals/menus'
|
|
263
268
|
export { tltime } from './lib/globals/time'
|
|
@@ -381,6 +386,7 @@ export {
|
|
|
381
386
|
type SharedStyle,
|
|
382
387
|
} from './lib/utils/SharedStylesMap'
|
|
383
388
|
export { dataUrlToFile, getDefaultCdnBaseUrl } from './lib/utils/assets'
|
|
389
|
+
export { clampToBrowserMaxCanvasSize, type CanvasMaxSize } from './lib/utils/browserCanvasMaxSize'
|
|
384
390
|
export {
|
|
385
391
|
debugFlags,
|
|
386
392
|
featureFlags,
|
|
@@ -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>
|
|
@@ -485,10 +485,7 @@ function DebugSvgCopy({ id, mode }: { id: TLShapeId; mode: 'img' | 'iframe' }) {
|
|
|
485
485
|
if (!bounds) return
|
|
486
486
|
bounds = bounds.clone().expandBy(padding)
|
|
487
487
|
|
|
488
|
-
const result = await editor.getSvgString([id], {
|
|
489
|
-
padding,
|
|
490
|
-
background: editor.getInstanceState().exportBackground,
|
|
491
|
-
})
|
|
488
|
+
const result = await editor.getSvgString([id], { padding })
|
|
492
489
|
|
|
493
490
|
if (latest !== renderId || !result) return
|
|
494
491
|
|
|
@@ -61,7 +61,9 @@ const defaultAssetResolve: NonNullable<TLAssetStore['resolve']> = (asset) => ass
|
|
|
61
61
|
|
|
62
62
|
/** @public */
|
|
63
63
|
export const inlineBase64AssetStore: TLAssetStore = {
|
|
64
|
-
upload: (_, file) =>
|
|
64
|
+
upload: async (_, file) => {
|
|
65
|
+
return { src: await FileHelpers.blobToDataUrl(file) }
|
|
66
|
+
},
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
/**
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -104,6 +104,7 @@ import {
|
|
|
104
104
|
ZOOM_TO_FIT_PADDING,
|
|
105
105
|
} from '../constants'
|
|
106
106
|
import { exportToSvg } from '../exports/exportToSvg'
|
|
107
|
+
import { getSvgAsImage } from '../exports/getSvgAsImage'
|
|
107
108
|
import { tlenv } from '../globals/environment'
|
|
108
109
|
import { tlmenus } from '../globals/menus'
|
|
109
110
|
import { tltime } from '../globals/time'
|
|
@@ -162,6 +163,7 @@ import {
|
|
|
162
163
|
TLCameraMoveOptions,
|
|
163
164
|
TLCameraOptions,
|
|
164
165
|
TLImageExportOptions,
|
|
166
|
+
TLSvgExportOptions,
|
|
165
167
|
} from './types/misc-types'
|
|
166
168
|
import { TLResizeHandle } from './types/selection-types'
|
|
167
169
|
|
|
@@ -927,6 +929,21 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
927
929
|
return shapeUtil
|
|
928
930
|
}
|
|
929
931
|
|
|
932
|
+
/**
|
|
933
|
+
* Returns true if the editor has a shape util for the given shape / shape type.
|
|
934
|
+
*
|
|
935
|
+
* @param shape - A shape, shape partial, or shape type.
|
|
936
|
+
*/
|
|
937
|
+
hasShapeUtil<S extends TLUnknownShape>(shape: S | TLShapePartial<S>): boolean
|
|
938
|
+
hasShapeUtil<S extends TLUnknownShape>(type: S['type']): boolean
|
|
939
|
+
hasShapeUtil<T extends ShapeUtil>(
|
|
940
|
+
type: T extends ShapeUtil<infer R> ? R['type'] : string
|
|
941
|
+
): boolean
|
|
942
|
+
hasShapeUtil(arg: string | { type: string }): boolean {
|
|
943
|
+
const type = typeof arg === 'string' ? arg : arg.type
|
|
944
|
+
return hasOwnProperty(this.shapeUtils, type)
|
|
945
|
+
}
|
|
946
|
+
|
|
930
947
|
/* ------------------- Binding Utils ------------------ */
|
|
931
948
|
/**
|
|
932
949
|
* A map of shape utility classes (TLShapeUtils) by shape type.
|
|
@@ -1458,10 +1475,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1458
1475
|
if (partial.isChangingStyle !== undefined) {
|
|
1459
1476
|
clearTimeout(this._isChangingStyleTimeout)
|
|
1460
1477
|
if (partial.isChangingStyle === true) {
|
|
1461
|
-
// If we've set to true, set a new reset timeout to change the value back to false after
|
|
1478
|
+
// If we've set to true, set a new reset timeout to change the value back to false after 1 seconds
|
|
1462
1479
|
this._isChangingStyleTimeout = this.timers.setTimeout(() => {
|
|
1463
1480
|
this._updateInstanceState({ isChangingStyle: false }, { history: 'ignore' })
|
|
1464
|
-
},
|
|
1481
|
+
}, 1000)
|
|
1465
1482
|
}
|
|
1466
1483
|
}
|
|
1467
1484
|
|
|
@@ -4145,20 +4162,24 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4145
4162
|
context: {
|
|
4146
4163
|
screenScale?: number
|
|
4147
4164
|
shouldResolveToOriginal?: boolean
|
|
4165
|
+
dpr?: number
|
|
4148
4166
|
}
|
|
4149
4167
|
): Promise<string | null> {
|
|
4150
4168
|
if (!assetId) return null
|
|
4151
4169
|
const asset = this.getAsset(assetId)
|
|
4152
4170
|
if (!asset) return null
|
|
4153
4171
|
|
|
4154
|
-
const {
|
|
4172
|
+
const {
|
|
4173
|
+
screenScale = 1,
|
|
4174
|
+
shouldResolveToOriginal = false,
|
|
4175
|
+
dpr = this.getInstanceState().devicePixelRatio,
|
|
4176
|
+
} = context
|
|
4155
4177
|
|
|
4156
4178
|
// We only look at the zoom level at powers of 2.
|
|
4157
4179
|
const zoomStepFunction = (zoom: number) => Math.pow(2, Math.ceil(Math.log2(zoom)))
|
|
4158
|
-
const steppedScreenScale =
|
|
4180
|
+
const steppedScreenScale = zoomStepFunction(screenScale)
|
|
4159
4181
|
const networkEffectiveType: string | null =
|
|
4160
4182
|
'connection' in navigator ? (navigator as any).connection.effectiveType : null
|
|
4161
|
-
const dpr = this.getInstanceState().devicePixelRatio
|
|
4162
4183
|
|
|
4163
4184
|
return await this.store.props.assets.resolve(asset, {
|
|
4164
4185
|
screenScale: screenScale || 1,
|
|
@@ -4172,7 +4193,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4172
4193
|
* Upload an asset to the store's asset service, returning a URL that can be used to resolve the
|
|
4173
4194
|
* asset.
|
|
4174
4195
|
*/
|
|
4175
|
-
async uploadAsset(
|
|
4196
|
+
async uploadAsset(
|
|
4197
|
+
asset: TLAsset,
|
|
4198
|
+
file: File,
|
|
4199
|
+
abortSignal?: AbortSignal
|
|
4200
|
+
): Promise<{ src: string; meta?: JsonObject }> {
|
|
4176
4201
|
return await this.store.props.assets.upload(asset, file, abortSignal)
|
|
4177
4202
|
}
|
|
4178
4203
|
|
|
@@ -6750,6 +6775,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6750
6775
|
}
|
|
6751
6776
|
}
|
|
6752
6777
|
|
|
6778
|
+
let didResize = false
|
|
6779
|
+
|
|
6753
6780
|
if (util.onResize && util.canResize(initialShape)) {
|
|
6754
6781
|
// get the model changes from the shape util
|
|
6755
6782
|
const newPagePoint = this._scalePagePoint(
|
|
@@ -6788,24 +6815,30 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6788
6815
|
)
|
|
6789
6816
|
}
|
|
6790
6817
|
|
|
6818
|
+
const resizedShape = util.onResize(
|
|
6819
|
+
{ ...initialShape, x, y },
|
|
6820
|
+
{
|
|
6821
|
+
newPoint: newLocalPoint,
|
|
6822
|
+
handle: opts.dragHandle ?? 'bottom_right',
|
|
6823
|
+
// don't set isSingle to true for children
|
|
6824
|
+
mode: opts.mode ?? 'scale_shape',
|
|
6825
|
+
scaleX: myScale.x,
|
|
6826
|
+
scaleY: myScale.y,
|
|
6827
|
+
initialBounds,
|
|
6828
|
+
initialShape,
|
|
6829
|
+
}
|
|
6830
|
+
)
|
|
6831
|
+
|
|
6832
|
+
if (resizedShape) {
|
|
6833
|
+
didResize = true
|
|
6834
|
+
}
|
|
6835
|
+
|
|
6791
6836
|
workingShape = applyPartialToRecordWithProps(workingShape, {
|
|
6792
6837
|
id,
|
|
6793
6838
|
type: initialShape.type as any,
|
|
6794
6839
|
x: newLocalPoint.x,
|
|
6795
6840
|
y: newLocalPoint.y,
|
|
6796
|
-
...
|
|
6797
|
-
{ ...initialShape, x, y },
|
|
6798
|
-
{
|
|
6799
|
-
newPoint: newLocalPoint,
|
|
6800
|
-
handle: opts.dragHandle ?? 'bottom_right',
|
|
6801
|
-
// don't set isSingle to true for children
|
|
6802
|
-
mode: opts.mode ?? 'scale_shape',
|
|
6803
|
-
scaleX: myScale.x,
|
|
6804
|
-
scaleY: myScale.y,
|
|
6805
|
-
initialBounds,
|
|
6806
|
-
initialShape,
|
|
6807
|
-
}
|
|
6808
|
-
),
|
|
6841
|
+
...resizedShape,
|
|
6809
6842
|
})
|
|
6810
6843
|
|
|
6811
6844
|
if (!opts.skipStartAndEndCallbacks) {
|
|
@@ -6816,7 +6849,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6816
6849
|
}
|
|
6817
6850
|
|
|
6818
6851
|
this.updateShapes([workingShape])
|
|
6819
|
-
}
|
|
6852
|
+
}
|
|
6853
|
+
|
|
6854
|
+
if (!didResize) {
|
|
6855
|
+
// reposition shape (rather than resizing it) based on where its resized center would be
|
|
6856
|
+
|
|
6820
6857
|
const initialPageCenter = Mat.applyToPoint(pageTransform, initialBounds.center)
|
|
6821
6858
|
// get the model changes from the shape util
|
|
6822
6859
|
const newPageCenter = this._scalePagePoint(
|
|
@@ -8564,11 +8601,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8564
8601
|
*
|
|
8565
8602
|
* @public
|
|
8566
8603
|
*/
|
|
8567
|
-
async getSvgElement(shapes: TLShapeId[] | TLShape[], opts:
|
|
8604
|
+
async getSvgElement(shapes: TLShapeId[] | TLShape[], opts: TLSvgExportOptions = {}) {
|
|
8568
8605
|
const ids =
|
|
8569
|
-
|
|
8570
|
-
? (
|
|
8571
|
-
:
|
|
8606
|
+
shapes.length === 0
|
|
8607
|
+
? this.getCurrentPageShapeIdsSorted()
|
|
8608
|
+
: typeof shapes[0] === 'string'
|
|
8609
|
+
? (shapes as TLShapeId[])
|
|
8610
|
+
: (shapes as TLShape[]).map((s) => s.id)
|
|
8572
8611
|
|
|
8573
8612
|
if (ids.length === 0) return undefined
|
|
8574
8613
|
|
|
@@ -8585,7 +8624,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8585
8624
|
*
|
|
8586
8625
|
* @public
|
|
8587
8626
|
*/
|
|
8588
|
-
async getSvgString(shapes: TLShapeId[] | TLShape[], opts:
|
|
8627
|
+
async getSvgString(shapes: TLShapeId[] | TLShape[], opts: TLSvgExportOptions = {}) {
|
|
8589
8628
|
const result = await this.getSvgElement(shapes, opts)
|
|
8590
8629
|
if (!result) return undefined
|
|
8591
8630
|
|
|
@@ -8598,12 +8637,63 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8598
8637
|
}
|
|
8599
8638
|
|
|
8600
8639
|
/** @deprecated Use {@link Editor.getSvgString} or {@link Editor.getSvgElement} instead. */
|
|
8601
|
-
async getSvg(shapes: TLShapeId[] | TLShape[], opts:
|
|
8640
|
+
async getSvg(shapes: TLShapeId[] | TLShape[], opts: TLSvgExportOptions = {}) {
|
|
8602
8641
|
const result = await this.getSvgElement(shapes, opts)
|
|
8603
8642
|
if (!result) return undefined
|
|
8604
8643
|
return result.svg
|
|
8605
8644
|
}
|
|
8606
8645
|
|
|
8646
|
+
/**
|
|
8647
|
+
* Get an exported image of the given shapes.
|
|
8648
|
+
*
|
|
8649
|
+
* @param shapes - The shapes (or shape ids) to export.
|
|
8650
|
+
* @param opts - Options for the export.
|
|
8651
|
+
*
|
|
8652
|
+
* @returns A blob of the image.
|
|
8653
|
+
* @public
|
|
8654
|
+
*/
|
|
8655
|
+
async toImage(shapes: TLShapeId[] | TLShape[], opts: TLImageExportOptions = {}) {
|
|
8656
|
+
const withDefaults = {
|
|
8657
|
+
format: 'png',
|
|
8658
|
+
scale: 1,
|
|
8659
|
+
pixelRatio: opts.format === 'svg' ? undefined : 2,
|
|
8660
|
+
...opts,
|
|
8661
|
+
} satisfies TLImageExportOptions
|
|
8662
|
+
const result = await this.getSvgString(shapes, withDefaults)
|
|
8663
|
+
if (!result) throw new Error('Could not create SVG')
|
|
8664
|
+
|
|
8665
|
+
switch (withDefaults.format) {
|
|
8666
|
+
case 'svg':
|
|
8667
|
+
return {
|
|
8668
|
+
blob: new Blob([result.svg], { type: 'text/plain' }),
|
|
8669
|
+
width: result.width,
|
|
8670
|
+
height: result.height,
|
|
8671
|
+
}
|
|
8672
|
+
case 'jpeg':
|
|
8673
|
+
case 'png':
|
|
8674
|
+
case 'webp': {
|
|
8675
|
+
const blob = await getSvgAsImage(result.svg, {
|
|
8676
|
+
type: withDefaults.format,
|
|
8677
|
+
quality: withDefaults.quality,
|
|
8678
|
+
pixelRatio: withDefaults.pixelRatio,
|
|
8679
|
+
width: result.width,
|
|
8680
|
+
height: result.height,
|
|
8681
|
+
})
|
|
8682
|
+
if (!blob) {
|
|
8683
|
+
throw new Error('Could not construct image.')
|
|
8684
|
+
}
|
|
8685
|
+
return {
|
|
8686
|
+
blob,
|
|
8687
|
+
width: result.width,
|
|
8688
|
+
height: result.height,
|
|
8689
|
+
}
|
|
8690
|
+
}
|
|
8691
|
+
default: {
|
|
8692
|
+
exhaustiveSwitchError(withDefaults.format)
|
|
8693
|
+
}
|
|
8694
|
+
}
|
|
8695
|
+
}
|
|
8696
|
+
|
|
8607
8697
|
/* --------------------- Events --------------------- */
|
|
8608
8698
|
|
|
8609
8699
|
/**
|
|
@@ -230,6 +230,7 @@ export class TextManager {
|
|
|
230
230
|
elm.style.setProperty('font-weight', opts.fontWeight)
|
|
231
231
|
elm.style.setProperty('line-height', `${opts.lineHeight * opts.fontSize}px`)
|
|
232
232
|
elm.style.setProperty('text-align', textAlignmentsForLtr[opts.textAlign])
|
|
233
|
+
elm.style.setProperty('font-style', opts.fontStyle)
|
|
233
234
|
|
|
234
235
|
const shouldTruncateToFirstLine =
|
|
235
236
|
opts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'
|
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
TLHandle,
|
|
6
6
|
TLPropsMigrations,
|
|
7
7
|
TLShape,
|
|
8
|
+
TLShapeCrop,
|
|
8
9
|
TLShapePartial,
|
|
9
10
|
TLUnknownShape,
|
|
10
11
|
} from '@tldraw/tlschema'
|
|
11
12
|
import { ReactElement } from 'react'
|
|
12
|
-
import { Box } from '../../primitives/Box'
|
|
13
|
+
import { Box, SelectionHandle } from '../../primitives/Box'
|
|
13
14
|
import { Vec } from '../../primitives/Vec'
|
|
14
15
|
import { Geometry2d } from '../../primitives/geometry/Geometry2d'
|
|
15
16
|
import type { Editor } from '../Editor'
|
|
@@ -419,6 +420,19 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
419
420
|
*/
|
|
420
421
|
onBeforeUpdate?(prev: Shape, next: Shape): Shape | void
|
|
421
422
|
|
|
423
|
+
/**
|
|
424
|
+
* A callback called when a shape changes from a crop.
|
|
425
|
+
*
|
|
426
|
+
* @param shape - The shape at the start of the crop.
|
|
427
|
+
* @param info - Info about the crop.
|
|
428
|
+
* @returns A change to apply to the shape, or void.
|
|
429
|
+
* @public
|
|
430
|
+
*/
|
|
431
|
+
onCrop?(
|
|
432
|
+
shape: Shape,
|
|
433
|
+
info: TLCropInfo<Shape>
|
|
434
|
+
): Omit<TLShapePartial<Shape>, 'id' | 'type'> | undefined | void
|
|
435
|
+
|
|
422
436
|
/**
|
|
423
437
|
* A callback called when some other shapes are dragged over this one.
|
|
424
438
|
*
|
|
@@ -616,6 +630,21 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
616
630
|
onEditEnd?(shape: Shape): void
|
|
617
631
|
}
|
|
618
632
|
|
|
633
|
+
/**
|
|
634
|
+
* Info about a crop.
|
|
635
|
+
* @param handle - The handle being dragged.
|
|
636
|
+
* @param change - The distance the handle is moved.
|
|
637
|
+
* @param initialShape - The shape at the start of the resize.
|
|
638
|
+
* @public
|
|
639
|
+
*/
|
|
640
|
+
export interface TLCropInfo<T extends TLShape> {
|
|
641
|
+
handle: SelectionHandle
|
|
642
|
+
change: Vec
|
|
643
|
+
crop: TLShapeCrop
|
|
644
|
+
uncroppedSize: { w: number; h: number }
|
|
645
|
+
initialShape: T
|
|
646
|
+
}
|
|
647
|
+
|
|
619
648
|
/**
|
|
620
649
|
* The type of resize.
|
|
621
650
|
*
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { TLBaseShape } from '@tldraw/tlschema'
|
|
2
|
+
import { exhaustiveSwitchError } from '@tldraw/utils'
|
|
3
|
+
import { Vec } from '../../../primitives/Vec'
|
|
4
|
+
import { TLResizeInfo } from '../ShapeUtil'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Resize a shape that has a scale prop.
|
|
8
|
+
*
|
|
9
|
+
* @param shape - The shape to resize
|
|
10
|
+
* @param info - The resize info
|
|
11
|
+
*
|
|
12
|
+
* @public */
|
|
13
|
+
export function resizeScaled(
|
|
14
|
+
shape: TLBaseShape<any, { scale: number }>,
|
|
15
|
+
{ initialBounds, scaleX, scaleY, newPoint, handle }: TLResizeInfo<any>
|
|
16
|
+
) {
|
|
17
|
+
let scaleDelta: number
|
|
18
|
+
switch (handle) {
|
|
19
|
+
case 'bottom_left':
|
|
20
|
+
case 'bottom_right':
|
|
21
|
+
case 'top_left':
|
|
22
|
+
case 'top_right': {
|
|
23
|
+
scaleDelta = Math.max(0.01, Math.max(Math.abs(scaleX), Math.abs(scaleY)))
|
|
24
|
+
break
|
|
25
|
+
}
|
|
26
|
+
case 'left':
|
|
27
|
+
case 'right': {
|
|
28
|
+
scaleDelta = Math.max(0.01, Math.abs(scaleX))
|
|
29
|
+
break
|
|
30
|
+
}
|
|
31
|
+
case 'bottom':
|
|
32
|
+
case 'top': {
|
|
33
|
+
scaleDelta = Math.max(0.01, Math.abs(scaleY))
|
|
34
|
+
break
|
|
35
|
+
}
|
|
36
|
+
default: {
|
|
37
|
+
throw exhaustiveSwitchError(handle)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Compute the offset (if flipped X or flipped Y)
|
|
42
|
+
const offset = new Vec(0, 0)
|
|
43
|
+
|
|
44
|
+
if (scaleX < 0) {
|
|
45
|
+
offset.x = -(initialBounds.width * scaleDelta)
|
|
46
|
+
}
|
|
47
|
+
if (scaleY < 0) {
|
|
48
|
+
offset.y = -(initialBounds.height * scaleDelta)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Apply the offset to the new point
|
|
52
|
+
const { x, y } = Vec.Add(newPoint, offset.rot(shape.rotation))
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
x,
|
|
56
|
+
y,
|
|
57
|
+
props: {
|
|
58
|
+
scale: scaleDelta * shape.props.scale,
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TLAssetId } from '@tldraw/tlschema'
|
|
1
2
|
import { promiseWithResolve } from '@tldraw/utils'
|
|
2
3
|
import { ReactElement, ReactNode, createContext, useContext, useEffect, useState } from 'react'
|
|
3
4
|
import { ContainerProvider } from '../../hooks/useContainer'
|
|
@@ -29,10 +30,30 @@ export interface SvgExportContext {
|
|
|
29
30
|
*/
|
|
30
31
|
waitUntil(promise: Promise<void>): void
|
|
31
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Resolve an asset URL in the context of this export. Supply the asset ID and the width in
|
|
35
|
+
* shape-pixels it'll be displayed at, and this will resolve the asset according to the export
|
|
36
|
+
* options.
|
|
37
|
+
*/
|
|
38
|
+
resolveAssetUrl(assetId: TLAssetId, width: number): Promise<string | null>
|
|
39
|
+
|
|
32
40
|
/**
|
|
33
41
|
* Whether the export should be in dark mode.
|
|
34
42
|
*/
|
|
35
43
|
readonly isDarkMode: boolean
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The scale of the export - how much CSS pixels will be scaled up/down by.
|
|
47
|
+
*/
|
|
48
|
+
readonly scale: number
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Use this value to optionally downscale images in the export. If we're exporting directly to
|
|
52
|
+
* an SVG, this will usually be null, and you shouldn't downscale images. If the export is to a
|
|
53
|
+
* raster format like PNG, this will be the number of raster pixels in the resulting bitmap per
|
|
54
|
+
* CSS pixel in the resulting SVG.
|
|
55
|
+
*/
|
|
56
|
+
readonly pixelRatio: number | null
|
|
36
57
|
}
|
|
37
58
|
|
|
38
59
|
const Context = createContext<SvgExportContext | null>(null)
|