Skip to content

Commit 32d04ed

Browse files
docs: update examples on recent changes (tldraw#8737)
Updates the examples pages and readmes based on the changes over the last couple of months. Adds in a new example showing how you can add in a color picker and font picker
1 parent 6ed7b78 commit 32d04ed

24 files changed

Lines changed: 909 additions & 190 deletions

File tree

apps/examples/src/examples/collaboration/sync-private-content/style.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
display: flex;
1313
align-items: center;
1414
gap: 10px;
15+
pointer-events: auto;
1516
}
1617

1718
.toggle-panel button {

apps/examples/src/examples/configuration/asset-props/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ Control the assets (images, videos, etc.) that can be added to the canvas.
2020

2121
---
2222

23-
This example demonstrates the `<Tldraw/>` component's props that give you control over assets: which types are allowed, the maximum size, and maximum dimensions.
23+
This example demonstrates the `<Tldraw/>` component's props that give you control over assets: which types are allowed (image only here), the maximum size (set to 1 MB), and maximum dimensions (no limit).

apps/examples/src/examples/configuration/configure-shape-util/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ Change the behavior of built-in shapes by setting their options.
2020

2121
Some of the builtin tldraw shapes can be customized to behave differently based on your needs. This is done via the `ShapeUtil.configure` function which returns a new version of the shape's util class with custom options specified.
2222

23-
You can see a shape's options by looking at the `options` property of its `ShapeUtil`. For example, the note shape's options are listed at [`NoteShapeOptions`](https://tldraw.dev/reference/tldraw/NoteShapeOptions).
23+
You can see a shape's options by looking at the `options` property of its `ShapeUtil`. For example, the note shape's options are listed at [`NoteShapeOptions`](https://tldraw.dev/reference/tldraw/NoteShapeOptions). In this example the note shape's options are modified so the note scales as text is added.

apps/examples/src/examples/configuration/custom-embed/CustomEmbedExample.tsx

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import {
33
DEFAULT_EMBED_DEFINITIONS,
44
DefaultEmbedDefinitionType,
55
EmbedShapeUtil,
6+
TLComponents,
67
Tldraw,
8+
TldrawUiButton,
9+
TldrawUiButtonLabel,
10+
useActions,
711
} from 'tldraw'
812
import 'tldraw/tldraw.css'
913

@@ -48,11 +52,32 @@ const customEmbed: CustomEmbedDefinition = {
4852
const embeds = [...defaultEmbedsToKeep, customEmbed]
4953
const shapeUtils = [EmbedShapeUtil.configure({ embedDefinitions: embeds })]
5054

55+
// [4]
56+
function InsertEmbedButton() {
57+
const actions = useActions()
58+
const insertEmbed = actions['insert-embed']
59+
return (
60+
<div style={{ pointerEvents: 'all' }}>
61+
<TldrawUiButton
62+
type="normal"
63+
onClick={() => insertEmbed.onSelect('helper-buttons')}
64+
style={{ backgroundColor: '#e5e5e5' }}
65+
>
66+
<TldrawUiButtonLabel>Insert embed</TldrawUiButtonLabel>
67+
</TldrawUiButton>
68+
</div>
69+
)
70+
}
71+
72+
const components: TLComponents = {
73+
TopPanel: InsertEmbedButton,
74+
}
75+
5176
export default function CustomEmbedExample() {
5277
return (
5378
<div className="tldraw__editor">
54-
{/* [4] */}
55-
<Tldraw shapeUtils={shapeUtils} />
79+
{/* [5] */}
80+
<Tldraw shapeUtils={shapeUtils} components={components} />
5681
</div>
5782
)
5883
}
@@ -66,9 +91,12 @@ tldraw has built-in support for embedding content from several popular apps. In
6691
We will also add support for embedding JSFiddles. Please note that you have to specify an icon that will be displayed in the `EmbedDialog` component.
6792
6893
[3]
69-
We concatenate the filtered embed definitions with our custom JSFiddle one.
94+
We concatenate the filtered embed definitions with our custom JSFiddle one.
7095
7196
[4]
72-
We configure `EmbedShapeUtil` with our custom embed definitions and pass the configured util via `shapeUtils`.
97+
For convenience, we add an "Insert embed" button to the editor's top panel. It uses the `useActions` hook to trigger the built-in `insert-embed` action, which opens the same embed dialog you'd get from the menu — a quick way to see the available embed options.
98+
99+
[5]
100+
We configure `EmbedShapeUtil` with our custom embed definitions and pass the configured util via `shapeUtils`. We also pass our custom `components` to render the button in the `TopPanel` slot.
73101
74102
*/

apps/examples/src/examples/configuration/deep-links/README.md

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,34 +21,36 @@ Allow linking to specific parts of a tldraw canvas.
2121

2222
Deep Links are URLs which point to a specific part of a document. We provide a comprehensive set of tools to help you create and manage deep links in your application.
2323

24-
## The `deepLinks` prop
24+
## The `deepLinks` option
2525

26-
The highest-level API for managing deep links is the `deepLinks` prop on the `<Tldraw />` component. This prop is designed for manipulating `window.location` to add a search param which tldraw can use to navigate to a specific part of the document.
26+
The highest-level API for managing deep links is the `deepLinks` option on the `<Tldraw />` component's `options` prop. This option is designed for manipulating `window.location` to add a search param which tldraw can use to navigate to a specific part of the document.
2727

2828
e.g. `https://my-app.com/document-name?d=v1234.-234.3.21`
2929

30-
If you set `deepLinks` to `true` e.g. `<Tldraw deepLinks />` the following default behavior will be enabled:
30+
If you set `deepLinks` to `true` e.g. `<Tldraw options={{ deepLinks: true }} />` the following default behavior will be enabled:
3131

3232
1. When the editor initializes, before the initial render, it will check the current `window.location` for a search param called `d`. If found, it will try to parse the value of this param as a deep link and navigate to that part of the document.
3333
2. 500 milliseconds after every time the editor finishes navigating to a new part of the document, it will update `window.location` to add the latest version of the `d` param.
3434

35-
You can customize this behavior by passing a configuration object as the `deepLinks` prop. e.g.
35+
You can customize this behavior by passing a configuration object as the `deepLinks` option. e.g.
3636

3737
```tsx
3838
<Tldraw
39-
deepLinks={{
40-
// change the param name to `page`
41-
paramName: 'page',
42-
// only link to the current page
43-
getTarget(editor) {
44-
return { type: 'page', pageId: editor.getCurrentPageId() }
39+
options={{
40+
deepLinks: {
41+
// change the param name to `page`
42+
paramName: 'page',
43+
// only link to the current page
44+
getTarget(editor) {
45+
return { type: 'page', pageId: editor.getCurrentPageId() }
46+
},
47+
// log the new search params to the console instead of updating `window.location`
48+
onChange(url) {
49+
console.log('the new search params are', url.searchParams)
50+
},
51+
// set the debounce interval to 100ms instead of 500ms
52+
debounceMs: 100,
4553
},
46-
// log the new search params to the console instead of updating `window.location`
47-
onChange(url) {
48-
console.log('the new search params are', url.searchParams)
49-
},
50-
// set the debounce interval to 100ms instead of 500ms
51-
debounceMs: 100,
5254
}}
5355
/>
5456
```
@@ -106,7 +108,7 @@ editor.navigateToDeepLink()
106108

107109
### Listening for deep link changes
108110

109-
You can listen for deep link changes with the [`Editor#registerDeepLinkListener`](?) method, which takes the same options as the `deepLinks` prop.
111+
You can listen for deep link changes with the [`Editor#registerDeepLinkListener`](?) method, which takes the same options as the `deepLinks` option.
110112

111113
```tsx
112114
useEffect(() => {
@@ -125,3 +127,7 @@ useEffect(() => {
125127
}
126128
}, [])
127129
```
130+
131+
### Trying deep links in this demo
132+
133+
Create a shape on the canvas then pan or zoom. The `d` param in the URL updates as you go. Copy that URL into a new tab to return to the same view later.

apps/examples/src/examples/configuration/resize-note/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ Make the note shape resizable.
1919

2020
---
2121

22-
The editor's options include alternative resizing behavior for note shapes. Set `resizeMode` to either `none` for the default behavior or `scale` to allow a user to scale the note.
22+
The editor's options include alternative resizing behavior for note shapes. Set `resizeMode` to either `none` for the default behavior or `scale` to allow a user to scale the note. Here you can drag the note to expand it.

apps/examples/src/examples/data/assets/custom-records/CustomRecordsExample.tsx

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -79,23 +79,6 @@ const MarkerOverlay = track(function MarkerOverlay() {
7979
.allRecords()
8080
.filter((r) => isCustomRecord(MARKER_TYPE, r)) as any as Marker[]
8181

82-
const addMarker = useCallback(() => {
83-
const label = prompt('Marker label:')
84-
if (!label) return
85-
const center = editor.getViewportScreenCenter()
86-
const point = editor.screenToPage(center)
87-
editor.store.put([
88-
{
89-
id: createMarkerId(),
90-
typeName: MARKER_TYPE,
91-
x: point.x,
92-
y: point.y,
93-
label,
94-
icon: ICONS[Math.floor(Math.random() * ICONS.length)],
95-
} as any,
96-
])
97-
}, [editor])
98-
9982
return (
10083
<>
10184
{markers.map((marker) => {
@@ -141,27 +124,48 @@ const MarkerOverlay = track(function MarkerOverlay() {
141124
</div>
142125
)
143126
})}
144-
<button
145-
onClick={addMarker}
146-
style={{
147-
position: 'absolute',
148-
top: 50,
149-
right: 10,
150-
zIndex: 1000,
151-
padding: '6px 12px',
152-
borderRadius: 6,
153-
border: '1px solid #ccc',
154-
background: 'white',
155-
cursor: 'pointer',
156-
fontSize: 14,
157-
}}
158-
>
159-
+ Add marker
160-
</button>
161127
</>
162128
)
163129
})
164130

131+
function AddMarkerButton() {
132+
const editor = useEditor()
133+
134+
const addMarker = useCallback(() => {
135+
const label = prompt('Marker label:')
136+
if (!label) return
137+
const center = editor.getViewportScreenCenter()
138+
const point = editor.screenToPage(center)
139+
editor.store.put([
140+
{
141+
id: createMarkerId(),
142+
typeName: MARKER_TYPE,
143+
x: point.x,
144+
y: point.y,
145+
label,
146+
icon: ICONS[Math.floor(Math.random() * ICONS.length)],
147+
} as any,
148+
])
149+
}, [editor])
150+
151+
return (
152+
<button
153+
onClick={addMarker}
154+
style={{
155+
padding: '6px 12px',
156+
borderRadius: 6,
157+
border: '1px solid #ccc',
158+
background: 'white',
159+
cursor: 'pointer',
160+
fontSize: 14,
161+
pointerEvents: 'all',
162+
}}
163+
>
164+
+ Add marker
165+
</button>
166+
)
167+
}
168+
165169
// [6]
166170
export default function CustomRecordsExample() {
167171
const store = useMemo(
@@ -178,6 +182,7 @@ export default function CustomRecordsExample() {
178182
store={store}
179183
components={{
180184
InFrontOfTheCanvas: MarkerOverlay,
185+
TopPanel: AddMarkerButton,
181186
}}
182187
/>
183188
</div>

apps/examples/src/examples/data/assets/export-canvas-settings/ExportCanvasImageSettingsExample.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ function ExportCanvasButton() {
2525
})
2626

2727
// [2]
28-
const [box, setBox] = useState({ x: 0, y: 0, w: 0, h: 0 })
28+
const [box, setBox] = useState(() => {
29+
const v = editor.getViewportPageBounds()
30+
return { x: Math.round(v.x), y: Math.round(v.y), w: Math.round(v.w), h: Math.round(v.h) }
31+
})
2932

3033
return (
3134
<div
@@ -108,10 +111,9 @@ function ExportCanvasButton() {
108111
const { blob } = await editor.toImage([...shapeIds], {
109112
format: 'png',
110113
...opts,
111-
// If we have numbers for all of the box values, we can use them as bounds
112-
bounds: Object.values(box).every((b) => !Number.isNaN(b))
113-
? new Box(box.x, box.y, box.w, box.h)
114-
: undefined,
114+
// Use the box as bounds when it has a positive width and height;
115+
// otherwise let the export auto-fit all shapes.
116+
bounds: box.w > 0 && box.h > 0 ? new Box(box.x, box.y, box.w, box.h) : undefined,
115117
})
116118

117119
const link = document.createElement('a')

0 commit comments

Comments
 (0)