Add component for viewing an image of a snapshot (#2804)
This PR adds the `TldrawImage` component that displays a tldraw snapshot as an SVG image. ![2024-02-15 at 12 29 52 - Coral Cod](https://github.com/tldraw/tldraw/assets/15892272/14140e9e-7d6d-4dd3-88a3-86a6786325c5) ## Why We've seen requests for this kind of thing from users. eg: GitBook, and on discord: <img width="710" alt="image" src="https://github.com/tldraw/tldraw/assets/15892272/3d3a3e9d-66b9-42e7-81de-a70aa7165bdc"> The component provides a way to do that. This PR also untangles various bits of editor state from image exporting, which makes it easier for library users to export images more agnostically. (ie: they can now export any shapes on any page in any theme. previously, they had to change the user's state to do that). ## What else - This PR also adds an **Image snapshot** example to demonstrate the new component. - We now pass an `isDarkMode` property to the `toSvg` method (inside the `ctx` argument). This means that `toSvg` doesn't have to rely on editor state anymore. I updated all our `toSvg` methods to use it. - See code comments for more info. ## Any issues? When you toggle to editing mode in the new example, text measurements are initially wrong (until you edit the size of a text shape). Click on the text shape to see how its indicator is wrong. Not sure why this is, or if it's even related. Does it ring a bell with anyone? If not, I'll take a closer look. (fixed, see comments --steve) ## Future work Now that we've untangled image exporting from editor state, we could expose some more helpful helpers for making this easier. Fixes tld-2122 ### Change Type - [x] `minor` — New feature [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Test Plan 1. Open the **Image snapshot** example. 2. Try editing the image, saving the image, and making sure the image updates. - [ ] Unit Tests - [ ] End to end tests ### Release Notes - Dev: Added the `TldrawImage` component. --------- Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
parent
dd67577fea
commit
212eb88480
26 changed files with 1159 additions and 70 deletions
10
apps/examples/src/examples/image-component/README.md
Normal file
10
apps/examples/src/examples/image-component/README.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: Tldraw image component
|
||||
component: ./TldrawImageExample.tsx
|
||||
category: ui
|
||||
priority: 3
|
||||
---
|
||||
|
||||
Display a tldraw snapshot as an image by using the `TldrawImage` component.
|
||||
|
||||
---
|
|
@ -0,0 +1,107 @@
|
|||
import { Box, Editor, StoreSnapshot, TLPageId, TLRecord, Tldraw, TldrawImage } from '@tldraw/tldraw'
|
||||
import '@tldraw/tldraw/tldraw.css'
|
||||
import { useState } from 'react'
|
||||
import initialSnapshot from './snapshot.json'
|
||||
|
||||
// There's a guide at the bottom of this file!
|
||||
|
||||
export default function TldrawImageExample() {
|
||||
const [editor, setEditor] = useState<Editor>()
|
||||
const [snapshot, setSnapshot] = useState<StoreSnapshot<TLRecord>>(initialSnapshot)
|
||||
const [currentPageId, setCurrentPageId] = useState<TLPageId | undefined>()
|
||||
const [showBackground, setShowBackground] = useState(true)
|
||||
const [isDarkMode, setIsDarkMode] = useState(false)
|
||||
const [viewportPageBounds, setViewportPageBounds] = useState(new Box(0, 0, 600, 400))
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [format, setFormat] = useState<'svg' | 'png'>('svg')
|
||||
|
||||
return (
|
||||
<div style={{ padding: 30 }}>
|
||||
<div>
|
||||
<button
|
||||
style={{ cursor: 'pointer', marginRight: 8 }}
|
||||
onClick={() => {
|
||||
if (isEditing) {
|
||||
if (!editor) return
|
||||
setIsDarkMode(editor.user.getIsDarkMode())
|
||||
setShowBackground(editor.getInstanceState().exportBackground)
|
||||
setViewportPageBounds(editor.getViewportPageBounds())
|
||||
setCurrentPageId(editor.getCurrentPageId())
|
||||
setSnapshot(editor.store.getSnapshot())
|
||||
setIsEditing(false)
|
||||
} else {
|
||||
setIsEditing(true)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{isEditing ? '✓ Save drawing' : '✎ Edit drawing'}
|
||||
</button>
|
||||
{!isEditing && (
|
||||
<>
|
||||
<label htmlFor="format" style={{ marginRight: 8 }}>
|
||||
Format
|
||||
</label>
|
||||
<select
|
||||
name="format"
|
||||
value={format}
|
||||
onChange={(e) => {
|
||||
setFormat(e.currentTarget.value as 'svg' | 'png')
|
||||
}}
|
||||
>
|
||||
<option value="svg">SVG</option>
|
||||
<option value="png">PNG</option>
|
||||
</select>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ width: 600, height: 400, marginTop: 15 }}>
|
||||
{isEditing ? (
|
||||
<Tldraw
|
||||
snapshot={snapshot}
|
||||
onMount={(editor: Editor) => {
|
||||
setEditor(editor)
|
||||
editor.updateInstanceState({ isDebugMode: false })
|
||||
editor.user.updateUserPreferences({ isDarkMode })
|
||||
if (currentPageId) {
|
||||
editor.setCurrentPage(currentPageId)
|
||||
}
|
||||
if (viewportPageBounds) {
|
||||
editor.zoomToBounds(viewportPageBounds, { inset: 0 })
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<TldrawImage
|
||||
//[1]
|
||||
snapshot={snapshot}
|
||||
// [2]
|
||||
pageId={currentPageId}
|
||||
// [3]
|
||||
background={showBackground}
|
||||
darkMode={isDarkMode}
|
||||
bounds={viewportPageBounds}
|
||||
padding={0}
|
||||
scale={1}
|
||||
format={format}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
This example shows how to use the `TldrawImage` component to display a snapshot
|
||||
as an image. The example also allows you to toggle between editing the snapshot
|
||||
and viewing it.
|
||||
|
||||
[1] Pass your snapshot to the `snapshot` prop of the `TldrawImage` component.
|
||||
|
||||
[2] You can specify which page to display by using the `pageId` prop. By
|
||||
default, the first page is shown.
|
||||
|
||||
[3] You can customize the appearance of the image by passing other props to the
|
||||
`TldrawImage` component. For example, you can toggle the background, set the
|
||||
dark mode, and specify the viewport bounds.
|
||||
*/
|
454
apps/examples/src/examples/image-component/snapshot.json
Normal file
454
apps/examples/src/examples/image-component/snapshot.json
Normal file
|
@ -0,0 +1,454 @@
|
|||
{
|
||||
"store": {
|
||||
"document:document": {
|
||||
"gridSize": 10,
|
||||
"name": "",
|
||||
"meta": {},
|
||||
"id": "document:document",
|
||||
"typeName": "document"
|
||||
},
|
||||
"page:3qj9EtNgqSCW_6knX2K9_": {
|
||||
"meta": {},
|
||||
"id": "page:3qj9EtNgqSCW_6knX2K9_",
|
||||
"name": "Page 1",
|
||||
"index": "a1",
|
||||
"typeName": "page"
|
||||
},
|
||||
"asset:imageAssetA": {
|
||||
"type": "image",
|
||||
"props": {
|
||||
"w": 1200,
|
||||
"h": 800,
|
||||
"name": "",
|
||||
"isAnimated": false,
|
||||
"mimeType": "png",
|
||||
"src": ""
|
||||
},
|
||||
"meta": {},
|
||||
"id": "asset:imageAssetA",
|
||||
"typeName": "asset"
|
||||
},
|
||||
"shape:EHeAIsYe4xu1-kGxK-Tl_": {
|
||||
"x": 108.01190683749454,
|
||||
"y": 138.58783300957418,
|
||||
"rotation": 0,
|
||||
"isLocked": false,
|
||||
"opacity": 1,
|
||||
"meta": {},
|
||||
"type": "draw",
|
||||
"props": {
|
||||
"segments": [
|
||||
{
|
||||
"type": "free",
|
||||
"points": [
|
||||
{ "x": 0, "y": 0, "z": 0.5 },
|
||||
{ "x": -0.07, "y": 0.07, "z": 0.5 },
|
||||
{ "x": -0.03, "y": 0.14, "z": 0.5 },
|
||||
{ "x": 0.47, "y": 0.14, "z": 0.5 },
|
||||
{ "x": 3.54, "y": 0.14, "z": 0.5 },
|
||||
{ "x": 10.47, "y": -1.05, "z": 0.5 },
|
||||
{ "x": 17.57, "y": -3.65, "z": 0.5 },
|
||||
{ "x": 24.13, "y": -7.67, "z": 0.5 },
|
||||
{ "x": 31.36, "y": -13.59, "z": 0.5 },
|
||||
{ "x": 36.65, "y": -20.16, "z": 0.5 },
|
||||
{ "x": 39.27, "y": -26.49, "z": 0.5 },
|
||||
{ "x": 40.12, "y": -32.21, "z": 0.5 },
|
||||
{ "x": 40.11, "y": -37.65, "z": 0.5 },
|
||||
{ "x": 39.45, "y": -41.67, "z": 0.5 },
|
||||
{ "x": 36.85, "y": -44.45, "z": 0.5 },
|
||||
{ "x": 33.68, "y": -46.36, "z": 0.5 },
|
||||
{ "x": 30.91, "y": -46.85, "z": 0.5 },
|
||||
{ "x": 27.84, "y": -46.77, "z": 0.5 },
|
||||
{ "x": 24.96, "y": -45.3, "z": 0.5 },
|
||||
{ "x": 22.43, "y": -42.1, "z": 0.5 },
|
||||
{ "x": 20.4, "y": -36.5, "z": 0.5 },
|
||||
{ "x": 19.37, "y": -26.9, "z": 0.5 },
|
||||
{ "x": 19.17, "y": -14.09, "z": 0.5 },
|
||||
{ "x": 21.12, "y": 1.65, "z": 0.5 },
|
||||
{ "x": 25.48, "y": 20.19, "z": 0.5 },
|
||||
{ "x": 29.24, "y": 36.72, "z": 0.5 },
|
||||
{ "x": 31.85, "y": 53.01, "z": 0.5 },
|
||||
{ "x": 33.37, "y": 70.4, "z": 0.5 },
|
||||
{ "x": 33.2, "y": 84.67, "z": 0.5 },
|
||||
{ "x": 30.57, "y": 95.01, "z": 0.5 },
|
||||
{ "x": 24.99, "y": 101.14, "z": 0.5 },
|
||||
{ "x": 17.55, "y": 103.88, "z": 0.5 },
|
||||
{ "x": 9.92, "y": 104.49, "z": 0.5 },
|
||||
{ "x": 3.66, "y": 104.01, "z": 0.5 },
|
||||
{ "x": -0.64, "y": 102.5, "z": 0.5 },
|
||||
{ "x": -2.92, "y": 100.27, "z": 0.5 },
|
||||
{ "x": -3.6, "y": 96.97, "z": 0.5 },
|
||||
{ "x": -0.45, "y": 91.45, "z": 0.5 },
|
||||
{ "x": 9.15, "y": 83.2, "z": 0.5 },
|
||||
{ "x": 24.06, "y": 72.39, "z": 0.5 },
|
||||
{ "x": 40.16, "y": 59.82, "z": 0.5 },
|
||||
{ "x": 54.06, "y": 45.99, "z": 0.5 },
|
||||
{ "x": 63.61, "y": 33.29, "z": 0.5 },
|
||||
{ "x": 69.31, "y": 20.87, "z": 0.5 },
|
||||
{ "x": 72.36, "y": 8.36, "z": 0.5 },
|
||||
{ "x": 73.05, "y": -0.07, "z": 0.5 },
|
||||
{ "x": 72.5, "y": -5.4, "z": 0.5 },
|
||||
{ "x": 71, "y": -9.15, "z": 0.5 },
|
||||
{ "x": 69.23, "y": -11.13, "z": 0.5 },
|
||||
{ "x": 67.7, "y": -12.08, "z": 0.5 },
|
||||
{ "x": 66.64, "y": -12.35, "z": 0.5 },
|
||||
{ "x": 66.07, "y": -12.29, "z": 0.5 },
|
||||
{ "x": 65.59, "y": -10.98, "z": 0.5 },
|
||||
{ "x": 64.91, "y": -5.01, "z": 0.5 },
|
||||
{ "x": 63.56, "y": 6.47, "z": 0.5 },
|
||||
{ "x": 61.24, "y": 21.76, "z": 0.5 },
|
||||
{ "x": 59.27, "y": 36.74, "z": 0.5 },
|
||||
{ "x": 58.63, "y": 50.8, "z": 0.5 },
|
||||
{ "x": 58.56, "y": 64.21, "z": 0.5 },
|
||||
{ "x": 59.69, "y": 72.89, "z": 0.5 },
|
||||
{ "x": 62.94, "y": 78.64, "z": 0.5 },
|
||||
{ "x": 66.62, "y": 82.15, "z": 0.5 },
|
||||
{ "x": 70.67, "y": 82.79, "z": 0.5 },
|
||||
{ "x": 75.98, "y": 81.09, "z": 0.5 },
|
||||
{ "x": 81.97, "y": 75.87, "z": 0.5 },
|
||||
{ "x": 87.87, "y": 68.39, "z": 0.5 },
|
||||
{ "x": 92.7, "y": 59.73, "z": 0.5 },
|
||||
{ "x": 95.82, "y": 50.99, "z": 0.5 },
|
||||
{ "x": 96.92, "y": 44.55, "z": 0.5 },
|
||||
{ "x": 97.02, "y": 39.91, "z": 0.5 },
|
||||
{ "x": 96.56, "y": 36.38, "z": 0.5 },
|
||||
{ "x": 95.41, "y": 34.37, "z": 0.5 },
|
||||
{ "x": 94.09, "y": 33.59, "z": 0.5 },
|
||||
{ "x": 92.51, "y": 33.65, "z": 0.5 },
|
||||
{ "x": 90.9, "y": 36.01, "z": 0.5 },
|
||||
{ "x": 89.8, "y": 42.11, "z": 0.5 },
|
||||
{ "x": 89.36, "y": 50.78, "z": 0.5 },
|
||||
{ "x": 90.17, "y": 60.6, "z": 0.5 },
|
||||
{ "x": 92.22, "y": 67.78, "z": 0.5 },
|
||||
{ "x": 95.07, "y": 71.7, "z": 0.5 },
|
||||
{ "x": 98.44, "y": 73.97, "z": 0.5 },
|
||||
{ "x": 101.62, "y": 74.13, "z": 0.5 },
|
||||
{ "x": 105.05, "y": 70.76, "z": 0.5 },
|
||||
{ "x": 108.93, "y": 63.31, "z": 0.5 },
|
||||
{ "x": 112.09, "y": 54.51, "z": 0.5 },
|
||||
{ "x": 113.75, "y": 47.54, "z": 0.5 },
|
||||
{ "x": 114.33, "y": 42.98, "z": 0.5 },
|
||||
{ "x": 114.57, "y": 40.11, "z": 0.5 },
|
||||
{ "x": 114.53, "y": 39.06, "z": 0.5 },
|
||||
{ "x": 114.21, "y": 39.22, "z": 0.5 },
|
||||
{ "x": 113.61, "y": 41.78, "z": 0.5 },
|
||||
{ "x": 113.24, "y": 47.65, "z": 0.5 },
|
||||
{ "x": 113.2, "y": 54.77, "z": 0.5 },
|
||||
{ "x": 113.59, "y": 59.94, "z": 0.5 },
|
||||
{ "x": 115.15, "y": 63.14, "z": 0.5 },
|
||||
{ "x": 117.5, "y": 65.35, "z": 0.5 },
|
||||
{ "x": 119.82, "y": 65.98, "z": 0.5 },
|
||||
{ "x": 122.17, "y": 64.83, "z": 0.5 },
|
||||
{ "x": 124.45, "y": 60.83, "z": 0.5 },
|
||||
{ "x": 126.38, "y": 54.53, "z": 0.5 },
|
||||
{ "x": 127.57, "y": 48.58, "z": 0.5 },
|
||||
{ "x": 128.02, "y": 43.71, "z": 0.5 },
|
||||
{ "x": 128.14, "y": 40, "z": 0.5 },
|
||||
{ "x": 128.14, "y": 37.99, "z": 0.5 },
|
||||
{ "x": 128.05, "y": 37.08, "z": 0.5 },
|
||||
{ "x": 127.96, "y": 36.89, "z": 0.5 },
|
||||
{ "x": 128.34, "y": 37.5, "z": 0.5 },
|
||||
{ "x": 131.02, "y": 39.91, "z": 0.5 },
|
||||
{ "x": 137.11, "y": 44.76, "z": 0.5 },
|
||||
{ "x": 145.28, "y": 51.58, "z": 0.5 },
|
||||
{ "x": 153.49, "y": 59.87, "z": 0.5 },
|
||||
{ "x": 159.26, "y": 69.47, "z": 0.5 },
|
||||
{ "x": 161.58, "y": 81.66, "z": 0.5 },
|
||||
{ "x": 158.71, "y": 94.96, "z": 0.5 },
|
||||
{ "x": 147.18, "y": 107.43, "z": 0.5 },
|
||||
{ "x": 132.52, "y": 116.36, "z": 0.5 },
|
||||
{ "x": 119.46, "y": 120.3, "z": 0.5 },
|
||||
{ "x": 109.14, "y": 121.49, "z": 0.5 },
|
||||
{ "x": 102.95, "y": 119.79, "z": 0.5 },
|
||||
{ "x": 100.5, "y": 114.09, "z": 0.5 },
|
||||
{ "x": 105.6, "y": 103.93, "z": 0.5 },
|
||||
{ "x": 120.72, "y": 89.8, "z": 0.5 },
|
||||
{ "x": 143.19, "y": 72.46, "z": 0.5 },
|
||||
{ "x": 167.67, "y": 53.41, "z": 0.5 },
|
||||
{ "x": 185.27, "y": 37.82, "z": 0.5 },
|
||||
{ "x": 193.79, "y": 26.69, "z": 0.5 },
|
||||
{ "x": 197.17, "y": 17.76, "z": 0.5 },
|
||||
{ "x": 194.75, "y": 11.87, "z": 0.5 },
|
||||
{ "x": 185.34, "y": 9.07, "z": 0.5 },
|
||||
{ "x": 172.73, "y": 8.91, "z": 0.5 },
|
||||
{ "x": 162.58, "y": 10.74, "z": 0.5 },
|
||||
{ "x": 155.42, "y": 13.45, "z": 0.5 },
|
||||
{ "x": 151.3, "y": 15.61, "z": 0.5 },
|
||||
{ "x": 150.03, "y": 17.66, "z": 0.5 }
|
||||
]
|
||||
}
|
||||
],
|
||||
"color": "black",
|
||||
"fill": "none",
|
||||
"dash": "draw",
|
||||
"size": "l",
|
||||
"isComplete": true,
|
||||
"isClosed": false,
|
||||
"isPen": false
|
||||
},
|
||||
"parentId": "page:3qj9EtNgqSCW_6knX2K9_",
|
||||
"index": "a1",
|
||||
"id": "shape:EHeAIsYe4xu1-kGxK-Tl_",
|
||||
"typeName": "shape"
|
||||
},
|
||||
"shape:v0c3Ac-kUqB5C8cLsyT_E": {
|
||||
"x": 325.71484375,
|
||||
"y": 165.9453125,
|
||||
"rotation": 0,
|
||||
"isLocked": false,
|
||||
"opacity": 1,
|
||||
"meta": {},
|
||||
"type": "text",
|
||||
"props": {
|
||||
"color": "black",
|
||||
"size": "m",
|
||||
"w": 139.38919029117903,
|
||||
"text": "hey hey hey",
|
||||
"font": "draw",
|
||||
"align": "middle",
|
||||
"autoSize": false,
|
||||
"scale": 1.178662627660688
|
||||
},
|
||||
"parentId": "page:3qj9EtNgqSCW_6knX2K9_",
|
||||
"index": "a2",
|
||||
"id": "shape:v0c3Ac-kUqB5C8cLsyT_E",
|
||||
"typeName": "shape"
|
||||
},
|
||||
"page:2E1xHBVQtZUB5fzXfSUPl": {
|
||||
"meta": {},
|
||||
"id": "page:2E1xHBVQtZUB5fzXfSUPl",
|
||||
"name": "Page 2",
|
||||
"index": "a2",
|
||||
"typeName": "page"
|
||||
},
|
||||
"shape:qZ9C8PqSv6tSWya7LpL1t": {
|
||||
"x": 144.09765625,
|
||||
"y": 139.14829380718655,
|
||||
"rotation": 0,
|
||||
"isLocked": false,
|
||||
"opacity": 1,
|
||||
"meta": {},
|
||||
"type": "text",
|
||||
"props": {
|
||||
"color": "black",
|
||||
"size": "m",
|
||||
"w": 74.73082091700364,
|
||||
"text": "Page",
|
||||
"font": "draw",
|
||||
"align": "middle",
|
||||
"autoSize": false,
|
||||
"scale": 1.6823620652062432
|
||||
},
|
||||
"parentId": "page:2E1xHBVQtZUB5fzXfSUPl",
|
||||
"index": "a1",
|
||||
"id": "shape:qZ9C8PqSv6tSWya7LpL1t",
|
||||
"typeName": "shape"
|
||||
},
|
||||
"shape:935Jl5xP5gxCs4JGaE81D": {
|
||||
"x": 293.97412290107434,
|
||||
"y": 137.07222276594962,
|
||||
"rotation": 0,
|
||||
"isLocked": false,
|
||||
"opacity": 1,
|
||||
"meta": {},
|
||||
"type": "draw",
|
||||
"props": {
|
||||
"segments": [
|
||||
{
|
||||
"type": "free",
|
||||
"points": [
|
||||
{ "x": 0, "y": 0, "z": 0.5 },
|
||||
{ "x": -0.07, "y": 0, "z": 0.5 },
|
||||
{ "x": -0.15, "y": -0.07, "z": 0.5 },
|
||||
{ "x": -0.15, "y": -0.13, "z": 0.5 },
|
||||
{ "x": -0.15, "y": -0.3, "z": 0.5 },
|
||||
{ "x": -0.15, "y": -0.57, "z": 0.5 },
|
||||
{ "x": -0.15, "y": -1.21, "z": 0.5 },
|
||||
{ "x": -0.15, "y": -2.07, "z": 0.5 },
|
||||
{ "x": -0.15, "y": -2.93, "z": 0.5 },
|
||||
{ "x": -0.15, "y": -3.79, "z": 0.5 },
|
||||
{ "x": 0.72, "y": -5.84, "z": 0.5 },
|
||||
{ "x": 2.02, "y": -8.45, "z": 0.5 },
|
||||
{ "x": 3.62, "y": -10.87, "z": 0.5 },
|
||||
{ "x": 5.3, "y": -13.09, "z": 0.5 },
|
||||
{ "x": 9.45, "y": -17.77, "z": 0.5 },
|
||||
{ "x": 10.99, "y": -19.35, "z": 0.5 },
|
||||
{ "x": 12.21, "y": -20.59, "z": 0.5 },
|
||||
{ "x": 17.21, "y": -25.22, "z": 0.5 },
|
||||
{ "x": 20.05, "y": -27.37, "z": 0.5 },
|
||||
{ "x": 22.88, "y": -29.39, "z": 0.5 },
|
||||
{ "x": 25.87, "y": -31.19, "z": 0.5 },
|
||||
{ "x": 28.6, "y": -32.74, "z": 0.5 },
|
||||
{ "x": 29.98, "y": -33.01, "z": 0.5 },
|
||||
{ "x": 31.22, "y": -33.16, "z": 0.5 },
|
||||
{ "x": 36.02, "y": -33.8, "z": 0.5 },
|
||||
{ "x": 38.11, "y": -33.82, "z": 0.5 },
|
||||
{ "x": 41.03, "y": -33.82, "z": 0.5 },
|
||||
{ "x": 44.67, "y": -33.82, "z": 0.5 },
|
||||
{ "x": 47.41, "y": -33.82, "z": 0.5 },
|
||||
{ "x": 49.29, "y": -33.56, "z": 0.5 },
|
||||
{ "x": 50.76, "y": -33.13, "z": 0.5 },
|
||||
{ "x": 51.93, "y": -32.45, "z": 0.5 },
|
||||
{ "x": 52.89, "y": -31.61, "z": 0.5 },
|
||||
{ "x": 53.67, "y": -30.82, "z": 0.5 },
|
||||
{ "x": 54.27, "y": -30.1, "z": 0.5 },
|
||||
{ "x": 54.93, "y": -29.21, "z": 0.5 },
|
||||
{ "x": 55.59, "y": -28.3, "z": 0.5 },
|
||||
{ "x": 55.97, "y": -27.29, "z": 0.5 },
|
||||
{ "x": 56.22, "y": -26.26, "z": 0.5 },
|
||||
{ "x": 56.39, "y": -25.3, "z": 0.5 },
|
||||
{ "x": 56.51, "y": -24.46, "z": 0.5 },
|
||||
{ "x": 56.54, "y": -23.28, "z": 0.5 },
|
||||
{ "x": 56.54, "y": -22.04, "z": 0.5 },
|
||||
{ "x": 56.66, "y": -20.78, "z": 0.5 },
|
||||
{ "x": 56.8, "y": -19.55, "z": 0.5 },
|
||||
{ "x": 56.83, "y": -17.43, "z": 0.5 },
|
||||
{ "x": 56.83, "y": -15.23, "z": 0.5 },
|
||||
{ "x": 56.83, "y": -13.16, "z": 0.5 },
|
||||
{ "x": 56.83, "y": -11.22, "z": 0.5 },
|
||||
{ "x": 56.38, "y": -9.22, "z": 0.5 },
|
||||
{ "x": 55.89, "y": -7.28, "z": 0.5 },
|
||||
{ "x": 54.78, "y": -4.61, "z": 0.5 },
|
||||
{ "x": 52.96, "y": -0.39, "z": 0.5 },
|
||||
{ "x": 51.48, "y": 2.96, "z": 0.5 },
|
||||
{ "x": 47.31, "y": 10.43, "z": 0.5 },
|
||||
{ "x": 40.34, "y": 21.06, "z": 0.5 },
|
||||
{ "x": 34.42, "y": 28.68, "z": 0.5 },
|
||||
{ "x": 30.28, "y": 33.6, "z": 0.5 },
|
||||
{ "x": 27.27, "y": 37.05, "z": 0.5 },
|
||||
{ "x": 25.13, "y": 39.37, "z": 0.5 },
|
||||
{ "x": 21.82, "y": 42.92, "z": 0.5 },
|
||||
{ "x": 17.82, "y": 47.17, "z": 0.5 },
|
||||
{ "x": 15.34, "y": 49.73, "z": 0.5 },
|
||||
{ "x": 13.9, "y": 51.19, "z": 0.5 },
|
||||
{ "x": 11.34, "y": 53.73, "z": 0.5 },
|
||||
{ "x": 8.28, "y": 56.8, "z": 0.5 },
|
||||
{ "x": 5.59, "y": 59.34, "z": 0.5 },
|
||||
{ "x": 3.26, "y": 61.47, "z": 0.5 },
|
||||
{ "x": 2.07, "y": 62.53, "z": 0.5 },
|
||||
{ "x": 1.33, "y": 63.14, "z": 0.5 },
|
||||
{ "x": 0, "y": 64.42, "z": 0.5 },
|
||||
{ "x": -1.45, "y": 65.88, "z": 0.5 },
|
||||
{ "x": -2.44, "y": 66.86, "z": 0.5 },
|
||||
{ "x": -3.21, "y": 67.63, "z": 0.5 },
|
||||
{ "x": -3.85, "y": 68.27, "z": 0.5 },
|
||||
{ "x": -4.46, "y": 68.88, "z": 0.5 },
|
||||
{ "x": -4.73, "y": 69.23, "z": 0.5 },
|
||||
{ "x": -4.91, "y": 69.5, "z": 0.5 },
|
||||
{ "x": -5.01, "y": 69.67, "z": 0.5 },
|
||||
{ "x": -5.1, "y": 69.83, "z": 0.5 },
|
||||
{ "x": -5.1, "y": 69.92, "z": 0.5 },
|
||||
{ "x": -5.1, "y": 69.99, "z": 0.5 },
|
||||
{ "x": -5.03, "y": 70, "z": 0.5 },
|
||||
{ "x": -4.44, "y": 69.89, "z": 0.5 },
|
||||
{ "x": -3.48, "y": 69.5, "z": 0.5 },
|
||||
{ "x": -2.76, "y": 69.01, "z": 0.5 },
|
||||
{ "x": -1.38, "y": 68.32, "z": 0.5 },
|
||||
{ "x": 0.4, "y": 67.51, "z": 0.5 },
|
||||
{ "x": 3.06, "y": 66.45, "z": 0.5 },
|
||||
{ "x": 6.29, "y": 65.24, "z": 0.5 },
|
||||
{ "x": 9.81, "y": 64.23, "z": 0.5 },
|
||||
{ "x": 13.24, "y": 63.43, "z": 0.5 },
|
||||
{ "x": 17.8, "y": 62.5, "z": 0.5 },
|
||||
{ "x": 22.71, "y": 61.56, "z": 0.5 },
|
||||
{ "x": 28.7, "y": 60.72, "z": 0.5 },
|
||||
{ "x": 34.89, "y": 59.94, "z": 0.5 },
|
||||
{ "x": 42.4, "y": 59.29, "z": 0.5 },
|
||||
{ "x": 50.03, "y": 58.71, "z": 0.5 },
|
||||
{ "x": 57.97, "y": 58.58, "z": 0.5 },
|
||||
{ "x": 65.61, "y": 58.58, "z": 0.5 },
|
||||
{ "x": 73.79, "y": 58.58, "z": 0.5 },
|
||||
{ "x": 81.93, "y": 58.58, "z": 0.5 },
|
||||
{ "x": 89.91, "y": 58.58, "z": 0.5 },
|
||||
{ "x": 97.54, "y": 58.58, "z": 0.5 },
|
||||
{ "x": 101.72, "y": 58.58, "z": 0.5 },
|
||||
{ "x": 104.83, "y": 58.58, "z": 0.5 },
|
||||
{ "x": 110.48, "y": 58.58, "z": 0.5 },
|
||||
{ "x": 117.33, "y": 58.75, "z": 0.5 },
|
||||
{ "x": 119.83, "y": 59.08, "z": 0.5 },
|
||||
{ "x": 124.73, "y": 59.89, "z": 0.5 },
|
||||
{ "x": 129.54, "y": 60.77, "z": 0.5 },
|
||||
{ "x": 131.86, "y": 61.42, "z": 0.5 },
|
||||
{ "x": 134.07, "y": 62.11, "z": 0.5 },
|
||||
{ "x": 135.85, "y": 62.57, "z": 0.5 },
|
||||
{ "x": 137.3, "y": 62.85, "z": 0.5 },
|
||||
{ "x": 138.44, "y": 63.19, "z": 0.5 },
|
||||
{ "x": 139.28, "y": 63.56, "z": 0.5 },
|
||||
{ "x": 139.96, "y": 63.9, "z": 0.5 },
|
||||
{ "x": 140.48, "y": 64.22, "z": 0.5 },
|
||||
{ "x": 140.93, "y": 64.47, "z": 0.5 },
|
||||
{ "x": 141.32, "y": 64.65, "z": 0.5 },
|
||||
{ "x": 141.57, "y": 64.84, "z": 0.5 },
|
||||
{ "x": 141.72, "y": 65.01, "z": 0.5 },
|
||||
{ "x": 141.84, "y": 65.17, "z": 0.5 },
|
||||
{ "x": 141.92, "y": 65.33, "z": 0.5 },
|
||||
{ "x": 141.94, "y": 65.43, "z": 0.5 },
|
||||
{ "x": 141.94, "y": 65.51, "z": 0.5 },
|
||||
{ "x": 141.94, "y": 65.58, "z": 0.5 },
|
||||
{ "x": 141.94, "y": 65.65, "z": 0.5 },
|
||||
{ "x": 141.94, "y": 65.73, "z": 0.5 },
|
||||
{ "x": 141.94, "y": 65.8, "z": 0.5 },
|
||||
{ "x": 141.94, "y": 65.88, "z": 0.5 },
|
||||
{ "x": 141.94, "y": 65.95, "z": 0.5 },
|
||||
{ "x": 141.94, "y": 66.03, "z": 0.5 },
|
||||
{ "x": 141.94, "y": 66.1, "z": 0.5 },
|
||||
{ "x": 141.94, "y": 66.18, "z": 0.5 },
|
||||
{ "x": 141.86, "y": 66.29, "z": 0.5 },
|
||||
{ "x": 141.69, "y": 66.37, "z": 0.5 },
|
||||
{ "x": 141.45, "y": 66.42, "z": 0.5 },
|
||||
{ "x": 141.12, "y": 66.5, "z": 0.5 }
|
||||
]
|
||||
}
|
||||
],
|
||||
"color": "black",
|
||||
"fill": "none",
|
||||
"dash": "draw",
|
||||
"size": "xl",
|
||||
"isComplete": true,
|
||||
"isClosed": false,
|
||||
"isPen": false
|
||||
},
|
||||
"parentId": "page:2E1xHBVQtZUB5fzXfSUPl",
|
||||
"index": "a2",
|
||||
"id": "shape:935Jl5xP5gxCs4JGaE81D",
|
||||
"typeName": "shape"
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"schemaVersion": 1,
|
||||
"storeVersion": 4,
|
||||
"recordVersions": {
|
||||
"asset": {
|
||||
"version": 1,
|
||||
"subTypeKey": "type",
|
||||
"subTypeVersions": { "image": 3, "video": 3, "bookmark": 1 }
|
||||
},
|
||||
"camera": { "version": 1 },
|
||||
"document": { "version": 2 },
|
||||
"instance": { "version": 24 },
|
||||
"instance_page_state": { "version": 5 },
|
||||
"page": { "version": 1 },
|
||||
"shape": {
|
||||
"version": 3,
|
||||
"subTypeKey": "type",
|
||||
"subTypeVersions": {
|
||||
"group": 0,
|
||||
"text": 1,
|
||||
"bookmark": 2,
|
||||
"draw": 1,
|
||||
"geo": 8,
|
||||
"note": 5,
|
||||
"line": 1,
|
||||
"frame": 0,
|
||||
"arrow": 3,
|
||||
"highlight": 0,
|
||||
"embed": 4,
|
||||
"image": 3,
|
||||
"video": 2
|
||||
}
|
||||
},
|
||||
"instance_presence": { "version": 5 },
|
||||
"pointer": { "version": 1 }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,8 +5,6 @@ category: ui
|
|||
priority: 3
|
||||
---
|
||||
|
||||
...
|
||||
|
||||
---
|
||||
|
||||
The `Tldraw` component can be used inline with a set height and width.
|
||||
|
|
|
@ -358,6 +358,12 @@ export function clockwiseAngleDist(a0: number, a1: number): number;
|
|||
|
||||
export { computed }
|
||||
|
||||
// @internal (undocumented)
|
||||
export function ContainerProvider({ container, children, }: {
|
||||
container: HTMLDivElement;
|
||||
children: React.ReactNode;
|
||||
}): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const coreShapes: readonly [typeof GroupShapeUtil];
|
||||
|
||||
|
@ -907,12 +913,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
visitDescendants(parent: TLPage | TLParentId | TLShape, visitor: (id: TLShapeId) => false | void): this;
|
||||
zoomIn(point?: Vec, animation?: TLAnimationOptions): this;
|
||||
zoomOut(point?: Vec, animation?: TLAnimationOptions): this;
|
||||
zoomToBounds(bounds: Box, targetZoom?: number, animation?: TLAnimationOptions): this;
|
||||
zoomToBounds(bounds: Box, opts?: {
|
||||
targetZoom?: number;
|
||||
inset?: number;
|
||||
} & TLAnimationOptions): this;
|
||||
zoomToContent(): this;
|
||||
zoomToFit(animation?: TLAnimationOptions): this;
|
||||
zoomToSelection(animation?: TLAnimationOptions): this;
|
||||
}
|
||||
|
||||
// @internal (undocumented)
|
||||
export const EditorContext: React_2.Context<Editor>;
|
||||
|
||||
// @public (undocumented)
|
||||
export class Ellipse2d extends Geometry2d {
|
||||
constructor(config: Omit<Geometry2dOptions, 'isClosed'> & {
|
||||
|
@ -1813,6 +1825,7 @@ export type SVGContainerProps = React_3.HTMLAttributes<SVGElement>;
|
|||
// @public (undocumented)
|
||||
export interface SvgExportContext {
|
||||
addExportDef(def: SvgExportDef): void;
|
||||
readonly isDarkMode: boolean;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
|
|
@ -11234,7 +11234,7 @@
|
|||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/editor!Editor#getPageShapeIds:member(1)",
|
||||
"docComment": "/**\n * Get the ids of shapes on a page.\n *\n * @param page - The page (or page id) to get.\n *\n * @example\n * ```ts\n * const idsOnPage1 = editor.getCurrentPageShapeIds('page1')\n * const idsOnPage2 = editor.getCurrentPageShapeIds(myPage2)\n * ```\n *\n * @public\n */\n",
|
||||
"docComment": "/**\n * Get the ids of shapes on a page.\n *\n * @param page - The page (or page id) to get.\n *\n * @example\n * ```ts\n * const idsOnPage1 = editor.getPageShapeIds('page1')\n * const idsOnPage2 = editor.getPageShapeIds(myPage2)\n * ```\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
|
@ -19171,7 +19171,7 @@
|
|||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/editor!Editor#zoomToBounds:member(1)",
|
||||
"docComment": "/**\n * Zoom the camera to fit a bounding box (in the current page space).\n *\n * @param bounds - The bounding box.\n *\n * @param targetZoom - The desired zoom level. Defaults to 0.1.\n *\n * @param animation - The options for an animation.\n *\n * @example\n * ```ts\n * editor.zoomToBounds(myBounds)\n * editor.zoomToBounds(myBounds, 1)\n * editor.zoomToBounds(myBounds, 1, { duration: 100 })\n * ```\n *\n * @public\n */\n",
|
||||
"docComment": "/**\n * Zoom the camera to fit a bounding box (in the current page space).\n *\n * @param bounds - The bounding box.\n *\n * @param options - The options for an animation, target zoom, or custom inset amount.\n *\n * @example\n * ```ts\n * editor.zoomToBounds(myBounds)\n * editor.zoomToBounds(myBounds)\n * editor.zoomToBounds(myBounds, { duration: 100 })\n * editor.zoomToBounds(myBounds, { inset: 0, targetZoom: 1 })\n * ```\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
|
@ -19184,15 +19184,11 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", targetZoom?: "
|
||||
"text": ", opts?: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "number"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", animation?: "
|
||||
"text": "{\n targetZoom?: number;\n inset?: number;\n } & "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -19214,8 +19210,8 @@
|
|||
],
|
||||
"isStatic": false,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 7,
|
||||
"endIndex": 8
|
||||
"startIndex": 6,
|
||||
"endIndex": 7
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"isProtected": false,
|
||||
|
@ -19230,18 +19226,10 @@
|
|||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "targetZoom",
|
||||
"parameterName": "opts",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": true
|
||||
},
|
||||
{
|
||||
"parameterName": "animation",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 6
|
||||
"endIndex": 5
|
||||
},
|
||||
"isOptional": true
|
||||
}
|
||||
|
@ -34180,6 +34168,33 @@
|
|||
}
|
||||
],
|
||||
"name": "addExportDef"
|
||||
},
|
||||
{
|
||||
"kind": "PropertySignature",
|
||||
"canonicalReference": "@tldraw/editor!SvgExportContext#isDarkMode:member",
|
||||
"docComment": "/**\n * Whether the export should be in dark mode.\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "readonly isDarkMode: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "boolean"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isReadonly": true,
|
||||
"isOptional": false,
|
||||
"releaseTag": "Public",
|
||||
"name": "isDarkMode",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
}
|
||||
],
|
||||
"extendsTokenRanges": []
|
||||
|
|
|
@ -253,9 +253,9 @@ export {
|
|||
} from './lib/editor/types/history-types'
|
||||
export { type RequiredKeys, type TLSvgOptions } from './lib/editor/types/misc-types'
|
||||
export { type TLResizeHandle, type TLSelectionHandle } from './lib/editor/types/selection-types'
|
||||
export { useContainer } from './lib/hooks/useContainer'
|
||||
export { ContainerProvider, useContainer } from './lib/hooks/useContainer'
|
||||
export { getCursor } from './lib/hooks/useCursor'
|
||||
export { useEditor } from './lib/hooks/useEditor'
|
||||
export { EditorContext, useEditor } from './lib/hooks/useEditor'
|
||||
export type { TLEditorComponents } from './lib/hooks/useEditorComponents'
|
||||
export { useShallowArrayIdentity, useShallowObjectIdentity } from './lib/hooks/useIdentity'
|
||||
export { useIsCropping } from './lib/hooks/useIsCropping'
|
||||
|
|
|
@ -2176,7 +2176,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
const bounds = this.getSelectionPageBounds() ?? this.getCurrentPageBounds()
|
||||
|
||||
if (bounds) {
|
||||
this.zoomToBounds(bounds, Math.min(1, this.getZoomLevel()), { duration: 220 })
|
||||
this.zoomToBounds(bounds, { targetZoom: Math.min(1, this.getZoomLevel()), duration: 220 })
|
||||
}
|
||||
|
||||
return this
|
||||
|
@ -2202,7 +2202,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
if (ids.length <= 0) return this
|
||||
|
||||
const pageBounds = Box.Common(compact(ids.map((id) => this.getShapePageBounds(id))))
|
||||
this.zoomToBounds(pageBounds, undefined, animation)
|
||||
this.zoomToBounds(pageBounds, animation)
|
||||
return this
|
||||
}
|
||||
|
||||
|
@ -2333,7 +2333,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
const selectionPageBounds = this.getSelectionPageBounds()
|
||||
if (!selectionPageBounds) return this
|
||||
|
||||
this.zoomToBounds(selectionPageBounds, Math.max(1, this.getZoomLevel()), animation)
|
||||
this.zoomToBounds(selectionPageBounds, {
|
||||
targetZoom: Math.max(1, this.getZoomLevel()),
|
||||
...animation,
|
||||
})
|
||||
|
||||
return this
|
||||
}
|
||||
|
@ -2355,7 +2358,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
const viewportPageBounds = this.getViewportPageBounds()
|
||||
|
||||
if (viewportPageBounds.h < selectionBounds.h || viewportPageBounds.w < selectionBounds.w) {
|
||||
this.zoomToBounds(selectionBounds, this.getCamera().z, animation)
|
||||
this.zoomToBounds(selectionBounds, { targetZoom: this.getCamera().z, ...animation })
|
||||
|
||||
return this
|
||||
} else {
|
||||
|
@ -2398,22 +2401,25 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
* @example
|
||||
* ```ts
|
||||
* editor.zoomToBounds(myBounds)
|
||||
* editor.zoomToBounds(myBounds, 1)
|
||||
* editor.zoomToBounds(myBounds, 1, { duration: 100 })
|
||||
* editor.zoomToBounds(myBounds)
|
||||
* editor.zoomToBounds(myBounds, { duration: 100 })
|
||||
* editor.zoomToBounds(myBounds, { inset: 0, targetZoom: 1 })
|
||||
* ```
|
||||
*
|
||||
* @param bounds - The bounding box.
|
||||
* @param targetZoom - The desired zoom level. Defaults to 0.1.
|
||||
* @param animation - The options for an animation.
|
||||
* @param options - The options for an animation, target zoom, or custom inset amount.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
zoomToBounds(bounds: Box, targetZoom?: number, animation?: TLAnimationOptions): this {
|
||||
zoomToBounds(
|
||||
bounds: Box,
|
||||
opts?: { targetZoom?: number; inset?: number } & TLAnimationOptions
|
||||
): this {
|
||||
if (!this.getInstanceState().canMoveCamera) return this
|
||||
|
||||
const viewportScreenBounds = this.getViewportScreenBounds()
|
||||
|
||||
const inset = Math.min(256, viewportScreenBounds.width * 0.28)
|
||||
const inset = opts?.inset ?? Math.min(256, viewportScreenBounds.width * 0.28)
|
||||
|
||||
let zoom = clamp(
|
||||
Math.min(
|
||||
|
@ -2424,8 +2430,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
MAX_ZOOM
|
||||
)
|
||||
|
||||
if (targetZoom !== undefined) {
|
||||
zoom = Math.min(targetZoom, zoom)
|
||||
if (opts?.targetZoom !== undefined) {
|
||||
zoom = Math.min(opts.targetZoom, zoom)
|
||||
}
|
||||
|
||||
this.setCamera(
|
||||
|
@ -2434,7 +2440,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
y: -bounds.minY + (viewportScreenBounds.height - bounds.height * zoom) / 2 / zoom,
|
||||
z: zoom,
|
||||
},
|
||||
animation
|
||||
opts
|
||||
)
|
||||
|
||||
return this
|
||||
|
@ -3140,9 +3146,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
}
|
||||
}
|
||||
|
||||
for (const childId of this.getSortedChildIdsForParent(this.getCurrentPageId())) {
|
||||
// If we're using editor state, then we're only interested in on-screen shapes.
|
||||
// If we're not using the editor state, then we're interested in ALL shapes, even those from other pages.
|
||||
const pages = useEditorState ? [this.getCurrentPage()] : this.getPages()
|
||||
for (const page of pages) {
|
||||
for (const childId of this.getSortedChildIdsForParent(page.id)) {
|
||||
addShapeById(childId, 1, false)
|
||||
}
|
||||
}
|
||||
|
||||
return renderingShapes
|
||||
}
|
||||
|
@ -3295,8 +3306,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const idsOnPage1 = editor.getCurrentPageShapeIds('page1')
|
||||
* const idsOnPage2 = editor.getCurrentPageShapeIds(myPage2)
|
||||
* const idsOnPage1 = editor.getPageShapeIds('page1')
|
||||
* const idsOnPage2 = editor.getPageShapeIds(myPage2)
|
||||
* ```
|
||||
*
|
||||
* @param page - The page (or page id) to get.
|
||||
|
@ -8048,8 +8059,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
preserveAspectRatio = false,
|
||||
} = opts
|
||||
|
||||
// todo: we shouldn't depend on the public theme here
|
||||
const theme = getDefaultColorTheme({ isDarkMode: this.user.getIsDarkMode() })
|
||||
const isDarkMode = opts.darkMode ?? this.user.getIsDarkMode()
|
||||
const theme = getDefaultColorTheme({ isDarkMode })
|
||||
|
||||
// ---Figure out which shapes we need to include
|
||||
const shapeIdsToInclude = this.getShapeAndDescendantIds(ids)
|
||||
|
@ -8127,6 +8138,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
|
||||
const exportDefPromisesById = new Map<string, Promise<void>>()
|
||||
const exportContext: SvgExportContext = {
|
||||
isDarkMode,
|
||||
addExportDef: (def: SvgExportDef) => {
|
||||
if (exportDefPromisesById.has(def.key)) return
|
||||
const promise = (async () => {
|
||||
|
|
|
@ -11,4 +11,9 @@ export interface SvgExportContext {
|
|||
* key. If multiple defs come with the same key, only one will be added.
|
||||
*/
|
||||
addExportDef(def: SvgExportDef): void
|
||||
|
||||
/**
|
||||
* Whether the export should be in dark mode.
|
||||
*/
|
||||
readonly isDarkMode: boolean
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react'
|
||||
import { Editor } from '../editor/Editor'
|
||||
|
||||
/** @internal */
|
||||
export const EditorContext = React.createContext({} as Editor)
|
||||
|
||||
/** @public */
|
||||
|
|
|
@ -19,6 +19,7 @@ import { Editor } from '@tldraw/editor';
|
|||
import { EMBED_DEFINITIONS } from '@tldraw/editor';
|
||||
import { EmbedDefinition } from '@tldraw/editor';
|
||||
import { EnumStyleProp } from '@tldraw/editor';
|
||||
import { Expand } from '@tldraw/editor';
|
||||
import { Geometry2d } from '@tldraw/editor';
|
||||
import { Group2d } from '@tldraw/editor';
|
||||
import { HandleSnapGeometry } from '@tldraw/editor';
|
||||
|
@ -91,6 +92,7 @@ import { TLOnResizeEndHandler } from '@tldraw/editor';
|
|||
import { TLOnResizeHandler } from '@tldraw/editor';
|
||||
import { TLOnTranslateHandler } from '@tldraw/editor';
|
||||
import { TLOnTranslateStartHandler } from '@tldraw/editor';
|
||||
import { TLPageId } from '@tldraw/editor';
|
||||
import { TLParentId } from '@tldraw/editor';
|
||||
import { TLPointerEvent } from '@tldraw/editor';
|
||||
import { TLPointerEventInfo } from '@tldraw/editor';
|
||||
|
@ -534,7 +536,7 @@ export class FrameShapeUtil extends BaseBoxShapeUtil<TLFrameShape> {
|
|||
// (undocumented)
|
||||
providesBackgroundForChildren(): boolean;
|
||||
// (undocumented)
|
||||
toSvg(shape: TLFrameShape): Promise<SVGElement> | SVGElement;
|
||||
toSvg(shape: TLFrameShape, ctx: SvgExportContext): Promise<SVGElement> | SVGElement;
|
||||
// (undocumented)
|
||||
static type: "frame";
|
||||
}
|
||||
|
@ -698,6 +700,9 @@ export function getSvgAsImage(svg: SVGElement, isSafari: boolean, options: {
|
|||
scale: number;
|
||||
}): Promise<Blob | null>;
|
||||
|
||||
// @public (undocumented)
|
||||
export function getSvgAsString(svg: SVGElement): Promise<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export class HandTool extends StateNode {
|
||||
// (undocumented)
|
||||
|
@ -764,7 +769,7 @@ export class HighlightShapeUtil extends ShapeUtil<TLHighlightShape> {
|
|||
// (undocumented)
|
||||
toBackgroundSvg(shape: TLHighlightShape): SVGPathElement;
|
||||
// (undocumented)
|
||||
toSvg(shape: TLHighlightShape): SVGPathElement;
|
||||
toSvg(shape: TLHighlightShape, ctx: SvgExportContext): SVGPathElement;
|
||||
// (undocumented)
|
||||
static type: "highlight";
|
||||
}
|
||||
|
@ -891,7 +896,7 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
|
|||
handles: DictValidator<IndexKey, VecModel>;
|
||||
};
|
||||
// (undocumented)
|
||||
toSvg(shape: TLLineShape): SVGGElement;
|
||||
toSvg(shape: TLLineShape, ctx: SvgExportContext): SVGGElement;
|
||||
// (undocumented)
|
||||
static type: "line";
|
||||
}
|
||||
|
@ -1199,6 +1204,28 @@ export const TldrawHandles: TLHandlesComponent;
|
|||
// @public (undocumented)
|
||||
export const TldrawHoveredShapeIndicator: TLHoveredShapeIndicatorComponent;
|
||||
|
||||
// @public
|
||||
export const TldrawImage: NamedExoticComponent< {
|
||||
snapshot: StoreSnapshot<TLRecord>;
|
||||
format?: "png" | "svg" | undefined;
|
||||
pageId?: TLPageId | undefined;
|
||||
shapeUtils?: readonly TLAnyShapeUtilConstructor[] | undefined;
|
||||
bounds?: Box | undefined;
|
||||
scale?: number | undefined;
|
||||
background?: boolean | undefined;
|
||||
padding?: number | undefined;
|
||||
darkMode?: boolean | undefined;
|
||||
preserveAspectRatio?: string | undefined;
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export type TldrawImageProps = Expand<{
|
||||
snapshot: StoreSnapshot<TLRecord>;
|
||||
format?: 'png' | 'svg';
|
||||
pageId?: TLPageId;
|
||||
shapeUtils?: readonly TLAnyShapeUtilConstructor[];
|
||||
} & Partial<TLSvgOptions>>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TldrawProps = (Omit<TldrawUiProps, 'components'> & Omit<TldrawEditorBaseProps, 'components'> & {
|
||||
components?: TLComponents;
|
||||
|
|
|
@ -6321,6 +6321,15 @@
|
|||
"text": "TLFrameShape",
|
||||
"canonicalReference": "@tldraw/tlschema!TLFrameShape:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", ctx: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "SvgExportContext",
|
||||
"canonicalReference": "@tldraw/editor!SvgExportContext:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
|
@ -6355,8 +6364,8 @@
|
|||
],
|
||||
"isStatic": false,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 8
|
||||
"startIndex": 5,
|
||||
"endIndex": 10
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"isProtected": false,
|
||||
|
@ -6369,6 +6378,14 @@
|
|||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "ctx",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"isOptional": false,
|
||||
|
@ -7721,6 +7738,57 @@
|
|||
],
|
||||
"name": "getSvgAsImage"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/tldraw!getSvgAsString:function(1)",
|
||||
"docComment": "/**\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getSvgAsString(svg: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "SVGElement",
|
||||
"canonicalReference": "!SVGElement:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "Promise",
|
||||
"canonicalReference": "!Promise:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<string>"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/utils/export/export.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 5
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "svg",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "getSvgAsString"
|
||||
},
|
||||
{
|
||||
"kind": "Class",
|
||||
"canonicalReference": "@tldraw/tldraw!HandTool:class",
|
||||
|
@ -8802,6 +8870,15 @@
|
|||
"text": "TLHighlightShape",
|
||||
"canonicalReference": "@tldraw/tlschema!TLHighlightShape:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", ctx: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "SvgExportContext",
|
||||
"canonicalReference": "@tldraw/editor!SvgExportContext:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
|
@ -8818,8 +8895,8 @@
|
|||
],
|
||||
"isStatic": false,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
"startIndex": 5,
|
||||
"endIndex": 6
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"isProtected": false,
|
||||
|
@ -8832,6 +8909,14 @@
|
|||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "ctx",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"isOptional": false,
|
||||
|
@ -10650,6 +10735,15 @@
|
|||
"text": "TLLineShape",
|
||||
"canonicalReference": "@tldraw/tlschema!TLLineShape:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", ctx: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "SvgExportContext",
|
||||
"canonicalReference": "@tldraw/editor!SvgExportContext:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
|
@ -10666,8 +10760,8 @@
|
|||
],
|
||||
"isStatic": false,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
"startIndex": 5,
|
||||
"endIndex": 6
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"isProtected": false,
|
||||
|
@ -10680,6 +10774,14 @@
|
|||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "ctx",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"isOptional": false,
|
||||
|
@ -13930,6 +14032,168 @@
|
|||
"endIndex": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawImage:var",
|
||||
"docComment": "/**\n * A renderered SVG image of a Tldraw snapshot.\n *\n * @example\n * ```tsx\n * <TldrawImage snapshot={snapshot} />\n * \tsnapshot={snapshot}\n * \tpageId={pageId}\n * \tbackground={false}\n * darkMode={true}\n * bounds={new Box(0,0,600,400)}\n * scale={1}\n * />\n * ```\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "TldrawImage: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "import(\"react\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "NamedExoticComponent",
|
||||
"canonicalReference": "@types/react!React.NamedExoticComponent:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<{\n snapshot: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "StoreSnapshot",
|
||||
"canonicalReference": "@tldraw/store!StoreSnapshot:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<"
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLRecord",
|
||||
"canonicalReference": "@tldraw/tlschema!TLRecord:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ">;\n format?: \"png\" | \"svg\" | undefined;\n pageId?: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLPageId",
|
||||
"canonicalReference": "@tldraw/tlschema!TLPageId:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": " | undefined;\n shapeUtils?: readonly "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLAnyShapeUtilConstructor",
|
||||
"canonicalReference": "@tldraw/editor!TLAnyShapeUtilConstructor:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "[] | undefined;\n bounds?: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "Box",
|
||||
"canonicalReference": "@tldraw/editor!Box:class"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": " | undefined;\n scale?: number | undefined;\n background?: boolean | undefined;\n padding?: number | undefined;\n darkMode?: boolean | undefined;\n preserveAspectRatio?: string | undefined;\n}>"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/TldrawImage.tsx",
|
||||
"isReadonly": true,
|
||||
"releaseTag": "Public",
|
||||
"name": "TldrawImage",
|
||||
"variableTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "TypeAlias",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawImageProps:type",
|
||||
"docComment": "/**\n * Props for the {@link @tldraw/tldraw#TldrawImage} component.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export type TldrawImageProps = "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "Expand",
|
||||
"canonicalReference": "@tldraw/utils!Expand:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<{\n snapshot: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "StoreSnapshot",
|
||||
"canonicalReference": "@tldraw/store!StoreSnapshot:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<"
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLRecord",
|
||||
"canonicalReference": "@tldraw/tlschema!TLRecord:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ">;\n format?: 'png' | 'svg';\n pageId?: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLPageId",
|
||||
"canonicalReference": "@tldraw/tlschema!TLPageId:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n shapeUtils?: readonly "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLAnyShapeUtilConstructor",
|
||||
"canonicalReference": "@tldraw/editor!TLAnyShapeUtilConstructor:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "[];\n} & "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "Partial",
|
||||
"canonicalReference": "!Partial:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<"
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLSvgOptions",
|
||||
"canonicalReference": "@tldraw/editor!TLSvgOptions:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ">>"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/TldrawImage.tsx",
|
||||
"releaseTag": "Public",
|
||||
"name": "TldrawImageProps",
|
||||
"typeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 15
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "TypeAlias",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawProps:type",
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// eslint-disable-next-line local/no-export-star
|
||||
export * from '@tldraw/editor'
|
||||
export { Tldraw, type TldrawProps } from './lib/Tldraw'
|
||||
export { TldrawImage, type TldrawImageProps } from './lib/TldrawImage'
|
||||
export { TldrawCropHandles, type TldrawCropHandlesProps } from './lib/canvas/TldrawCropHandles'
|
||||
export { TldrawHandles } from './lib/canvas/TldrawHandles'
|
||||
export { TldrawHoveredShapeIndicator } from './lib/canvas/TldrawHoveredShapeIndicator'
|
||||
|
@ -110,7 +111,7 @@ export {
|
|||
} from './lib/utils/assets/assets'
|
||||
export { getEmbedInfo } from './lib/utils/embeds/embeds'
|
||||
export { copyAs } from './lib/utils/export/copyAs'
|
||||
export { getSvgAsImage } from './lib/utils/export/export'
|
||||
export { getSvgAsImage, getSvgAsString } from './lib/utils/export/export'
|
||||
export { exportAs } from './lib/utils/export/exportAs'
|
||||
export { fitFrameToContent, removeFrame } from './lib/utils/frames/frames'
|
||||
export { setDefaultEditorAssetUrls } from './lib/utils/static-assets/assetUrls'
|
||||
|
|
178
packages/tldraw/src/lib/TldrawImage.tsx
Normal file
178
packages/tldraw/src/lib/TldrawImage.tsx
Normal file
|
@ -0,0 +1,178 @@
|
|||
import {
|
||||
Editor,
|
||||
ErrorScreen,
|
||||
Expand,
|
||||
LoadingScreen,
|
||||
StoreSnapshot,
|
||||
TLAnyShapeUtilConstructor,
|
||||
TLPageId,
|
||||
TLRecord,
|
||||
TLSvgOptions,
|
||||
useShallowArrayIdentity,
|
||||
useTLStore,
|
||||
} from '@tldraw/editor'
|
||||
import { memo, useLayoutEffect, useMemo, useState } from 'react'
|
||||
import { defaultShapeUtils } from './defaultShapeUtils'
|
||||
import { usePreloadAssets } from './ui/hooks/usePreloadAssets'
|
||||
import { getSvgAsImage, getSvgAsString } from './utils/export/export'
|
||||
import { useDefaultEditorAssetsWithOverrides } from './utils/static-assets/assetUrls'
|
||||
|
||||
/**
|
||||
* Props for the {@link @tldraw/tldraw#TldrawImage} component.
|
||||
*
|
||||
* @public
|
||||
**/
|
||||
export type TldrawImageProps = Expand<
|
||||
{
|
||||
/**
|
||||
* The snapshot to display.
|
||||
*/
|
||||
snapshot: StoreSnapshot<TLRecord>
|
||||
|
||||
/**
|
||||
* The image format to use. Defaults to 'svg'.
|
||||
*/
|
||||
format?: 'svg' | 'png'
|
||||
|
||||
/**
|
||||
* The page to display. Defaults to the first page.
|
||||
*/
|
||||
pageId?: TLPageId
|
||||
|
||||
/**
|
||||
* Additional shape utils to use.
|
||||
*/
|
||||
shapeUtils?: readonly TLAnyShapeUtilConstructor[]
|
||||
} & Partial<TLSvgOptions>
|
||||
>
|
||||
|
||||
/**
|
||||
* A renderered SVG image of a Tldraw snapshot.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <TldrawImage snapshot={snapshot} />
|
||||
* snapshot={snapshot}
|
||||
* pageId={pageId}
|
||||
* background={false}
|
||||
* darkMode={true}
|
||||
* bounds={new Box(0,0,600,400)}
|
||||
* scale={1}
|
||||
* />
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const TldrawImage = memo(function TldrawImage(props: TldrawImageProps) {
|
||||
const [url, setUrl] = useState<string | null>(null)
|
||||
const [container, setContainer] = useState<HTMLDivElement | null>(null)
|
||||
|
||||
const shapeUtils = useShallowArrayIdentity(props.shapeUtils ?? [])
|
||||
const shapeUtilsWithDefaults = useMemo(() => [...defaultShapeUtils, ...shapeUtils], [shapeUtils])
|
||||
const store = useTLStore({ snapshot: props.snapshot, shapeUtils: shapeUtilsWithDefaults })
|
||||
|
||||
const assets = useDefaultEditorAssetsWithOverrides()
|
||||
const { done: preloadingComplete, error: preloadingError } = usePreloadAssets(assets)
|
||||
|
||||
const {
|
||||
pageId,
|
||||
bounds,
|
||||
scale,
|
||||
background,
|
||||
padding,
|
||||
darkMode,
|
||||
preserveAspectRatio,
|
||||
format = 'svg',
|
||||
} = props
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!container) return
|
||||
if (!store) return
|
||||
if (!preloadingComplete) return
|
||||
|
||||
let isCancelled = false
|
||||
|
||||
const tempElm = document.createElement('div')
|
||||
container.appendChild(tempElm)
|
||||
container.classList.add('tl-container', 'tl-theme__light')
|
||||
|
||||
const editor = new Editor({
|
||||
store,
|
||||
shapeUtils: shapeUtilsWithDefaults ?? [],
|
||||
tools: [],
|
||||
getContainer: () => tempElm,
|
||||
})
|
||||
|
||||
if (pageId) editor.setCurrentPage(pageId)
|
||||
|
||||
const shapeIds = editor.getCurrentPageShapeIds()
|
||||
|
||||
async function setSvg() {
|
||||
const svg = await editor.getSvg([...shapeIds], {
|
||||
bounds,
|
||||
scale,
|
||||
background,
|
||||
padding,
|
||||
darkMode,
|
||||
preserveAspectRatio,
|
||||
})
|
||||
|
||||
if (svg && !isCancelled) {
|
||||
if (format === 'svg') {
|
||||
const string = await getSvgAsString(svg)
|
||||
if (!isCancelled) {
|
||||
const blob = new Blob([string], { type: 'image/svg+xml' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
setUrl(url)
|
||||
}
|
||||
} else if (format === 'png') {
|
||||
const blob = await getSvgAsImage(svg, editor.environment.isSafari, {
|
||||
type: format,
|
||||
quality: 1,
|
||||
scale: 2,
|
||||
})
|
||||
if (blob && !isCancelled) {
|
||||
const url = URL.createObjectURL(blob)
|
||||
setUrl(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editor.dispose()
|
||||
}
|
||||
|
||||
setSvg()
|
||||
|
||||
return () => {
|
||||
isCancelled = true
|
||||
}
|
||||
}, [
|
||||
format,
|
||||
container,
|
||||
store,
|
||||
shapeUtilsWithDefaults,
|
||||
pageId,
|
||||
bounds,
|
||||
scale,
|
||||
background,
|
||||
padding,
|
||||
darkMode,
|
||||
preserveAspectRatio,
|
||||
preloadingComplete,
|
||||
preloadingError,
|
||||
])
|
||||
|
||||
if (preloadingError) {
|
||||
return <ErrorScreen>Could not load assets.</ErrorScreen>
|
||||
}
|
||||
|
||||
if (!preloadingComplete) {
|
||||
return <LoadingScreen>Loading assets...</LoadingScreen>
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={setContainer} style={{ position: 'relative', width: '100%', height: '100%' }}>
|
||||
{url && <img src={url} style={{ width: '100%', height: '100%' }} />}
|
||||
</div>
|
||||
)
|
||||
})
|
|
@ -840,7 +840,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
}
|
||||
|
||||
override toSvg(shape: TLArrowShape, ctx: SvgExportContext) {
|
||||
const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.getIsDarkMode() })
|
||||
const theme = getDefaultColorTheme({ isDarkMode: ctx.isDarkMode })
|
||||
ctx.addExportDef(getFillDefForExport(shape.props.fill, theme))
|
||||
|
||||
const color = theme[shape.props.color].solid
|
||||
|
|
|
@ -187,7 +187,7 @@ export class DrawShapeUtil extends ShapeUtil<TLDrawShape> {
|
|||
}
|
||||
|
||||
override toSvg(shape: TLDrawShape, ctx: SvgExportContext) {
|
||||
const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.getIsDarkMode() })
|
||||
const theme = getDefaultColorTheme({ isDarkMode: ctx.isDarkMode })
|
||||
ctx.addExportDef(getFillDefForExport(shape.props.fill, theme))
|
||||
|
||||
const { color } = shape.props
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
Rectangle2d,
|
||||
SVGContainer,
|
||||
SelectionEdge,
|
||||
SvgExportContext,
|
||||
TLFrameShape,
|
||||
TLGroupShape,
|
||||
TLOnResizeEndHandler,
|
||||
|
@ -96,8 +97,8 @@ export class FrameShapeUtil extends BaseBoxShapeUtil<TLFrameShape> {
|
|||
)
|
||||
}
|
||||
|
||||
override toSvg(shape: TLFrameShape): SVGElement | Promise<SVGElement> {
|
||||
const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.getIsDarkMode() })
|
||||
override toSvg(shape: TLFrameShape, ctx: SvgExportContext): SVGElement | Promise<SVGElement> {
|
||||
const theme = getDefaultColorTheme({ isDarkMode: ctx.isDarkMode })
|
||||
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g')
|
||||
|
||||
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
|
||||
|
|
|
@ -618,7 +618,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|||
override toSvg(shape: TLGeoShape, ctx: SvgExportContext) {
|
||||
const { id, props } = shape
|
||||
const strokeWidth = STROKE_SIZES[props.size]
|
||||
const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.getIsDarkMode() })
|
||||
const theme = getDefaultColorTheme({ isDarkMode: ctx.isDarkMode })
|
||||
ctx.addExportDef(getFillDefForExport(shape.props.fill, theme))
|
||||
|
||||
let svgElm: SVGElement
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
Polygon2d,
|
||||
SVGContainer,
|
||||
ShapeUtil,
|
||||
SvgExportContext,
|
||||
TLDefaultColorTheme,
|
||||
TLDrawShapeSegment,
|
||||
TLHighlightShape,
|
||||
|
@ -116,8 +117,8 @@ export class HighlightShapeUtil extends ShapeUtil<TLHighlightShape> {
|
|||
return <path d={strokePath} />
|
||||
}
|
||||
|
||||
override toSvg(shape: TLHighlightShape) {
|
||||
const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.getIsDarkMode() })
|
||||
override toSvg(shape: TLHighlightShape, ctx: SvgExportContext) {
|
||||
const theme = getDefaultColorTheme({ isDarkMode: ctx.isDarkMode })
|
||||
return highlighterToSvg(getStrokeWidth(shape), shape, OVERLAY_OPACITY, theme)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
Polyline2d,
|
||||
SVGContainer,
|
||||
ShapeUtil,
|
||||
SvgExportContext,
|
||||
TLHandle,
|
||||
TLLineShape,
|
||||
TLOnHandleDragHandler,
|
||||
|
@ -306,8 +307,8 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
|
|||
return <path d={path} />
|
||||
}
|
||||
|
||||
override toSvg(shape: TLLineShape) {
|
||||
const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.getIsDarkMode() })
|
||||
override toSvg(shape: TLLineShape, ctx: SvgExportContext) {
|
||||
const theme = getDefaultColorTheme({ isDarkMode: ctx.isDarkMode })
|
||||
const color = theme[shape.props.color].solid
|
||||
const spline = getGeometryForLineShape(shape)
|
||||
const strokeWidth = STROKE_SIZES[shape.props.size]
|
||||
|
|
|
@ -112,7 +112,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
|||
|
||||
override toSvg(shape: TLNoteShape, ctx: SvgExportContext) {
|
||||
ctx.addExportDef(getFontDefForExport(shape.props.font))
|
||||
const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.getIsDarkMode() })
|
||||
const theme = getDefaultColorTheme({ isDarkMode: ctx.isDarkMode })
|
||||
const bounds = this.editor.getShapeGeometry(shape).bounds
|
||||
|
||||
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g')
|
||||
|
|
|
@ -150,7 +150,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
|||
override toSvg(shape: TLTextShape, ctx: SvgExportContext) {
|
||||
ctx.addExportDef(getFontDefForExport(shape.props.font))
|
||||
|
||||
const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.getIsDarkMode() })
|
||||
const theme = getDefaultColorTheme({ isDarkMode: ctx.isDarkMode })
|
||||
const bounds = this.editor.getShapeGeometry(shape).bounds
|
||||
const text = shape.props.text
|
||||
|
||||
|
|
|
@ -53,8 +53,8 @@ export class ZoomBrushing extends StateNode {
|
|||
this.editor.zoomIn(point, { duration: 220 })
|
||||
}
|
||||
} else {
|
||||
const zoomLevel = this.editor.inputs.altKey ? this.editor.getZoomLevel() / 2 : undefined
|
||||
this.editor.zoomToBounds(zoomBrush, zoomLevel, { duration: 220 })
|
||||
const targetZoom = this.editor.inputs.altKey ? this.editor.getZoomLevel() / 2 : undefined
|
||||
this.editor.zoomToBounds(zoomBrush, { targetZoom, duration: 220 })
|
||||
}
|
||||
|
||||
this.parent.transition('idle', this.info)
|
||||
|
|
|
@ -91,7 +91,8 @@ export async function getSvgAsImage(
|
|||
})
|
||||
}
|
||||
|
||||
async function getSvgAsString(svg: SVGElement) {
|
||||
/** @public */
|
||||
export async function getSvgAsString(svg: SVGElement) {
|
||||
const clone = svg.cloneNode(true) as SVGGraphicsElement
|
||||
|
||||
svg.setAttribute('width', +svg.getAttribute('width')! + '')
|
||||
|
|
|
@ -591,7 +591,7 @@ export function buildFromV1Document(editor: Editor, document: LegacyTldrawDocume
|
|||
|
||||
const bounds = editor.getCurrentPageBounds()
|
||||
if (bounds) {
|
||||
editor.zoomToBounds(bounds, 1)
|
||||
editor.zoomToBounds(bounds, { targetZoom: 1 })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -296,7 +296,7 @@ export async function parseAndLoadDocument(
|
|||
|
||||
const bounds = editor.getCurrentPageBounds()
|
||||
if (bounds) {
|
||||
editor.zoomToBounds(bounds, 1)
|
||||
editor.zoomToBounds(bounds, { targetZoom: 1 })
|
||||
}
|
||||
editor.updateInstanceState({ isFocused })
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue