@canva/design 2.6.2-beta.1 → 2.7.0
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/{beta.d.ts → index.d.ts} +1467 -97
- package/lib/cjs/sdk/design/{beta.js → index.js} +2 -21
- package/lib/cjs/sdk/design/public.js +4 -0
- package/lib/esm/sdk/design/{beta.js → index.js} +1 -6
- package/lib/esm/sdk/design/public.js +1 -0
- package/package.json +11 -11
- package/lib/cjs/sdk/design/test/beta.js +0 -18
- package/lib/esm/sdk/design/test/beta.js +0 -1
- /package/test/{beta.d.ts → index.d.ts} +0 -0
package/{beta.d.ts → index.d.ts}
RENAMED
|
@@ -2,12 +2,131 @@
|
|
|
2
2
|
* Adds an audio track to the user's design.
|
|
3
3
|
* @public
|
|
4
4
|
* @param audioTrack - The audio track to add to the user's design.
|
|
5
|
+
*
|
|
6
|
+
* @example Add audio track to design
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { addAudioTrack } from "@canva/design";
|
|
9
|
+
* import type { AudioTrack } from "@canva/design";
|
|
10
|
+
* import type { AudioRef } from "@canva/asset";
|
|
11
|
+
*
|
|
12
|
+
* const exampleAudioRef = "YOUR_AUDIO_REF" as AudioRef;
|
|
13
|
+
*
|
|
14
|
+
* const audioTrack: AudioTrack = {
|
|
15
|
+
* ref: exampleAudioRef
|
|
16
|
+
* };
|
|
17
|
+
*
|
|
18
|
+
* await addAudioTrack(audioTrack);
|
|
19
|
+
* ```
|
|
5
20
|
*/
|
|
6
21
|
export declare const addAudioTrack: (audioTrack: AudioTrack) => Promise<void>;
|
|
7
22
|
|
|
8
23
|
/**
|
|
9
24
|
* @public
|
|
10
25
|
* Add element to responsive documents, which slot things into a text stream
|
|
26
|
+
*
|
|
27
|
+
* @example Insert an image at cursor position
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { addElementAtCursor } from "@canva/design";
|
|
30
|
+
* import type { ImageElement } from "@canva/design";
|
|
31
|
+
* import type { ImageRef } from "@canva/asset";
|
|
32
|
+
*
|
|
33
|
+
* const exampleImageRef = "YOUR_IMAGE_REF" as ImageRef;
|
|
34
|
+
*
|
|
35
|
+
* const imageElement: ImageElement = {
|
|
36
|
+
* type: 'image',
|
|
37
|
+
* ref: exampleImageRef,
|
|
38
|
+
* altText: { text: 'Product image', decorative: false }
|
|
39
|
+
* };
|
|
40
|
+
*
|
|
41
|
+
* await addElementAtCursor(imageElement);
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @example Insert a video at cursor position
|
|
45
|
+
* ```typescript
|
|
46
|
+
* import { addElementAtCursor } from "@canva/design";
|
|
47
|
+
* import type { VideoElement } from "@canva/design";
|
|
48
|
+
* import type { VideoRef } from "@canva/asset";
|
|
49
|
+
*
|
|
50
|
+
* const exampleVideoRef = "YOUR_VIDEO_REF" as VideoRef;
|
|
51
|
+
*
|
|
52
|
+
* const videoElement: VideoElement = {
|
|
53
|
+
* type: 'video',
|
|
54
|
+
* ref: exampleVideoRef,
|
|
55
|
+
* altText: { text: 'Product demo', decorative: false }
|
|
56
|
+
* };
|
|
57
|
+
*
|
|
58
|
+
* await addElementAtCursor(videoElement);
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @example Insert embedded content at cursor position
|
|
62
|
+
* ```typescript
|
|
63
|
+
* import { addElementAtCursor } from "@canva/design";
|
|
64
|
+
* import type { EmbedElement } from "@canva/design";
|
|
65
|
+
*
|
|
66
|
+
* const embedElement: EmbedElement = {
|
|
67
|
+
* type: 'embed',
|
|
68
|
+
* url: 'https://www.youtube.com/watch?v=...'
|
|
69
|
+
* };
|
|
70
|
+
*
|
|
71
|
+
* await addElementAtCursor(embedElement);
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @example Insert text at cursor position
|
|
75
|
+
* ```typescript
|
|
76
|
+
* import { addElementAtCursor } from "@canva/design";
|
|
77
|
+
* import type { TextElement } from "@canva/design";
|
|
78
|
+
*
|
|
79
|
+
* const textElement: TextElement = {
|
|
80
|
+
* type: 'text',
|
|
81
|
+
* children: ['Hello World'],
|
|
82
|
+
* fontSize: 24,
|
|
83
|
+
* color: '#000000'
|
|
84
|
+
* };
|
|
85
|
+
*
|
|
86
|
+
* await addElementAtCursor(textElement);
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @example Insert formatted text at cursor position
|
|
90
|
+
* ```typescript
|
|
91
|
+
* import { addElementAtCursor, createRichtextRange } from "@canva/design";
|
|
92
|
+
* import type { RichtextElement } from "@canva/design";
|
|
93
|
+
*
|
|
94
|
+
* const range = createRichtextRange();
|
|
95
|
+
* range.appendText('Rich Text Content', { color: '#000000' });
|
|
96
|
+
*
|
|
97
|
+
* const richtextElement: RichtextElement = {
|
|
98
|
+
* type: 'richtext',
|
|
99
|
+
* range
|
|
100
|
+
* };
|
|
101
|
+
*
|
|
102
|
+
* await addElementAtCursor(richtextElement);
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* @example Insert a table at cursor position
|
|
106
|
+
* ```typescript
|
|
107
|
+
* import { addElementAtCursor } from "@canva/design";
|
|
108
|
+
* import type { TableElement } from "@canva/design";
|
|
109
|
+
*
|
|
110
|
+
* const tableElement: TableElement = {
|
|
111
|
+
* type: 'table',
|
|
112
|
+
* rows: [
|
|
113
|
+
* {
|
|
114
|
+
* cells: [
|
|
115
|
+
* { type: 'string', value: 'Header 1' },
|
|
116
|
+
* { type: 'string', value: 'Header 2' }
|
|
117
|
+
* ]
|
|
118
|
+
* },
|
|
119
|
+
* {
|
|
120
|
+
* cells: [
|
|
121
|
+
* { type: 'string', value: 'Data 1' },
|
|
122
|
+
* { type: 'string', value: 'Data 2' }
|
|
123
|
+
* ]
|
|
124
|
+
* }
|
|
125
|
+
* ]
|
|
126
|
+
* };
|
|
127
|
+
*
|
|
128
|
+
* await addElementAtCursor(tableElement);
|
|
129
|
+
* ```
|
|
11
130
|
*/
|
|
12
131
|
export declare const addElementAtCursor: (
|
|
13
132
|
element: ElementAtCursor,
|
|
@@ -16,6 +135,84 @@ export declare const addElementAtCursor: (
|
|
|
16
135
|
/**
|
|
17
136
|
* @public
|
|
18
137
|
* Add element to fixed designs, which use a coordinate-based positioning system.
|
|
138
|
+
*
|
|
139
|
+
* @example Insert an image at specific coordinates
|
|
140
|
+
* ```typescript
|
|
141
|
+
* import { addElementAtPoint } from "@canva/design";
|
|
142
|
+
* import type { ImageElementAtPoint } from "@canva/design";
|
|
143
|
+
* import type { ImageRef } from "@canva/asset";
|
|
144
|
+
*
|
|
145
|
+
* const exampleImageRef = "YOUR_IMAGE_REF" as ImageRef;
|
|
146
|
+
*
|
|
147
|
+
* const imageElement: ImageElementAtPoint = {
|
|
148
|
+
* type: 'image',
|
|
149
|
+
* ref: exampleImageRef,
|
|
150
|
+
* altText: { text: 'Product image', decorative: false },
|
|
151
|
+
* top: 100,
|
|
152
|
+
* left: 100,
|
|
153
|
+
* width: 300,
|
|
154
|
+
* height: 200
|
|
155
|
+
* };
|
|
156
|
+
*
|
|
157
|
+
* await addElementAtPoint(imageElement);
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* @example Insert a video at specific coordinates
|
|
161
|
+
* ```typescript
|
|
162
|
+
* import { addElementAtPoint } from "@canva/design";
|
|
163
|
+
* import type { VideoElementAtPoint } from "@canva/design";
|
|
164
|
+
* import type { VideoRef } from "@canva/asset";
|
|
165
|
+
*
|
|
166
|
+
* const exampleVideoRef = "YOUR_VIDEO_REF" as VideoRef;
|
|
167
|
+
*
|
|
168
|
+
* const videoElement: VideoElementAtPoint = {
|
|
169
|
+
* type: 'video',
|
|
170
|
+
* ref: exampleVideoRef,
|
|
171
|
+
* altText: { text: 'Product demo', decorative: false },
|
|
172
|
+
* top: 100,
|
|
173
|
+
* left: 100,
|
|
174
|
+
* width: 400,
|
|
175
|
+
* height: 300
|
|
176
|
+
* };
|
|
177
|
+
*
|
|
178
|
+
* await addElementAtPoint(videoElement);
|
|
179
|
+
* ```
|
|
180
|
+
*
|
|
181
|
+
* @example Insert embedded content at specific coordinates
|
|
182
|
+
* ```typescript
|
|
183
|
+
* import { addElementAtPoint } from "@canva/design";
|
|
184
|
+
* import type { EmbedElementAtPoint } from "@canva/design";
|
|
185
|
+
*
|
|
186
|
+
* const embedElement: EmbedElementAtPoint = {
|
|
187
|
+
* type: 'embed',
|
|
188
|
+
* url: 'https://www.youtube.com/watch?v=...',
|
|
189
|
+
* top: 100,
|
|
190
|
+
* left: 100,
|
|
191
|
+
* width: 560,
|
|
192
|
+
* height: 315
|
|
193
|
+
* };
|
|
194
|
+
*
|
|
195
|
+
* await addElementAtPoint(embedElement);
|
|
196
|
+
* ```
|
|
197
|
+
*
|
|
198
|
+
* @example Insert text at specific coordinates
|
|
199
|
+
* ```typescript
|
|
200
|
+
* import { addElementAtPoint } from "@canva/design";
|
|
201
|
+
* import type { TextElementAtPoint } from "@canva/design";
|
|
202
|
+
*
|
|
203
|
+
* const textElement: TextElementAtPoint = {
|
|
204
|
+
* type: 'text',
|
|
205
|
+
* children: ['Hello World'],
|
|
206
|
+
* top: 100,
|
|
207
|
+
* left: 100,
|
|
208
|
+
* width: 200,
|
|
209
|
+
* fontSize: 24,
|
|
210
|
+
* color: '#000000',
|
|
211
|
+
* textAlign: 'justify'
|
|
212
|
+
* };
|
|
213
|
+
*
|
|
214
|
+
* await addElementAtPoint(textElement);
|
|
215
|
+
* ```
|
|
19
216
|
*/
|
|
20
217
|
export declare const addElementAtPoint: (
|
|
21
218
|
element: DesignElement | ElementAtPoint,
|
|
@@ -26,6 +223,22 @@ export declare const addElementAtPoint: (
|
|
|
26
223
|
* @public
|
|
27
224
|
* Adds a native element to the user's design.
|
|
28
225
|
* @param element - The element to add to the user's design.
|
|
226
|
+
*
|
|
227
|
+
* @example Basic usage
|
|
228
|
+
* ```typescript
|
|
229
|
+
* import { addNativeElement } from "@canva/design";
|
|
230
|
+
* import type { NativeElementWithBox } from "@canva/design";
|
|
231
|
+
*
|
|
232
|
+
* const element: NativeElementWithBox = {
|
|
233
|
+
* type: 'text',
|
|
234
|
+
* children: ['Legacy element'],
|
|
235
|
+
* top: 100,
|
|
236
|
+
* left: 100,
|
|
237
|
+
* width: 200
|
|
238
|
+
* };
|
|
239
|
+
*
|
|
240
|
+
* await addNativeElement(element);
|
|
241
|
+
* ```
|
|
29
242
|
*/
|
|
30
243
|
export declare const addNativeElement: (
|
|
31
244
|
element: NativeElement | NativeElementWithBox,
|
|
@@ -35,6 +248,94 @@ export declare const addNativeElement: (
|
|
|
35
248
|
* @public
|
|
36
249
|
* Adds a new page immediately after the currently selected page.
|
|
37
250
|
* @param opts - Configuration for the new page to be added.
|
|
251
|
+
*
|
|
252
|
+
* @example Create empty page
|
|
253
|
+
* ```typescript
|
|
254
|
+
* import { addPage } from "@canva/design";
|
|
255
|
+
*
|
|
256
|
+
* await addPage();
|
|
257
|
+
* ```
|
|
258
|
+
*
|
|
259
|
+
* @example Create page with title
|
|
260
|
+
* ```typescript
|
|
261
|
+
* import { addPage } from "@canva/design";
|
|
262
|
+
*
|
|
263
|
+
* await addPage({
|
|
264
|
+
* title: 'My New Page'
|
|
265
|
+
* });
|
|
266
|
+
* ```
|
|
267
|
+
*
|
|
268
|
+
* @example Create page with background color
|
|
269
|
+
* ```typescript
|
|
270
|
+
* import { addPage } from "@canva/design";
|
|
271
|
+
* import type { PageBackgroundFill } from "@canva/design";
|
|
272
|
+
*
|
|
273
|
+
* const background: PageBackgroundFill = {
|
|
274
|
+
* color: '#F5F5F5',
|
|
275
|
+
* };
|
|
276
|
+
*
|
|
277
|
+
* await addPage({ background });
|
|
278
|
+
* ```
|
|
279
|
+
*
|
|
280
|
+
* @example Create page with background image
|
|
281
|
+
* ```typescript
|
|
282
|
+
* import { addPage } from "@canva/design";
|
|
283
|
+
* import type { PageBackgroundFill } from "@canva/design";
|
|
284
|
+
* import type { ImageRef } from "@canva/asset";
|
|
285
|
+
*
|
|
286
|
+
* const exampleImageRef = "YOUR_IMAGE_REF" as ImageRef;
|
|
287
|
+
*
|
|
288
|
+
* const background: PageBackgroundFill = {
|
|
289
|
+
* asset: {
|
|
290
|
+
* type: 'image',
|
|
291
|
+
* ref: exampleImageRef,
|
|
292
|
+
* altText: { text: 'Background image', decorative: true }
|
|
293
|
+
* },
|
|
294
|
+
* };
|
|
295
|
+
*
|
|
296
|
+
* await addPage({ background });
|
|
297
|
+
* ```
|
|
298
|
+
*
|
|
299
|
+
* @example Create page with multiple elements
|
|
300
|
+
* ```typescript
|
|
301
|
+
* import { addPage } from "@canva/design";
|
|
302
|
+
* import type { TextElementAtPoint, ImageElementAtPoint } from "@canva/design";
|
|
303
|
+
* import type { ImageRef } from "@canva/asset";
|
|
304
|
+
*
|
|
305
|
+
* const exampleImageRef = "YOUR_IMAGE_REF" as ImageRef;
|
|
306
|
+
*
|
|
307
|
+
* await addPage({
|
|
308
|
+
* elements: [
|
|
309
|
+
* {
|
|
310
|
+
* type: 'text',
|
|
311
|
+
* children: ['Page Title'],
|
|
312
|
+
* top: 50,
|
|
313
|
+
* left: 100,
|
|
314
|
+
* width: 400,
|
|
315
|
+
* fontSize: 32,
|
|
316
|
+
* textAlign: 'center'
|
|
317
|
+
* } as TextElementAtPoint,
|
|
318
|
+
* {
|
|
319
|
+
* type: 'text',
|
|
320
|
+
* children: ['Subtitle text'],
|
|
321
|
+
* top: 100,
|
|
322
|
+
* left: 100,
|
|
323
|
+
* width: 400,
|
|
324
|
+
* fontSize: 18,
|
|
325
|
+
* textAlign: 'center'
|
|
326
|
+
* } as TextElementAtPoint,
|
|
327
|
+
* {
|
|
328
|
+
* type: 'image',
|
|
329
|
+
* ref: exampleImageRef,
|
|
330
|
+
* altText: { text: 'Featured image', decorative: false },
|
|
331
|
+
* top: 150,
|
|
332
|
+
* left: 200,
|
|
333
|
+
* width: 200,
|
|
334
|
+
* height: 200
|
|
335
|
+
* } as ImageElementAtPoint,
|
|
336
|
+
* ]
|
|
337
|
+
* });
|
|
338
|
+
* ```
|
|
38
339
|
*/
|
|
39
340
|
export declare const addPage: (opts?: {
|
|
40
341
|
/** Elements to be added to the page */
|
|
@@ -75,6 +376,67 @@ export declare type AltText = {
|
|
|
75
376
|
* - the user selects an existing app element
|
|
76
377
|
*
|
|
77
378
|
* @param appElement - Information about the app element that was changed.
|
|
379
|
+
*
|
|
380
|
+
* @example Handle element selection
|
|
381
|
+
* ```typescript
|
|
382
|
+
* import { initAppElement } from "@canva/design";
|
|
383
|
+
*
|
|
384
|
+
* const appElement = initAppElement<{ content: string }>({
|
|
385
|
+
* render: (data) => {
|
|
386
|
+
* // Render based on data
|
|
387
|
+
* return [{
|
|
388
|
+
* type: 'text',
|
|
389
|
+
* children: [data.content || 'Default text'],
|
|
390
|
+
* top: 0,
|
|
391
|
+
* left: 0,
|
|
392
|
+
* width: 200
|
|
393
|
+
* }];
|
|
394
|
+
* }
|
|
395
|
+
* });
|
|
396
|
+
*
|
|
397
|
+
* appElement.registerOnElementChange((element) => {
|
|
398
|
+
* if (element) {
|
|
399
|
+
* // Element selected or created, do something with the `element.data`
|
|
400
|
+
* } else {
|
|
401
|
+
* // No element selected
|
|
402
|
+
* }
|
|
403
|
+
* });
|
|
404
|
+
* ```
|
|
405
|
+
*
|
|
406
|
+
* @example Update element data when selected
|
|
407
|
+
* ```typescript
|
|
408
|
+
* import { initAppElement } from "@canva/design";
|
|
409
|
+
*
|
|
410
|
+
* const appElement = initAppElement<{
|
|
411
|
+
* content: string;
|
|
412
|
+
* lastSelected: number;
|
|
413
|
+
* lastUpdated: number;
|
|
414
|
+
* metadata: { lastEdited: number; editCount: number };
|
|
415
|
+
* }>({
|
|
416
|
+
* render: (data) => {
|
|
417
|
+
* // Render based on data
|
|
418
|
+
* return [{
|
|
419
|
+
* type: 'text',
|
|
420
|
+
* children: [data.content || 'Default text'],
|
|
421
|
+
* top: 0,
|
|
422
|
+
* left: 0,
|
|
423
|
+
* width: 200
|
|
424
|
+
* }];
|
|
425
|
+
* }
|
|
426
|
+
* });
|
|
427
|
+
*
|
|
428
|
+
* appElement.registerOnElementChange(async (element) => {
|
|
429
|
+
* if (element) {
|
|
430
|
+
* // Use the update method to modify the element's data
|
|
431
|
+
* await element.update({
|
|
432
|
+
* data: {
|
|
433
|
+
* ...element.data,
|
|
434
|
+
* lastSelected: Date.now()
|
|
435
|
+
* }
|
|
436
|
+
* });
|
|
437
|
+
* }
|
|
438
|
+
* });
|
|
439
|
+
* ```
|
|
78
440
|
*/
|
|
79
441
|
export declare type AppElementChangeHandler<A extends AppElementData> = (
|
|
80
442
|
appElement:
|
|
@@ -89,6 +451,55 @@ export declare type AppElementChangeHandler<A extends AppElementData> = (
|
|
|
89
451
|
version: number;
|
|
90
452
|
/**
|
|
91
453
|
* Function to update the app element data.
|
|
454
|
+
*
|
|
455
|
+
* @param opts - The data and placement to update the app element with.
|
|
456
|
+
*
|
|
457
|
+
* @example Update element data only
|
|
458
|
+
* ```typescript
|
|
459
|
+
* if (element) {
|
|
460
|
+
* await element.update({
|
|
461
|
+
* data: {
|
|
462
|
+
* ...element.data,
|
|
463
|
+
* content: 'Updated content',
|
|
464
|
+
* lastUpdated: Date.now()
|
|
465
|
+
* }
|
|
466
|
+
* });
|
|
467
|
+
* }
|
|
468
|
+
* ```
|
|
469
|
+
*
|
|
470
|
+
* @example Update data and placement
|
|
471
|
+
* ```typescript
|
|
472
|
+
* if (element) {
|
|
473
|
+
* await element.update({
|
|
474
|
+
* data: {
|
|
475
|
+
* ...element.data,
|
|
476
|
+
* content: 'Positioned element'
|
|
477
|
+
* },
|
|
478
|
+
* placement: {
|
|
479
|
+
* top: 200,
|
|
480
|
+
* left: 200,
|
|
481
|
+
* width: 300,
|
|
482
|
+
* height: 100
|
|
483
|
+
* }
|
|
484
|
+
* });
|
|
485
|
+
* }
|
|
486
|
+
* ```
|
|
487
|
+
*
|
|
488
|
+
* @example Add metadata to element
|
|
489
|
+
* ```typescript
|
|
490
|
+
* if (element) {
|
|
491
|
+
* await element.update({
|
|
492
|
+
* data: {
|
|
493
|
+
* ...element.data,
|
|
494
|
+
* metadata: {
|
|
495
|
+
* ...(element.data.metadata || {}),
|
|
496
|
+
* lastEdited: Date.now(),
|
|
497
|
+
* editCount: (element.data.metadata?.editCount || 0) + 1,
|
|
498
|
+
* },
|
|
499
|
+
* },
|
|
500
|
+
* });
|
|
501
|
+
* }
|
|
502
|
+
* ```
|
|
92
503
|
*/
|
|
93
504
|
update: (opts: AppElementOptions<A>) => Promise<void>;
|
|
94
505
|
}
|
|
@@ -106,11 +517,135 @@ export declare interface AppElementClient<A extends AppElementData> {
|
|
|
106
517
|
* Otherwise, the provided data is used to create a new app element.
|
|
107
518
|
* @param appElementData - The data to attach to the app element. Existing data will be overwritten.
|
|
108
519
|
* @param placement - The position, dimensions, and rotation of the app element.
|
|
520
|
+
*
|
|
521
|
+
* @example Create or update an element (deprecated)
|
|
522
|
+
* ```typescript
|
|
523
|
+
* import { initAppElement } from "@canva/design";
|
|
524
|
+
*
|
|
525
|
+
* // Initialize the app element client
|
|
526
|
+
* const appElement = initAppElement<{ content: string; timestamp: number }>({
|
|
527
|
+
* render: (data) => {
|
|
528
|
+
* return [{
|
|
529
|
+
* type: 'text',
|
|
530
|
+
* children: [data.content || 'Default text'],
|
|
531
|
+
* top: 100,
|
|
532
|
+
* left: 100,
|
|
533
|
+
* width: 200
|
|
534
|
+
* }];
|
|
535
|
+
* }
|
|
536
|
+
* });
|
|
537
|
+
*
|
|
538
|
+
* // Create a new element or update selected element
|
|
539
|
+
* await appElement.addOrUpdateElement({
|
|
540
|
+
* content: 'Hello from the app',
|
|
541
|
+
* timestamp: Date.now()
|
|
542
|
+
* });
|
|
543
|
+
* ```
|
|
544
|
+
*
|
|
545
|
+
* @example Update with specific placement (deprecated)
|
|
546
|
+
* ```typescript
|
|
547
|
+
* import { initAppElement } from "@canva/design";
|
|
548
|
+
*
|
|
549
|
+
* const appElement = initAppElement<{ content: string }>({
|
|
550
|
+
* render: (data) => {
|
|
551
|
+
* return [{
|
|
552
|
+
* type: 'text',
|
|
553
|
+
* children: [data.content || 'Default text'],
|
|
554
|
+
* top: 0,
|
|
555
|
+
* left: 0,
|
|
556
|
+
* width: 200
|
|
557
|
+
* }];
|
|
558
|
+
* }
|
|
559
|
+
* });
|
|
560
|
+
*
|
|
561
|
+
* // Create or update with specific placement
|
|
562
|
+
* await appElement.addOrUpdateElement(
|
|
563
|
+
* {
|
|
564
|
+
* content: 'Positioned content'
|
|
565
|
+
* },
|
|
566
|
+
* {
|
|
567
|
+
* top: 200,
|
|
568
|
+
* left: 200,
|
|
569
|
+
* width: 300,
|
|
570
|
+
* height: 100
|
|
571
|
+
* }
|
|
572
|
+
* );
|
|
573
|
+
* ```
|
|
109
574
|
*/
|
|
110
575
|
addOrUpdateElement(appElementData: A, placement?: Placement): Promise<void>;
|
|
111
576
|
/**
|
|
112
577
|
* Adds a new app element to the design.
|
|
113
578
|
* @param opts - The data and placement of the app element.
|
|
579
|
+
*
|
|
580
|
+
* @example Add new element with data
|
|
581
|
+
* ```typescript
|
|
582
|
+
* import { initAppElement } from "@canva/design";
|
|
583
|
+
*
|
|
584
|
+
* const appElement = initAppElement<{ content: string; id: string }>({
|
|
585
|
+
* render: (data) => {
|
|
586
|
+
* return [{
|
|
587
|
+
* type: 'text',
|
|
588
|
+
* children: [data.content || 'Default text'],
|
|
589
|
+
* top: 0,
|
|
590
|
+
* left: 0,
|
|
591
|
+
* width: 200
|
|
592
|
+
* }];
|
|
593
|
+
* }
|
|
594
|
+
* });
|
|
595
|
+
*
|
|
596
|
+
* // Add a new element
|
|
597
|
+
* await appElement.addElement({
|
|
598
|
+
* data: {
|
|
599
|
+
* content: 'New element content',
|
|
600
|
+
* id: 'element-' + Date.now()
|
|
601
|
+
* }
|
|
602
|
+
* });
|
|
603
|
+
* ```
|
|
604
|
+
*
|
|
605
|
+
* @example Add element with specific placement
|
|
606
|
+
* ```typescript
|
|
607
|
+
* import { initAppElement } from "@canva/design";
|
|
608
|
+
*
|
|
609
|
+
* const appElement = initAppElement<{
|
|
610
|
+
* title: string;
|
|
611
|
+
* description: string;
|
|
612
|
+
* createdAt: number;
|
|
613
|
+
* }>({
|
|
614
|
+
* render: (data) => {
|
|
615
|
+
* return [{
|
|
616
|
+
* type: 'text',
|
|
617
|
+
* children: [data.title || 'Default title'],
|
|
618
|
+
* top: 0,
|
|
619
|
+
* left: 0,
|
|
620
|
+
* width: 300,
|
|
621
|
+
* fontWeight: 'bold',
|
|
622
|
+
* fontSize: 24
|
|
623
|
+
* }, {
|
|
624
|
+
* type: 'text',
|
|
625
|
+
* children: [data.description || 'Default description'],
|
|
626
|
+
* top: 50,
|
|
627
|
+
* left: 0,
|
|
628
|
+
* width: 300
|
|
629
|
+
* }];
|
|
630
|
+
* }
|
|
631
|
+
* });
|
|
632
|
+
*
|
|
633
|
+
* // Add element with specific placement
|
|
634
|
+
* await appElement.addElement({
|
|
635
|
+
* data: {
|
|
636
|
+
* title: 'Element Title',
|
|
637
|
+
* description: 'This is a description of the element',
|
|
638
|
+
* createdAt: Date.now()
|
|
639
|
+
* },
|
|
640
|
+
* placement: {
|
|
641
|
+
* top: 100,
|
|
642
|
+
* left: 100,
|
|
643
|
+
* width: 400,
|
|
644
|
+
* height: 150,
|
|
645
|
+
* rotation: 0
|
|
646
|
+
* }
|
|
647
|
+
* });
|
|
648
|
+
* ```
|
|
114
649
|
*/
|
|
115
650
|
addElement(opts: AppElementOptions<A>): Promise<void>;
|
|
116
651
|
/**
|
|
@@ -121,6 +656,89 @@ export declare interface AppElementClient<A extends AppElementData> {
|
|
|
121
656
|
* - the user selects an existing app element
|
|
122
657
|
*
|
|
123
658
|
* @param handler - The callback to run when the app element changes.
|
|
659
|
+
*
|
|
660
|
+
* @example Handle element selection and update
|
|
661
|
+
* ```typescript
|
|
662
|
+
* import { initAppElement } from "@canva/design";
|
|
663
|
+
*
|
|
664
|
+
* const appElement = initAppElement<{ content: string }>({
|
|
665
|
+
* render: (data) => {
|
|
666
|
+
* return [{
|
|
667
|
+
* type: 'text',
|
|
668
|
+
* children: [data.content || 'Default text'],
|
|
669
|
+
* top: 0,
|
|
670
|
+
* left: 0,
|
|
671
|
+
* width: 200
|
|
672
|
+
* }];
|
|
673
|
+
* }
|
|
674
|
+
* });
|
|
675
|
+
*
|
|
676
|
+
* // Register a handler for element changes
|
|
677
|
+
* appElement.registerOnElementChange((element) => {
|
|
678
|
+
* if (element) {
|
|
679
|
+
* // Element is created or selected
|
|
680
|
+
* // Optionally update the element
|
|
681
|
+
* // element.update({
|
|
682
|
+
* // data: { ...element.data, lastSelected: Date.now() }
|
|
683
|
+
* // });
|
|
684
|
+
* } else {
|
|
685
|
+
* // No element is selected
|
|
686
|
+
* }
|
|
687
|
+
* });
|
|
688
|
+
* ```
|
|
689
|
+
*
|
|
690
|
+
* @example Update element when selected
|
|
691
|
+
* ```typescript
|
|
692
|
+
* import { initAppElement } from "@canva/design";
|
|
693
|
+
*
|
|
694
|
+
* const appElement = initAppElement<{
|
|
695
|
+
* content: string;
|
|
696
|
+
* metadata: { created: number; lastSelected: number };
|
|
697
|
+
* }>({
|
|
698
|
+
* render: (data) => {
|
|
699
|
+
* // Render based on data
|
|
700
|
+
* return [{
|
|
701
|
+
* type: 'text',
|
|
702
|
+
* children: [data.content || ''],
|
|
703
|
+
* top: 0,
|
|
704
|
+
* left: 0,
|
|
705
|
+
* width: 200
|
|
706
|
+
* }];
|
|
707
|
+
* }
|
|
708
|
+
* });
|
|
709
|
+
*
|
|
710
|
+
* // Update element when selected
|
|
711
|
+
* appElement.registerOnElementChange(async (element) => {
|
|
712
|
+
* if (element) {
|
|
713
|
+
* // Check if this is a newly created or a selected element
|
|
714
|
+
* const isNewElement = !element.data.metadata?.created;
|
|
715
|
+
*
|
|
716
|
+
* if (isNewElement) {
|
|
717
|
+
* // Update a new element with initial metadata
|
|
718
|
+
* await element.update({
|
|
719
|
+
* data: {
|
|
720
|
+
* ...element.data,
|
|
721
|
+
* metadata: {
|
|
722
|
+
* created: Date.now(),
|
|
723
|
+
* lastSelected: Date.now()
|
|
724
|
+
* }
|
|
725
|
+
* }
|
|
726
|
+
* });
|
|
727
|
+
* } else {
|
|
728
|
+
* // Update existing element's last selected time
|
|
729
|
+
* await element.update({
|
|
730
|
+
* data: {
|
|
731
|
+
* ...element.data,
|
|
732
|
+
* metadata: {
|
|
733
|
+
* ...element.data.metadata,
|
|
734
|
+
* lastSelected: Date.now()
|
|
735
|
+
* }
|
|
736
|
+
* }
|
|
737
|
+
* });
|
|
738
|
+
* }
|
|
739
|
+
* }
|
|
740
|
+
* });
|
|
741
|
+
* ```
|
|
124
742
|
*/
|
|
125
743
|
registerOnElementChange(handler: AppElementChangeHandler<A>): void;
|
|
126
744
|
}
|
|
@@ -344,20 +962,64 @@ export declare interface ContentDraft<T> {
|
|
|
344
962
|
* - Any changes the app has made to to the content will be reflected in the user's design.
|
|
345
963
|
* - Any changes the user has made to the content since the snapshot was created may be overwritten.
|
|
346
964
|
* - Only properties that are different from the original state will be written to the design.
|
|
965
|
+
*
|
|
966
|
+
* @example Save changes to selected text
|
|
967
|
+
* ```typescript
|
|
968
|
+
* import { selection } from "@canva/design";
|
|
969
|
+
*
|
|
970
|
+
* selection.registerOnChange({
|
|
971
|
+
* scope: 'plaintext',
|
|
972
|
+
* onChange: async (event) => {
|
|
973
|
+
* if (event.count > 0) {
|
|
974
|
+
* const draft = await event.read();
|
|
975
|
+
*
|
|
976
|
+
* // Make changes to the content
|
|
977
|
+
* for (const content of draft.contents) {
|
|
978
|
+
* content.text = content.text.toUpperCase();
|
|
979
|
+
* }
|
|
980
|
+
*
|
|
981
|
+
* // Save the changes to the design
|
|
982
|
+
* await draft.save();
|
|
983
|
+
* }
|
|
984
|
+
* }
|
|
985
|
+
* });
|
|
986
|
+
* ```
|
|
987
|
+
*
|
|
988
|
+
* @example Modify then save rich text content
|
|
989
|
+
* ```typescript
|
|
990
|
+
* import { selection } from "@canva/design";
|
|
991
|
+
*
|
|
992
|
+
* selection.registerOnChange({
|
|
993
|
+
* scope: 'richtext',
|
|
994
|
+
* onChange: async (event) => {
|
|
995
|
+
* if (event.count > 0) {
|
|
996
|
+
* const draft = await event.read();
|
|
997
|
+
* const range = draft.contents[0];
|
|
998
|
+
*
|
|
999
|
+
* // Get the plain text
|
|
1000
|
+
* const text = range.readPlaintext();
|
|
1001
|
+
*
|
|
1002
|
+
* // Apply formatting to the entire text
|
|
1003
|
+
* range.formatText(
|
|
1004
|
+
* { index: 0, length: text.length },
|
|
1005
|
+
* { fontWeight: 'bold', color: '#0066CC' }
|
|
1006
|
+
* );
|
|
1007
|
+
*
|
|
1008
|
+
* // Save the formatted text back to the design
|
|
1009
|
+
* await draft.save();
|
|
1010
|
+
* }
|
|
1011
|
+
* }
|
|
1012
|
+
* });
|
|
1013
|
+
* ```
|
|
347
1014
|
*/
|
|
348
1015
|
save(): Promise<void>;
|
|
349
1016
|
}
|
|
350
1017
|
|
|
351
|
-
/**
|
|
352
|
-
* @beta
|
|
353
|
-
*/
|
|
354
|
-
export declare type ContentType = "richtext" | "fill";
|
|
355
|
-
|
|
356
1018
|
/**
|
|
357
1019
|
* @public
|
|
358
1020
|
* A type of content that can be read from a user's design.
|
|
359
1021
|
*/
|
|
360
|
-
declare type
|
|
1022
|
+
export declare type ContentType = "richtext";
|
|
361
1023
|
|
|
362
1024
|
/**
|
|
363
1025
|
* @public
|
|
@@ -385,6 +1047,76 @@ export declare type Coordinates = {
|
|
|
385
1047
|
/**
|
|
386
1048
|
* @public
|
|
387
1049
|
* Creates a new RichtextRange object, which contains methods to manipulate text.
|
|
1050
|
+
*
|
|
1051
|
+
* @example Create formatted text range
|
|
1052
|
+
* ```typescript
|
|
1053
|
+
* import { createRichtextRange } from "@canva/design";
|
|
1054
|
+
* import type { InlineFormatting } from "@canva/design";
|
|
1055
|
+
*
|
|
1056
|
+
* const range = createRichtextRange();
|
|
1057
|
+
*
|
|
1058
|
+
* range.appendText('Hello World', {
|
|
1059
|
+
* color: '#000000',
|
|
1060
|
+
* fontWeight: 'bold'
|
|
1061
|
+
* } as InlineFormatting);
|
|
1062
|
+
* ```
|
|
1063
|
+
*
|
|
1064
|
+
* @example Format paragraph styles
|
|
1065
|
+
* ```typescript
|
|
1066
|
+
* import { createRichtextRange } from "@canva/design";
|
|
1067
|
+
* import type { RichtextFormatting } from "@canva/design";
|
|
1068
|
+
*
|
|
1069
|
+
* const range = createRichtextRange();
|
|
1070
|
+
*
|
|
1071
|
+
* const bounds = range.appendText('Centered Title\n');
|
|
1072
|
+
* range.formatParagraph(bounds.bounds, {
|
|
1073
|
+
* fontSize: 32,
|
|
1074
|
+
* textAlign: 'center'
|
|
1075
|
+
* } as RichtextFormatting);
|
|
1076
|
+
* ```
|
|
1077
|
+
*
|
|
1078
|
+
* @example Create bulleted list
|
|
1079
|
+
* ```typescript
|
|
1080
|
+
* import { createRichtextRange } from "@canva/design";
|
|
1081
|
+
*
|
|
1082
|
+
* const range = createRichtextRange();
|
|
1083
|
+
*
|
|
1084
|
+
* const item = range.appendText('List item\n');
|
|
1085
|
+
* range.formatParagraph(item.bounds, {
|
|
1086
|
+
* fontSize: 16,
|
|
1087
|
+
* listLevel: 1,
|
|
1088
|
+
* listMarker: 'disc'
|
|
1089
|
+
* });
|
|
1090
|
+
* ```
|
|
1091
|
+
*
|
|
1092
|
+
* @example Extract text content
|
|
1093
|
+
* ```typescript
|
|
1094
|
+
* import { createRichtextRange } from "@canva/design";
|
|
1095
|
+
*
|
|
1096
|
+
* const range = createRichtextRange();
|
|
1097
|
+
* range.appendText('Sample text');
|
|
1098
|
+
*
|
|
1099
|
+
* // Get plain text content
|
|
1100
|
+
* const text = range.readPlaintext();
|
|
1101
|
+
*
|
|
1102
|
+
* // Get formatted regions
|
|
1103
|
+
* const regions = range.readTextRegions();
|
|
1104
|
+
* ```
|
|
1105
|
+
*
|
|
1106
|
+
* @example Replace text with formatting
|
|
1107
|
+
* ```typescript
|
|
1108
|
+
* import { createRichtextRange } from "@canva/design";
|
|
1109
|
+
*
|
|
1110
|
+
* const range = createRichtextRange();
|
|
1111
|
+
* range.appendText('Original text');
|
|
1112
|
+
*
|
|
1113
|
+
* // Replace text while adding formatting
|
|
1114
|
+
* range.replaceText(
|
|
1115
|
+
* { index: 0, length: range.readPlaintext().length },
|
|
1116
|
+
* 'Modified',
|
|
1117
|
+
* { color: '#0066CC', decoration: 'underline' }
|
|
1118
|
+
* );
|
|
1119
|
+
* ```
|
|
388
1120
|
*/
|
|
389
1121
|
export declare const createRichtextRange: () => RichtextRange;
|
|
390
1122
|
|
|
@@ -1917,7 +2649,7 @@ export declare type DesignElement =
|
|
|
1917
2649
|
| TableElement;
|
|
1918
2650
|
|
|
1919
2651
|
/**
|
|
1920
|
-
* @
|
|
2652
|
+
* @public
|
|
1921
2653
|
* Information about the design.
|
|
1922
2654
|
*/
|
|
1923
2655
|
export declare type DesignMetadata = {
|
|
@@ -1941,7 +2673,7 @@ export declare type DesignMetadata = {
|
|
|
1941
2673
|
* @remarks
|
|
1942
2674
|
* The order of pages is not guaranteed.
|
|
1943
2675
|
*/
|
|
1944
|
-
pageMetadata: Iterable<
|
|
2676
|
+
pageMetadata: Iterable<PageMetadata>;
|
|
1945
2677
|
/**
|
|
1946
2678
|
* The duration of the whole design in seconds.
|
|
1947
2679
|
* @remarks
|
|
@@ -1973,6 +2705,80 @@ export declare type DesignOverlay = {
|
|
|
1973
2705
|
/**
|
|
1974
2706
|
* Registers a callback that runs when the `canOpen` state of an overlay target changes.
|
|
1975
2707
|
* @param opts - Options for configuring the callback.
|
|
2708
|
+
*
|
|
2709
|
+
* @example Register overlay handler
|
|
2710
|
+
* ```typescript
|
|
2711
|
+
* import { overlay } from "@canva/design";
|
|
2712
|
+
*
|
|
2713
|
+
* overlay.registerOnCanOpen({
|
|
2714
|
+
* target: 'image_selection',
|
|
2715
|
+
* onCanOpen: async (event) => {
|
|
2716
|
+
* if (event.canOpen) {
|
|
2717
|
+
* // Can open overlay for selected image
|
|
2718
|
+
* }
|
|
2719
|
+
* }
|
|
2720
|
+
* });
|
|
2721
|
+
* ```
|
|
2722
|
+
*
|
|
2723
|
+
* @example Open image editing overlay
|
|
2724
|
+
* ```typescript
|
|
2725
|
+
* import { overlay } from "@canva/design";
|
|
2726
|
+
*
|
|
2727
|
+
* overlay.registerOnCanOpen({
|
|
2728
|
+
* target: 'image_selection',
|
|
2729
|
+
* onCanOpen: async (event) => {
|
|
2730
|
+
* if (event.canOpen) {
|
|
2731
|
+
* const processId = await event.open({
|
|
2732
|
+
* launchParameters: {
|
|
2733
|
+
* mode: 'edit'
|
|
2734
|
+
* }
|
|
2735
|
+
* });
|
|
2736
|
+
* // Overlay process started, with `processId`
|
|
2737
|
+
* }
|
|
2738
|
+
* }
|
|
2739
|
+
* });
|
|
2740
|
+
* ```
|
|
2741
|
+
*
|
|
2742
|
+
* @example Open overlay with filters
|
|
2743
|
+
* ```typescript
|
|
2744
|
+
* import { overlay } from "@canva/design";
|
|
2745
|
+
*
|
|
2746
|
+
* overlay.registerOnCanOpen({
|
|
2747
|
+
* target: 'image_selection',
|
|
2748
|
+
* onCanOpen: async (event) => {
|
|
2749
|
+
* if (event.canOpen) {
|
|
2750
|
+
* await event.open({
|
|
2751
|
+
* launchParameters: {
|
|
2752
|
+
* mode: 'edit',
|
|
2753
|
+
* filters: ['brightness', 'contrast', 'saturation']
|
|
2754
|
+
* }
|
|
2755
|
+
* });
|
|
2756
|
+
* }
|
|
2757
|
+
* }
|
|
2758
|
+
* });
|
|
2759
|
+
* ```
|
|
2760
|
+
*
|
|
2761
|
+
* @example Handle overlay unavailability
|
|
2762
|
+
* ```typescript
|
|
2763
|
+
* import { overlay } from "@canva/design";
|
|
2764
|
+
*
|
|
2765
|
+
* overlay.registerOnCanOpen({
|
|
2766
|
+
* target: 'image_selection',
|
|
2767
|
+
* onCanOpen: async (event) => {
|
|
2768
|
+
* if (!event.canOpen) {
|
|
2769
|
+
* // Cannot open overlay, handle specific reasons
|
|
2770
|
+
* switch (event.reason) {
|
|
2771
|
+
* case 'no_selection':
|
|
2772
|
+
* // No image is selected
|
|
2773
|
+
* break;
|
|
2774
|
+
* case 'invalid_selection':
|
|
2775
|
+
* // Selected content cannot be edited
|
|
2776
|
+
* break;
|
|
2777
|
+
* }
|
|
2778
|
+
* }
|
|
2779
|
+
* }
|
|
2780
|
+
* });
|
|
2781
|
+
* ```
|
|
1976
2782
|
*/
|
|
1977
2783
|
registerOnCanOpen<Target extends OverlayTarget>(opts: {
|
|
1978
2784
|
/**
|
|
@@ -2003,6 +2809,67 @@ export declare type DesignSelection = {
|
|
|
2003
2809
|
*
|
|
2004
2810
|
* @remarks
|
|
2005
2811
|
* This callback fires immediately if content is already selected when the callback is registered.
|
|
2812
|
+
*
|
|
2813
|
+
* @example Handling plaintext selection
|
|
2814
|
+
* ```typescript
|
|
2815
|
+
* import { selection } from "@canva/design";
|
|
2816
|
+
*
|
|
2817
|
+
* selection.registerOnChange({
|
|
2818
|
+
* scope: 'plaintext',
|
|
2819
|
+
* onChange: async (event) => {
|
|
2820
|
+
* if (event.count > 0) {
|
|
2821
|
+
* const draft = await event.read();
|
|
2822
|
+
* // Do something with the selected text, e.g. `draft.contents[0].text`
|
|
2823
|
+
* }
|
|
2824
|
+
* }
|
|
2825
|
+
* });
|
|
2826
|
+
* ```
|
|
2827
|
+
*
|
|
2828
|
+
* @example Handling image selection
|
|
2829
|
+
* ```typescript
|
|
2830
|
+
* import { selection } from "@canva/design";
|
|
2831
|
+
*
|
|
2832
|
+
* selection.registerOnChange({
|
|
2833
|
+
* scope: 'image',
|
|
2834
|
+
* onChange: async (event) => {
|
|
2835
|
+
* if (event.count > 0) {
|
|
2836
|
+
* const draft = await event.read();
|
|
2837
|
+
* // Do something with the selected image ref, e.g. `draft.contents[0].ref`
|
|
2838
|
+
* }
|
|
2839
|
+
* }
|
|
2840
|
+
* });
|
|
2841
|
+
* ```
|
|
2842
|
+
*
|
|
2843
|
+
* @example Handling video selection
|
|
2844
|
+
* ```typescript
|
|
2845
|
+
* import { selection } from "@canva/design";
|
|
2846
|
+
*
|
|
2847
|
+
* selection.registerOnChange({
|
|
2848
|
+
* scope: 'video',
|
|
2849
|
+
* onChange: async (event) => {
|
|
2850
|
+
* if (event.count > 0) {
|
|
2851
|
+
* const draft = await event.read();
|
|
2852
|
+
* // Do something with the selected video ref, e.g. `draft.contents[0].ref`
|
|
2853
|
+
* }
|
|
2854
|
+
* }
|
|
2855
|
+
* });
|
|
2856
|
+
* ```
|
|
2857
|
+
*
|
|
2858
|
+
* @example Handling richtext selection
|
|
2859
|
+
* ```typescript
|
|
2860
|
+
* import { selection } from "@canva/design";
|
|
2861
|
+
*
|
|
2862
|
+
* selection.registerOnChange({
|
|
2863
|
+
* scope: 'richtext',
|
|
2864
|
+
* onChange: async (event) => {
|
|
2865
|
+
* if (event.count > 0) {
|
|
2866
|
+
* const draft = await event.read();
|
|
2867
|
+
* const range = draft.contents[0];
|
|
2868
|
+
* // Do something with the selected richtext, e.g. `range.readPlaintext()`
|
|
2869
|
+
* }
|
|
2870
|
+
* }
|
|
2871
|
+
* });
|
|
2872
|
+
* ```
|
|
2006
2873
|
*/
|
|
2007
2874
|
registerOnChange<Scope extends SelectionScope>(opts: {
|
|
2008
2875
|
/**
|
|
@@ -2052,53 +2919,39 @@ export declare type DragStartEvent<E extends Element> = Pick<
|
|
|
2052
2919
|
};
|
|
2053
2920
|
|
|
2054
2921
|
/**
|
|
2055
|
-
* @
|
|
2056
|
-
* Reads and edits
|
|
2057
|
-
* @param options - Options for configuring how a design is read.
|
|
2058
|
-
* @param callback - A callback
|
|
2059
|
-
*
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
)
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
*
|
|
2070
|
-
*
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
options: EditContentOptions & {
|
|
2077
|
-
contentType: "fill";
|
|
2078
|
-
},
|
|
2079
|
-
callback: (session: FillContentSession) => Promise<void> | void,
|
|
2080
|
-
): Promise<void>;
|
|
2081
|
-
|
|
2082
|
-
/**
|
|
2083
|
-
* @beta
|
|
2084
|
-
* A callback for reading and updating the requested design content.
|
|
2085
|
-
* @param session - The result of reading the content in the design.
|
|
2086
|
-
*/
|
|
2087
|
-
export declare type EditContentCallback = (
|
|
2088
|
-
session: RichtextContentSession | FillContentSession,
|
|
2089
|
-
) => Promise<void> | void;
|
|
2922
|
+
* @public
|
|
2923
|
+
* Reads and edits content of the specified type from the user's design.
|
|
2924
|
+
* @param options - Options for configuring how a design is read.
|
|
2925
|
+
* @param callback - A callback for operating on the read content.
|
|
2926
|
+
*
|
|
2927
|
+
* @example Read richtext content
|
|
2928
|
+
* ```typescript
|
|
2929
|
+
* import { editContent } from "@canva/design";
|
|
2930
|
+
*
|
|
2931
|
+
* await editContent(
|
|
2932
|
+
* { contentType: 'richtext', target: 'current_page' },
|
|
2933
|
+
* async (session) => {
|
|
2934
|
+
* // Do something with the richtext content, e.g. `session.contents`
|
|
2935
|
+
* }
|
|
2936
|
+
* );
|
|
2937
|
+
* ```
|
|
2938
|
+
*/
|
|
2939
|
+
export declare const editContent: (
|
|
2940
|
+
options: EditContentOptions,
|
|
2941
|
+
callback: EditContentCallback,
|
|
2942
|
+
) => Promise<void>;
|
|
2090
2943
|
|
|
2091
2944
|
/**
|
|
2092
2945
|
* @public
|
|
2093
2946
|
* A callback for reading and updating the requested design content.
|
|
2094
2947
|
* @param session - The result of reading the content in the design.
|
|
2095
2948
|
*/
|
|
2096
|
-
declare type
|
|
2949
|
+
export declare type EditContentCallback = (
|
|
2097
2950
|
session: RichtextContentSession,
|
|
2098
2951
|
) => Promise<void> | void;
|
|
2099
2952
|
|
|
2100
2953
|
/**
|
|
2101
|
-
* @
|
|
2954
|
+
* @public
|
|
2102
2955
|
* Options for configuring how the design content is read.
|
|
2103
2956
|
*/
|
|
2104
2957
|
export declare type EditContentOptions = {
|
|
@@ -2108,17 +2961,6 @@ export declare type EditContentOptions = {
|
|
|
2108
2961
|
contentType: ContentType;
|
|
2109
2962
|
} & ContextOptions;
|
|
2110
2963
|
|
|
2111
|
-
/**
|
|
2112
|
-
* @public
|
|
2113
|
-
* Options for configuring how the design content is read.
|
|
2114
|
-
*/
|
|
2115
|
-
declare type EditContentOptions_2 = {
|
|
2116
|
-
/**
|
|
2117
|
-
* The type of content to edit from the user's design
|
|
2118
|
-
*/
|
|
2119
|
-
contentType: ContentType_2;
|
|
2120
|
-
} & ContextOptions;
|
|
2121
|
-
|
|
2122
2964
|
/**
|
|
2123
2965
|
* @public
|
|
2124
2966
|
* Elements targeting a cursor are a subset of the base Element
|
|
@@ -2349,31 +3191,6 @@ export declare type Fill = {
|
|
|
2349
3191
|
asset?: ImageFill | VideoFill;
|
|
2350
3192
|
};
|
|
2351
3193
|
|
|
2352
|
-
/**
|
|
2353
|
-
* @beta
|
|
2354
|
-
* Object for interacting with fill content (images/videos).
|
|
2355
|
-
*/
|
|
2356
|
-
export declare type FillContent =
|
|
2357
|
-
| {
|
|
2358
|
-
type: "image";
|
|
2359
|
-
ref: ImageRef;
|
|
2360
|
-
deleted: boolean;
|
|
2361
|
-
}
|
|
2362
|
-
| {
|
|
2363
|
-
type: "video";
|
|
2364
|
-
ref: VideoRef;
|
|
2365
|
-
deleted: boolean;
|
|
2366
|
-
};
|
|
2367
|
-
|
|
2368
|
-
/**
|
|
2369
|
-
* @beta
|
|
2370
|
-
* Session for reading and updating fill content in a user's design.
|
|
2371
|
-
*/
|
|
2372
|
-
export declare interface FillContentSession {
|
|
2373
|
-
readonly contents: readonly FillContent[];
|
|
2374
|
-
sync(): Promise<void>;
|
|
2375
|
-
}
|
|
2376
|
-
|
|
2377
3194
|
/**
|
|
2378
3195
|
* @public
|
|
2379
3196
|
* A reference to a font that can be used in other parts of the SDK.
|
|
@@ -2401,6 +3218,18 @@ export declare type FontWeight =
|
|
|
2401
3218
|
* Allows to get the context of currently selected page.
|
|
2402
3219
|
* @public
|
|
2403
3220
|
* @returns Page context of currently selected page
|
|
3221
|
+
*
|
|
3222
|
+
* @example Get current page information
|
|
3223
|
+
* ```typescript
|
|
3224
|
+
* import { getCurrentPageContext } from "@canva/design";
|
|
3225
|
+
*
|
|
3226
|
+
* const pageContext = await getCurrentPageContext();
|
|
3227
|
+
* if (pageContext.dimensions) {
|
|
3228
|
+
* // Do something with the page dimensions, e.g. `pageContext.dimensions.width` and `pageContext.dimensions.height`
|
|
3229
|
+
* } else {
|
|
3230
|
+
* // This page type does not have fixed dimensions, e.g. Whiteboard or Doc
|
|
3231
|
+
* }
|
|
3232
|
+
* ```
|
|
2404
3233
|
*/
|
|
2405
3234
|
export declare const getCurrentPageContext: () => Promise<PageContext>;
|
|
2406
3235
|
|
|
@@ -2412,20 +3241,92 @@ export declare const getCurrentPageContext: () => Promise<PageContext>;
|
|
|
2412
3241
|
* design that is applied whenever a new page is created.
|
|
2413
3242
|
*
|
|
2414
3243
|
* Returns `undefined` if the design is unbounded (e.g. Whiteboard or Doc).
|
|
3244
|
+
*
|
|
3245
|
+
* @example Get default page dimensions
|
|
3246
|
+
* ```typescript
|
|
3247
|
+
* import { getDefaultPageDimensions } from "@canva/design";
|
|
3248
|
+
*
|
|
3249
|
+
* const dimensions = await getDefaultPageDimensions();
|
|
3250
|
+
*
|
|
3251
|
+
* if (dimensions) {
|
|
3252
|
+
* // Do something with the dimensions, e.g. `dimensions.width` and `dimensions.height`
|
|
3253
|
+
* } else {
|
|
3254
|
+
* // This design type does not have fixed dimensions, e.g. Whiteboard or Doc
|
|
3255
|
+
* }
|
|
3256
|
+
* ```
|
|
3257
|
+
*
|
|
3258
|
+
* @example Center element using page dimensions
|
|
3259
|
+
* ```typescript
|
|
3260
|
+
* import { getDefaultPageDimensions, addElementAtPoint } from "@canva/design";
|
|
3261
|
+
* import type { ImageElementAtPoint } from "@canva/design";
|
|
3262
|
+
*
|
|
3263
|
+
* const dimensions = await getDefaultPageDimensions();
|
|
3264
|
+
*
|
|
3265
|
+
* if (dimensions) {
|
|
3266
|
+
* const elementWidth = 300;
|
|
3267
|
+
* const elementHeight = 200;
|
|
3268
|
+
*
|
|
3269
|
+
* const element: ImageElementAtPoint = {
|
|
3270
|
+
* type: 'image',
|
|
3271
|
+
* dataUrl: 'data:image/png;base64,...',
|
|
3272
|
+
* altText: { text: 'Centered image', decorative: false },
|
|
3273
|
+
* top: (dimensions.height - elementHeight) / 2,
|
|
3274
|
+
* left: (dimensions.width - elementWidth) / 2,
|
|
3275
|
+
* width: elementWidth,
|
|
3276
|
+
* height: elementHeight
|
|
3277
|
+
* };
|
|
3278
|
+
*
|
|
3279
|
+
* await addElementAtPoint(element);
|
|
3280
|
+
* }
|
|
3281
|
+
* ```
|
|
2415
3282
|
*/
|
|
2416
3283
|
export declare const getDefaultPageDimensions: () => Promise<
|
|
2417
3284
|
Dimensions | undefined
|
|
2418
3285
|
>;
|
|
2419
3286
|
|
|
2420
3287
|
/**
|
|
2421
|
-
* @
|
|
3288
|
+
* @public
|
|
2422
3289
|
* Retrieves information about the design.
|
|
3290
|
+
*
|
|
3291
|
+
* @example Get design metadata
|
|
3292
|
+
* ```typescript
|
|
3293
|
+
* import { getDesignMetadata } from "@canva/design";
|
|
3294
|
+
*
|
|
3295
|
+
* const metadata = await getDesignMetadata();
|
|
3296
|
+
*
|
|
3297
|
+
* const { title, defaultPageDimensions, pageMetadata, durationInSeconds } = metadata;
|
|
3298
|
+
* ```
|
|
2423
3299
|
*/
|
|
2424
3300
|
export declare const getDesignMetadata: () => Promise<DesignMetadata>;
|
|
2425
3301
|
|
|
2426
3302
|
/**
|
|
2427
3303
|
* @public
|
|
2428
3304
|
* Retrieves a signed JWT that contains the Design ID, App ID and User ID.
|
|
3305
|
+
*
|
|
3306
|
+
* @example Get design token
|
|
3307
|
+
* ```typescript
|
|
3308
|
+
* import { getDesignToken } from "@canva/design";
|
|
3309
|
+
*
|
|
3310
|
+
* const { token } = await getDesignToken();
|
|
3311
|
+
* ```
|
|
3312
|
+
*
|
|
3313
|
+
* @example Verify token with backend service
|
|
3314
|
+
* ```typescript
|
|
3315
|
+
* import { getDesignToken } from "@canva/design";
|
|
3316
|
+
*
|
|
3317
|
+
* const { token } = await getDesignToken();
|
|
3318
|
+
*
|
|
3319
|
+
* const verifyResponse = await fetch('https://your-backend.com/verify', {
|
|
3320
|
+
* method: 'POST',
|
|
3321
|
+
* headers: {
|
|
3322
|
+
* 'Content-Type': 'application/json'
|
|
3323
|
+
* },
|
|
3324
|
+
* body: JSON.stringify({ token })
|
|
3325
|
+
* });
|
|
3326
|
+
*
|
|
3327
|
+
* const json = await verifyResponse.json();
|
|
3328
|
+
* const { designId, appId, userId } = json;
|
|
3329
|
+
* ```
|
|
2429
3330
|
*/
|
|
2430
3331
|
export declare const getDesignToken: () => Promise<DesignToken>;
|
|
2431
3332
|
|
|
@@ -2579,6 +3480,40 @@ export declare type ImageRef = string & {
|
|
|
2579
3480
|
/**
|
|
2580
3481
|
* @public
|
|
2581
3482
|
* @param appElementConfig - Configuration for an AppElementClient
|
|
3483
|
+
*
|
|
3484
|
+
* @example Initialize app element client
|
|
3485
|
+
* ```typescript
|
|
3486
|
+
* import { initAppElement } from "@canva/design";
|
|
3487
|
+
*
|
|
3488
|
+
* const appElement = initAppElement<{ content: string }>({
|
|
3489
|
+
* render: (data) => {
|
|
3490
|
+
* return [{
|
|
3491
|
+
* type: 'text',
|
|
3492
|
+
* children: [data.content],
|
|
3493
|
+
* top: 100,
|
|
3494
|
+
* left: 100,
|
|
3495
|
+
* width: 200
|
|
3496
|
+
* }];
|
|
3497
|
+
* }
|
|
3498
|
+
* });
|
|
3499
|
+
* ```
|
|
3500
|
+
*
|
|
3501
|
+
* @example Initialize V2 app element client
|
|
3502
|
+
* ```typescript
|
|
3503
|
+
* import { initAppElement } from "@canva/design";
|
|
3504
|
+
*
|
|
3505
|
+
* const appElement = initAppElement<{ content: string }>({
|
|
3506
|
+
* render: (data) => {
|
|
3507
|
+
* return [{
|
|
3508
|
+
* type: 'text',
|
|
3509
|
+
* children: [data.content],
|
|
3510
|
+
* top: 100,
|
|
3511
|
+
* left: 100,
|
|
3512
|
+
* width: 200
|
|
3513
|
+
* }];
|
|
3514
|
+
* }
|
|
3515
|
+
* });
|
|
3516
|
+
* ```
|
|
2582
3517
|
*/
|
|
2583
3518
|
export declare const initAppElement: <A extends AppElementData>(
|
|
2584
3519
|
appElementConfig: AppElementClientConfiguration<A>,
|
|
@@ -2869,6 +3804,20 @@ export declare type PageDimensions = {
|
|
|
2869
3804
|
height: number;
|
|
2870
3805
|
};
|
|
2871
3806
|
|
|
3807
|
+
/**
|
|
3808
|
+
* @public
|
|
3809
|
+
* Information about a page.
|
|
3810
|
+
*/
|
|
3811
|
+
export declare type PageMetadata = {
|
|
3812
|
+
/**
|
|
3813
|
+
* The dimensions of the page, in pixels.
|
|
3814
|
+
*
|
|
3815
|
+
* @remarks
|
|
3816
|
+
* This may be `undefined` because some types of pages don't have dimensions, such as whiteboards.
|
|
3817
|
+
*/
|
|
3818
|
+
dimensions?: PageDimensions;
|
|
3819
|
+
};
|
|
3820
|
+
|
|
2872
3821
|
/**
|
|
2873
3822
|
* @public
|
|
2874
3823
|
* The outline of a path.
|
|
@@ -2966,10 +3915,121 @@ export declare interface RichtextContentRange extends RichtextRange {
|
|
|
2966
3915
|
|
|
2967
3916
|
/**
|
|
2968
3917
|
* @public
|
|
2969
|
-
*
|
|
3918
|
+
* A callback for reading and updating the requested design content.
|
|
3919
|
+
* @param session - The result of reading the content in the design.
|
|
3920
|
+
*
|
|
3921
|
+
* @example Read and update richtext content
|
|
3922
|
+
* ```typescript
|
|
3923
|
+
* import { editContent } from "@canva/design";
|
|
3924
|
+
*
|
|
3925
|
+
* await editContent(
|
|
3926
|
+
* { contentType: 'richtext', target: 'current_page' },
|
|
3927
|
+
* async (session) => {
|
|
3928
|
+
* // Read the content
|
|
3929
|
+
* const contents = session.contents;
|
|
3930
|
+
*
|
|
3931
|
+
* if (contents.length > 0) {
|
|
3932
|
+
* const range = contents[0];
|
|
3933
|
+
*
|
|
3934
|
+
* // Modify the content (e.g., adding text)
|
|
3935
|
+
* range.appendText("\nAppended text from app");
|
|
3936
|
+
*
|
|
3937
|
+
* // Sync changes back to the design
|
|
3938
|
+
* await session.sync();
|
|
3939
|
+
* }
|
|
3940
|
+
* }
|
|
3941
|
+
* );
|
|
3942
|
+
* ```
|
|
3943
|
+
*
|
|
3944
|
+
* @example Format all richtext content in the design
|
|
3945
|
+
* ```typescript
|
|
3946
|
+
* import { editContent } from "@canva/design";
|
|
3947
|
+
*
|
|
3948
|
+
* await editContent(
|
|
3949
|
+
* { contentType: 'richtext', target: 'current_page' },
|
|
3950
|
+
* async (session) => {
|
|
3951
|
+
* // Process each richtext range in the content
|
|
3952
|
+
* for (const range of session.contents) {
|
|
3953
|
+
* // Skip if the content has been deleted
|
|
3954
|
+
* if (range.deleted) continue;
|
|
3955
|
+
*
|
|
3956
|
+
* // Get the text content
|
|
3957
|
+
* const text = range.readPlaintext();
|
|
3958
|
+
* if (text.length === 0) continue;
|
|
3959
|
+
*
|
|
3960
|
+
* // Apply consistent formatting
|
|
3961
|
+
* range.formatParagraph(
|
|
3962
|
+
* { index: 0, length: text.length },
|
|
3963
|
+
* {
|
|
3964
|
+
* fontRef: 'YOUR_FONT_REF',
|
|
3965
|
+
* fontSize: 16,
|
|
3966
|
+
* textAlign: 'start'
|
|
3967
|
+
* }
|
|
3968
|
+
* );
|
|
3969
|
+
* }
|
|
3970
|
+
*
|
|
3971
|
+
* // Sync all changes back to the design
|
|
3972
|
+
* await session.sync();
|
|
3973
|
+
* }
|
|
3974
|
+
* );
|
|
3975
|
+
* ```
|
|
3976
|
+
*
|
|
3977
|
+
* @example Modify content without saving changes
|
|
3978
|
+
* ```typescript
|
|
3979
|
+
* import { editContent } from "@canva/design";
|
|
3980
|
+
*
|
|
3981
|
+
* await editContent(
|
|
3982
|
+
* { contentType: 'richtext', target: 'current_page' },
|
|
3983
|
+
* async (session) => {
|
|
3984
|
+
* // Read and analyze the content without making changes
|
|
3985
|
+
* for (const range of session.contents) {
|
|
3986
|
+
* const text = range.readPlaintext();
|
|
3987
|
+
* // Do something with the content preview, e.g. `text.substring(0, 50)`
|
|
3988
|
+
*
|
|
3989
|
+
* // No call to session.sync() means no changes are saved
|
|
3990
|
+
* }
|
|
3991
|
+
*
|
|
3992
|
+
* // Since we're not calling sync(), no changes will be made to the design
|
|
3993
|
+
* }
|
|
3994
|
+
* );
|
|
3995
|
+
* ```
|
|
2970
3996
|
*/
|
|
2971
3997
|
export declare interface RichtextContentSession {
|
|
3998
|
+
/**
|
|
3999
|
+
* Richtext content in the design.
|
|
4000
|
+
*/
|
|
2972
4001
|
readonly contents: readonly RichtextContentRange[];
|
|
4002
|
+
/**
|
|
4003
|
+
* Saves any changes made during the session while keeping the transaction open.
|
|
4004
|
+
*
|
|
4005
|
+
* @remarks
|
|
4006
|
+
* - Any changes in the session are only reflected in the design after this method is called.
|
|
4007
|
+
* - Once this method is called, further changes in the session can still be made.
|
|
4008
|
+
*
|
|
4009
|
+
* @example Sync changes after modifying content
|
|
4010
|
+
* ```typescript
|
|
4011
|
+
* import { editContent } from "@canva/design";
|
|
4012
|
+
*
|
|
4013
|
+
* await editContent(
|
|
4014
|
+
* { contentType: 'richtext', target: 'current_page' },
|
|
4015
|
+
* async (session) => {
|
|
4016
|
+
* if (session.contents.length > 0) {
|
|
4017
|
+
* const range = session.contents[0];
|
|
4018
|
+
*
|
|
4019
|
+
* // Make modifications to the content
|
|
4020
|
+
* range.appendText(" - Modified by app");
|
|
4021
|
+
*
|
|
4022
|
+
* try {
|
|
4023
|
+
* // Save changes back to the design
|
|
4024
|
+
* await session.sync();
|
|
4025
|
+
* } catch (error) {
|
|
4026
|
+
* console.error('Failed to sync changes:', error);
|
|
4027
|
+
* }
|
|
4028
|
+
* }
|
|
4029
|
+
* }
|
|
4030
|
+
* );
|
|
4031
|
+
* ```
|
|
4032
|
+
*/
|
|
2973
4033
|
sync(): Promise<void>;
|
|
2974
4034
|
}
|
|
2975
4035
|
|
|
@@ -3063,6 +4123,42 @@ export declare type RichtextRange = {
|
|
|
3063
4123
|
* - The `\n` character indicates the end of a paragraph.
|
|
3064
4124
|
* - All paragraphs that overlap the provided bounds will be formatted in their entirety.
|
|
3065
4125
|
*
|
|
4126
|
+
* @example Format paragraph as a heading
|
|
4127
|
+
* ```typescript
|
|
4128
|
+
* import { createRichtextRange } from "@canva/design";
|
|
4129
|
+
*
|
|
4130
|
+
* const range = createRichtextRange();
|
|
4131
|
+
*
|
|
4132
|
+
* range.appendText("Heading Text\nRegular paragraph text.");
|
|
4133
|
+
*
|
|
4134
|
+
* // Format just the first paragraph as a heading
|
|
4135
|
+
* range.formatParagraph(
|
|
4136
|
+
* { index: 0, length: 12 }, // Only need to include part of the paragraph
|
|
4137
|
+
* {
|
|
4138
|
+
* fontSize: 24,
|
|
4139
|
+
* fontWeight: 'bold',
|
|
4140
|
+
* textAlign: 'center'
|
|
4141
|
+
* }
|
|
4142
|
+
* );
|
|
4143
|
+
* ```
|
|
4144
|
+
*
|
|
4145
|
+
* @example Create a bulleted list
|
|
4146
|
+
* ```typescript
|
|
4147
|
+
* import { createRichtextRange } from "@canva/design";
|
|
4148
|
+
*
|
|
4149
|
+
* const range = createRichtextRange();
|
|
4150
|
+
* const text = "Item 1\nItem 2\nItem 3";
|
|
4151
|
+
* range.appendText(text);
|
|
4152
|
+
*
|
|
4153
|
+
* // Format all paragraphs as a bulleted list
|
|
4154
|
+
* range.formatParagraph(
|
|
4155
|
+
* { index: 0, length: text.length },
|
|
4156
|
+
* {
|
|
4157
|
+
* listLevel: 1,
|
|
4158
|
+
* listMarker: 'disc'
|
|
4159
|
+
* }
|
|
4160
|
+
* );
|
|
4161
|
+
* ```
|
|
3066
4162
|
*/
|
|
3067
4163
|
formatParagraph(bounds: Bounds, formatting: RichtextFormatting): void;
|
|
3068
4164
|
/**
|
|
@@ -3070,13 +4166,79 @@ export declare type RichtextRange = {
|
|
|
3070
4166
|
*
|
|
3071
4167
|
* @param bounds - The segment of the range on which to apply the formatting.
|
|
3072
4168
|
* @param formatting - The formatting to apply to the text.
|
|
4169
|
+
*
|
|
4170
|
+
* @example Format specific words in a paragraph
|
|
4171
|
+
* ```typescript
|
|
4172
|
+
* import { createRichtextRange } from "@canva/design";
|
|
4173
|
+
*
|
|
4174
|
+
* const range = createRichtextRange();
|
|
4175
|
+
* range.appendText("This text contains important information.");
|
|
4176
|
+
*
|
|
4177
|
+
* // Format just the word "important"
|
|
4178
|
+
* range.formatText(
|
|
4179
|
+
* { index: 16, length: 9 },
|
|
4180
|
+
* {
|
|
4181
|
+
* fontWeight: 'bold',
|
|
4182
|
+
* color: '#FF0000'
|
|
4183
|
+
* }
|
|
4184
|
+
* );
|
|
4185
|
+
* ```
|
|
4186
|
+
*
|
|
4187
|
+
* @example Add a link to text
|
|
4188
|
+
* ```typescript
|
|
4189
|
+
* import { createRichtextRange } from "@canva/design";
|
|
4190
|
+
*
|
|
4191
|
+
* const range = createRichtextRange();
|
|
4192
|
+
* range.appendText("Visit our website for more information.");
|
|
4193
|
+
*
|
|
4194
|
+
* // Add a link to "our website"
|
|
4195
|
+
* range.formatText(
|
|
4196
|
+
* { index: 6, length: 11 },
|
|
4197
|
+
* {
|
|
4198
|
+
* link: "https://www.example.com",
|
|
4199
|
+
* decoration: 'underline',
|
|
4200
|
+
* color: '#0066CC'
|
|
4201
|
+
* }
|
|
4202
|
+
* );
|
|
4203
|
+
* ```
|
|
3073
4204
|
*/
|
|
3074
4205
|
formatText(bounds: Bounds, formatting: InlineFormatting): void;
|
|
3075
4206
|
/**
|
|
3076
4207
|
* Appends the specified characters to the end of the range.
|
|
3077
4208
|
*
|
|
3078
4209
|
* @param characters - The characters to append to the richtext range.
|
|
3079
|
-
* @param
|
|
4210
|
+
* @param formatting - Optional formatting to apply to the appended text.
|
|
4211
|
+
*
|
|
4212
|
+
* @example Append plain text
|
|
4213
|
+
* ```typescript
|
|
4214
|
+
* import { createRichtextRange } from "@canva/design";
|
|
4215
|
+
*
|
|
4216
|
+
* const range = createRichtextRange();
|
|
4217
|
+
* range.appendText("First paragraph. ");
|
|
4218
|
+
*
|
|
4219
|
+
* // Append more text to the existing content
|
|
4220
|
+
* const result = range.appendText("This is additional text.");
|
|
4221
|
+
*
|
|
4222
|
+
* // The bounds of the newly added text are returned
|
|
4223
|
+
* // Do something with the bounds - result.bounds, e.g. { index: 17, length: 24 }
|
|
4224
|
+
* ```
|
|
4225
|
+
*
|
|
4226
|
+
* @example Append formatted text
|
|
4227
|
+
* ```typescript
|
|
4228
|
+
* import { createRichtextRange } from "@canva/design";
|
|
4229
|
+
*
|
|
4230
|
+
* const range = createRichtextRange();
|
|
4231
|
+
* range.appendText("Normal text followed by ");
|
|
4232
|
+
*
|
|
4233
|
+
* // Append formatted text
|
|
4234
|
+
* range.appendText("bold red text", {
|
|
4235
|
+
* fontWeight: 'bold',
|
|
4236
|
+
* color: '#FF0000'
|
|
4237
|
+
* });
|
|
4238
|
+
*
|
|
4239
|
+
* // Append a new paragraph
|
|
4240
|
+
* range.appendText("\nThis is a new paragraph.");
|
|
4241
|
+
* ```
|
|
3080
4242
|
*/
|
|
3081
4243
|
appendText(
|
|
3082
4244
|
characters: string,
|
|
@@ -3090,6 +4252,42 @@ export declare type RichtextRange = {
|
|
|
3090
4252
|
* @param bounds - The segment of the range to replace.
|
|
3091
4253
|
* @param characters - The replacement characters.
|
|
3092
4254
|
* @param formatting - The formatting to apply to the replaced text.
|
|
4255
|
+
*
|
|
4256
|
+
* @example Replace text while maintaining some formatting
|
|
4257
|
+
* ```typescript
|
|
4258
|
+
* import { createRichtextRange } from "@canva/design";
|
|
4259
|
+
*
|
|
4260
|
+
* const range = createRichtextRange();
|
|
4261
|
+
* range.appendText("This text needs correction.");
|
|
4262
|
+
*
|
|
4263
|
+
* // Replace "needs correction" with "is correct"
|
|
4264
|
+
* const result = range.replaceText(
|
|
4265
|
+
* { index: 10, length: 16 },
|
|
4266
|
+
* "is correct"
|
|
4267
|
+
* );
|
|
4268
|
+
*
|
|
4269
|
+
* // The bounds of the replaced text are returned
|
|
4270
|
+
* // Do something with the bounds - result.bounds, e.g. { index: 10, length: 10 }
|
|
4271
|
+
* ```
|
|
4272
|
+
*
|
|
4273
|
+
* @example Replace text with formatted text
|
|
4274
|
+
* ```typescript
|
|
4275
|
+
* import { createRichtextRange } from "@canva/design";
|
|
4276
|
+
*
|
|
4277
|
+
* const range = createRichtextRange();
|
|
4278
|
+
* range.appendText("Regular text that needs emphasis.");
|
|
4279
|
+
*
|
|
4280
|
+
* // Replace "needs emphasis" with formatted text
|
|
4281
|
+
* range.replaceText(
|
|
4282
|
+
* { index: 17, length: 15 },
|
|
4283
|
+
* "is important",
|
|
4284
|
+
* {
|
|
4285
|
+
* fontWeight: 'bold',
|
|
4286
|
+
* fontStyle: 'italic',
|
|
4287
|
+
* color: '#0066CC'
|
|
4288
|
+
* }
|
|
4289
|
+
* );
|
|
4290
|
+
* ```
|
|
3093
4291
|
*/
|
|
3094
4292
|
replaceText(
|
|
3095
4293
|
bounds: Bounds,
|
|
@@ -3103,11 +4301,87 @@ export declare type RichtextRange = {
|
|
|
3103
4301
|
};
|
|
3104
4302
|
/**
|
|
3105
4303
|
* Returns the current state of the richtext as plaintext.
|
|
4304
|
+
*
|
|
4305
|
+
* @example Extract plain text content
|
|
4306
|
+
* ```typescript
|
|
4307
|
+
* import { createRichtextRange } from "@canva/design";
|
|
4308
|
+
*
|
|
4309
|
+
* const range = createRichtextRange();
|
|
4310
|
+
* range.appendText("First paragraph.\n", { fontWeight: 'bold' });
|
|
4311
|
+
* range.appendText("Second paragraph with formatting.", { color: '#FF0000' });
|
|
4312
|
+
*
|
|
4313
|
+
* // Get plain text content without formatting
|
|
4314
|
+
* const plainText = range.readPlaintext();
|
|
4315
|
+
* // Do something with the plain text - plainText, e.g. "First paragraph.\nSecond paragraph with formatting."
|
|
4316
|
+
* ```
|
|
4317
|
+
*
|
|
4318
|
+
* @example Search within text content
|
|
4319
|
+
* ```typescript
|
|
4320
|
+
* import { createRichtextRange } from "@canva/design";
|
|
4321
|
+
*
|
|
4322
|
+
* const range = createRichtextRange();
|
|
4323
|
+
* range.appendText("This text contains a searchable term.");
|
|
4324
|
+
*
|
|
4325
|
+
* // Search for a specific word
|
|
4326
|
+
* const plainText = range.readPlaintext();
|
|
4327
|
+
* const searchTerm = "searchable";
|
|
4328
|
+
* const index = plainText.indexOf(searchTerm);
|
|
4329
|
+
*
|
|
4330
|
+
* if (index !== -1) {
|
|
4331
|
+
* // Format the found term
|
|
4332
|
+
* range.formatText(
|
|
4333
|
+
* { index, length: searchTerm.length },
|
|
4334
|
+
* { fontWeight: 'bold', decoration: 'underline' }
|
|
4335
|
+
* );
|
|
4336
|
+
* }
|
|
4337
|
+
* ```
|
|
3106
4338
|
*/
|
|
3107
4339
|
readPlaintext(): string;
|
|
3108
4340
|
/**
|
|
3109
4341
|
* Returns the current state of the richtext as one or more text regions.
|
|
3110
4342
|
* Each region is an object that contains the text content and its formatting.
|
|
4343
|
+
*
|
|
4344
|
+
* @example Get text with formatting information
|
|
4345
|
+
* ```typescript
|
|
4346
|
+
* import { createRichtextRange } from "@canva/design";
|
|
4347
|
+
*
|
|
4348
|
+
* const range = createRichtextRange();
|
|
4349
|
+
* range.appendText("Normal text ", {});
|
|
4350
|
+
* range.appendText("bold text", { fontWeight: 'bold' });
|
|
4351
|
+
* range.appendText(" and ", {});
|
|
4352
|
+
* range.appendText("red text", { color: '#FF0000' });
|
|
4353
|
+
*
|
|
4354
|
+
* // Get formatted regions
|
|
4355
|
+
* const regions = range.readTextRegions();
|
|
4356
|
+
* // Do something with the regions, e.g.
|
|
4357
|
+
* // [
|
|
4358
|
+
* // { text: "Normal text ", formatting: {} },
|
|
4359
|
+
* // { text: "bold text", formatting: { fontWeight: 'bold' } },
|
|
4360
|
+
* // { text: " and ", formatting: {} },
|
|
4361
|
+
* // { text: "red text", formatting: { color: '#FF0000' } }
|
|
4362
|
+
* // ]
|
|
4363
|
+
* ```
|
|
4364
|
+
*
|
|
4365
|
+
* @example Analyze formatting variations
|
|
4366
|
+
* ```typescript
|
|
4367
|
+
* import { createRichtextRange } from "@canva/design";
|
|
4368
|
+
*
|
|
4369
|
+
* const range = createRichtextRange();
|
|
4370
|
+
* range.appendText("Mixed ", {});
|
|
4371
|
+
* range.appendText("formatted ", { fontWeight: 'bold' });
|
|
4372
|
+
* range.appendText("text", { color: '#0066CC' });
|
|
4373
|
+
*
|
|
4374
|
+
* // Analyze formatting variations
|
|
4375
|
+
* const regions = range.readTextRegions();
|
|
4376
|
+
* const formattingTypes = regions.map(region => {
|
|
4377
|
+
* const formatting = region.formatting || {};
|
|
4378
|
+
* return {
|
|
4379
|
+
* text: region.text,
|
|
4380
|
+
* hasWeight: !!formatting.fontWeight,
|
|
4381
|
+
* hasColor: !!formatting.color
|
|
4382
|
+
* };
|
|
4383
|
+
* });
|
|
4384
|
+
* ```
|
|
3111
4385
|
*/
|
|
3112
4386
|
readTextRegions(): TextRegion[];
|
|
3113
4387
|
};
|
|
@@ -3137,17 +4411,68 @@ export declare interface SelectionEvent<Scope extends SelectionScope> {
|
|
|
3137
4411
|
* Any changes made to `contents` are not immediately persisted or reflected in the user's design.
|
|
3138
4412
|
* To persist the changes, call the `save` method that's available via the draft.
|
|
3139
4413
|
*
|
|
3140
|
-
* @example
|
|
4414
|
+
* @example Read and modify plaintext selection
|
|
4415
|
+
* ```typescript
|
|
4416
|
+
* import { selection } from "@canva/design";
|
|
4417
|
+
*
|
|
4418
|
+
* selection.registerOnChange({
|
|
4419
|
+
* scope: 'plaintext',
|
|
4420
|
+
* onChange: async (event) => {
|
|
4421
|
+
* if (event.count > 0) {
|
|
4422
|
+
* // Read the content
|
|
4423
|
+
* const draft = await event.read();
|
|
4424
|
+
*
|
|
4425
|
+
* // Handle selected text `draft.contents[0].text`
|
|
4426
|
+
*
|
|
4427
|
+
* // Modify the text if needed
|
|
4428
|
+
* // draft.contents[0].text = 'Modified text';
|
|
4429
|
+
* // await draft.save();
|
|
4430
|
+
* }
|
|
4431
|
+
* }
|
|
4432
|
+
* });
|
|
3141
4433
|
* ```
|
|
3142
|
-
* const draft = await selectionEvent.read();
|
|
3143
4434
|
*
|
|
3144
|
-
*
|
|
3145
|
-
*
|
|
3146
|
-
*
|
|
3147
|
-
*
|
|
4435
|
+
* @example Read and analyze image selection
|
|
4436
|
+
* ```typescript
|
|
4437
|
+
* import { selection } from "@canva/design";
|
|
4438
|
+
*
|
|
4439
|
+
* selection.registerOnChange({
|
|
4440
|
+
* scope: 'image',
|
|
4441
|
+
* onChange: async (event) => {
|
|
4442
|
+
* // Check if any images are selected
|
|
4443
|
+
* if (event.count === 0) {
|
|
4444
|
+
* // Handle no images selected case
|
|
4445
|
+
* return;
|
|
4446
|
+
* }
|
|
4447
|
+
*
|
|
4448
|
+
* // Read the content
|
|
4449
|
+
* const draft = await event.read();
|
|
3148
4450
|
*
|
|
3149
|
-
*
|
|
3150
|
-
*
|
|
4451
|
+
* // Get information about selected images
|
|
4452
|
+
* const imageRefs = draft.contents.map(content => content.ref);
|
|
4453
|
+
*
|
|
4454
|
+
* // The ref can be used with other API methods
|
|
4455
|
+
* }
|
|
4456
|
+
* });
|
|
4457
|
+
* ```
|
|
4458
|
+
*
|
|
4459
|
+
* @example Process multiple selected items
|
|
4460
|
+
* ```typescript
|
|
4461
|
+
* import { selection } from "@canva/design";
|
|
4462
|
+
*
|
|
4463
|
+
* selection.registerOnChange({
|
|
4464
|
+
* scope: 'richtext',
|
|
4465
|
+
* onChange: async (event) => {
|
|
4466
|
+
* if (event.count > 0) {
|
|
4467
|
+
* const draft = await event.read();
|
|
4468
|
+
*
|
|
4469
|
+
* // Process each selected range
|
|
4470
|
+
* draft.contents.forEach((range) => {
|
|
4471
|
+
* // Do something with the range content and formatted regions, with `range.readPlaintext()` and `range.readTextRegions()`
|
|
4472
|
+
* });
|
|
4473
|
+
* }
|
|
4474
|
+
* }
|
|
4475
|
+
* });
|
|
3151
4476
|
* ```
|
|
3152
4477
|
*/
|
|
3153
4478
|
read(): Promise<ContentDraft<SelectionValue<Scope>>>;
|
|
@@ -3230,6 +4555,56 @@ export declare type SelectionValue<Scope extends SelectionScope> = {
|
|
|
3230
4555
|
* @public
|
|
3231
4556
|
* Updates the background of the user's current page. The background can be a solid color,
|
|
3232
4557
|
* an image or a video.
|
|
4558
|
+
*
|
|
4559
|
+
* @example Set background color
|
|
4560
|
+
* ```typescript
|
|
4561
|
+
* import { setCurrentPageBackground } from "@canva/design";
|
|
4562
|
+
* import type { PageBackgroundFill } from "@canva/design";
|
|
4563
|
+
*
|
|
4564
|
+
* const background: PageBackgroundFill = {
|
|
4565
|
+
* color: '#F5F5F5',
|
|
4566
|
+
* };
|
|
4567
|
+
*
|
|
4568
|
+
* await setCurrentPageBackground(background);
|
|
4569
|
+
* ```
|
|
4570
|
+
*
|
|
4571
|
+
* @example Set background image
|
|
4572
|
+
* ```typescript
|
|
4573
|
+
* import { setCurrentPageBackground } from "@canva/design";
|
|
4574
|
+
* import type { PageBackgroundFill } from "@canva/design";
|
|
4575
|
+
* import type { ImageRef } from "@canva/asset";
|
|
4576
|
+
*
|
|
4577
|
+
* const exampleImageRef = "YOUR_IMAGE_REF" as ImageRef;
|
|
4578
|
+
*
|
|
4579
|
+
* const background: PageBackgroundFill = {
|
|
4580
|
+
* asset: {
|
|
4581
|
+
* type: 'image',
|
|
4582
|
+
* ref: exampleImageRef,
|
|
4583
|
+
* altText: { text: 'Background image', decorative: true }
|
|
4584
|
+
* },
|
|
4585
|
+
* };
|
|
4586
|
+
*
|
|
4587
|
+
* await setCurrentPageBackground(background);
|
|
4588
|
+
* ```
|
|
4589
|
+
*
|
|
4590
|
+
* @example Set background video
|
|
4591
|
+
* ```typescript
|
|
4592
|
+
* import { setCurrentPageBackground } from "@canva/design";
|
|
4593
|
+
* import type { PageBackgroundFill } from "@canva/design";
|
|
4594
|
+
* import type { VideoRef } from "@canva/asset";
|
|
4595
|
+
*
|
|
4596
|
+
* const exampleVideoRef = "YOUR_VIDEO_REF" as VideoRef;
|
|
4597
|
+
*
|
|
4598
|
+
* const background: PageBackgroundFill = {
|
|
4599
|
+
* asset: {
|
|
4600
|
+
* type: 'video',
|
|
4601
|
+
* ref: exampleVideoRef,
|
|
4602
|
+
* altText: { text: 'Background video', decorative: true }
|
|
4603
|
+
* },
|
|
4604
|
+
* };
|
|
4605
|
+
*
|
|
4606
|
+
* await setCurrentPageBackground(background);
|
|
4607
|
+
* ```
|
|
3233
4608
|
*/
|
|
3234
4609
|
export declare const setCurrentPageBackground: (
|
|
3235
4610
|
opts: PageBackgroundFill,
|
|
@@ -3445,11 +4820,6 @@ export declare type TextDragConfig = {
|
|
|
3445
4820
|
* @defaultValue "none"
|
|
3446
4821
|
*/
|
|
3447
4822
|
decoration?: "none" | "underline";
|
|
3448
|
-
/**
|
|
3449
|
-
* @beta
|
|
3450
|
-
* A unique identifier that points to a font asset in Canva's backend.
|
|
3451
|
-
*/
|
|
3452
|
-
fontRef?: FontRef;
|
|
3453
4823
|
};
|
|
3454
4824
|
|
|
3455
4825
|
/**
|