@tldraw/tlschema 4.1.0-canary.ccd6179e1cb2 → 4.1.0-canary.d0d868ee0e4c

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.
Files changed (214) hide show
  1. package/dist-cjs/TLStore.js +3 -10
  2. package/dist-cjs/TLStore.js.map +2 -2
  3. package/dist-cjs/assets/TLBaseAsset.js.map +2 -2
  4. package/dist-cjs/assets/TLBookmarkAsset.js.map +2 -2
  5. package/dist-cjs/assets/TLImageAsset.js.map +2 -2
  6. package/dist-cjs/assets/TLVideoAsset.js.map +2 -2
  7. package/dist-cjs/bindings/TLArrowBinding.js.map +2 -2
  8. package/dist-cjs/bindings/TLBaseBinding.js.map +2 -2
  9. package/dist-cjs/createPresenceStateDerivation.js.map +2 -2
  10. package/dist-cjs/createTLSchema.js.map +2 -2
  11. package/dist-cjs/index.d.ts +4416 -223
  12. package/dist-cjs/index.js +1 -1
  13. package/dist-cjs/index.js.map +2 -2
  14. package/dist-cjs/misc/TLColor.js.map +2 -2
  15. package/dist-cjs/misc/TLCursor.js.map +2 -2
  16. package/dist-cjs/misc/TLHandle.js.map +2 -2
  17. package/dist-cjs/misc/TLOpacity.js.map +2 -2
  18. package/dist-cjs/misc/TLRichText.js.map +2 -2
  19. package/dist-cjs/misc/TLScribble.js.map +2 -2
  20. package/dist-cjs/misc/geometry-types.js.map +2 -2
  21. package/dist-cjs/misc/id-validator.js.map +2 -2
  22. package/dist-cjs/records/TLAsset.js.map +2 -2
  23. package/dist-cjs/records/TLBinding.js.map +2 -2
  24. package/dist-cjs/records/TLCamera.js.map +2 -2
  25. package/dist-cjs/records/TLDocument.js.map +2 -2
  26. package/dist-cjs/records/TLInstance.js.map +2 -2
  27. package/dist-cjs/records/TLPage.js.map +2 -2
  28. package/dist-cjs/records/TLPageState.js.map +2 -2
  29. package/dist-cjs/records/TLPointer.js.map +2 -2
  30. package/dist-cjs/records/TLPresence.js.map +2 -2
  31. package/dist-cjs/records/TLRecord.js.map +1 -1
  32. package/dist-cjs/records/TLShape.js.map +2 -2
  33. package/dist-cjs/recordsWithProps.js.map +2 -2
  34. package/dist-cjs/shapes/ShapeWithCrop.js.map +1 -1
  35. package/dist-cjs/shapes/TLArrowShape.js.map +2 -2
  36. package/dist-cjs/shapes/TLBaseShape.js.map +2 -2
  37. package/dist-cjs/shapes/TLBookmarkShape.js.map +2 -2
  38. package/dist-cjs/shapes/TLDrawShape.js.map +2 -2
  39. package/dist-cjs/shapes/TLEmbedShape.js +0 -10
  40. package/dist-cjs/shapes/TLEmbedShape.js.map +2 -2
  41. package/dist-cjs/shapes/TLFrameShape.js.map +2 -2
  42. package/dist-cjs/shapes/TLGeoShape.js.map +2 -2
  43. package/dist-cjs/shapes/TLGroupShape.js.map +2 -2
  44. package/dist-cjs/shapes/TLHighlightShape.js.map +2 -2
  45. package/dist-cjs/shapes/TLImageShape.js.map +2 -2
  46. package/dist-cjs/shapes/TLLineShape.js.map +2 -2
  47. package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
  48. package/dist-cjs/shapes/TLTextShape.js.map +2 -2
  49. package/dist-cjs/shapes/TLVideoShape.js.map +2 -2
  50. package/dist-cjs/store-migrations.js.map +2 -2
  51. package/dist-cjs/styles/TLColorStyle.js.map +2 -2
  52. package/dist-cjs/styles/TLDashStyle.js.map +2 -2
  53. package/dist-cjs/styles/TLFillStyle.js.map +2 -2
  54. package/dist-cjs/styles/TLFontStyle.js.map +2 -2
  55. package/dist-cjs/styles/TLHorizontalAlignStyle.js.map +2 -2
  56. package/dist-cjs/styles/TLSizeStyle.js.map +2 -2
  57. package/dist-cjs/styles/TLTextAlignStyle.js.map +2 -2
  58. package/dist-cjs/styles/TLVerticalAlignStyle.js.map +2 -2
  59. package/dist-cjs/translations/translations.js +1 -1
  60. package/dist-cjs/translations/translations.js.map +2 -2
  61. package/dist-cjs/util-types.js.map +1 -1
  62. package/dist-esm/TLStore.mjs +3 -10
  63. package/dist-esm/TLStore.mjs.map +2 -2
  64. package/dist-esm/assets/TLBaseAsset.mjs.map +2 -2
  65. package/dist-esm/assets/TLBookmarkAsset.mjs.map +2 -2
  66. package/dist-esm/assets/TLImageAsset.mjs.map +2 -2
  67. package/dist-esm/assets/TLVideoAsset.mjs.map +2 -2
  68. package/dist-esm/bindings/TLArrowBinding.mjs.map +2 -2
  69. package/dist-esm/bindings/TLBaseBinding.mjs.map +2 -2
  70. package/dist-esm/createPresenceStateDerivation.mjs.map +2 -2
  71. package/dist-esm/createTLSchema.mjs.map +2 -2
  72. package/dist-esm/index.d.mts +4416 -223
  73. package/dist-esm/index.mjs +1 -1
  74. package/dist-esm/index.mjs.map +2 -2
  75. package/dist-esm/misc/TLColor.mjs.map +2 -2
  76. package/dist-esm/misc/TLCursor.mjs.map +2 -2
  77. package/dist-esm/misc/TLHandle.mjs.map +2 -2
  78. package/dist-esm/misc/TLOpacity.mjs.map +2 -2
  79. package/dist-esm/misc/TLRichText.mjs.map +2 -2
  80. package/dist-esm/misc/TLScribble.mjs.map +2 -2
  81. package/dist-esm/misc/geometry-types.mjs.map +2 -2
  82. package/dist-esm/misc/id-validator.mjs.map +2 -2
  83. package/dist-esm/records/TLAsset.mjs.map +2 -2
  84. package/dist-esm/records/TLBinding.mjs.map +2 -2
  85. package/dist-esm/records/TLCamera.mjs.map +2 -2
  86. package/dist-esm/records/TLDocument.mjs.map +2 -2
  87. package/dist-esm/records/TLInstance.mjs.map +2 -2
  88. package/dist-esm/records/TLPage.mjs.map +2 -2
  89. package/dist-esm/records/TLPageState.mjs.map +2 -2
  90. package/dist-esm/records/TLPointer.mjs.map +2 -2
  91. package/dist-esm/records/TLPresence.mjs.map +2 -2
  92. package/dist-esm/records/TLShape.mjs.map +2 -2
  93. package/dist-esm/recordsWithProps.mjs.map +2 -2
  94. package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
  95. package/dist-esm/shapes/TLBaseShape.mjs.map +2 -2
  96. package/dist-esm/shapes/TLBookmarkShape.mjs.map +2 -2
  97. package/dist-esm/shapes/TLDrawShape.mjs.map +2 -2
  98. package/dist-esm/shapes/TLEmbedShape.mjs +0 -10
  99. package/dist-esm/shapes/TLEmbedShape.mjs.map +2 -2
  100. package/dist-esm/shapes/TLFrameShape.mjs.map +2 -2
  101. package/dist-esm/shapes/TLGeoShape.mjs.map +2 -2
  102. package/dist-esm/shapes/TLGroupShape.mjs.map +2 -2
  103. package/dist-esm/shapes/TLHighlightShape.mjs.map +2 -2
  104. package/dist-esm/shapes/TLImageShape.mjs.map +2 -2
  105. package/dist-esm/shapes/TLLineShape.mjs.map +2 -2
  106. package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
  107. package/dist-esm/shapes/TLTextShape.mjs.map +2 -2
  108. package/dist-esm/shapes/TLVideoShape.mjs.map +2 -2
  109. package/dist-esm/store-migrations.mjs.map +2 -2
  110. package/dist-esm/styles/TLColorStyle.mjs.map +2 -2
  111. package/dist-esm/styles/TLDashStyle.mjs.map +2 -2
  112. package/dist-esm/styles/TLFillStyle.mjs.map +2 -2
  113. package/dist-esm/styles/TLFontStyle.mjs.map +2 -2
  114. package/dist-esm/styles/TLHorizontalAlignStyle.mjs.map +2 -2
  115. package/dist-esm/styles/TLSizeStyle.mjs.map +2 -2
  116. package/dist-esm/styles/TLTextAlignStyle.mjs.map +2 -2
  117. package/dist-esm/styles/TLVerticalAlignStyle.mjs.map +2 -2
  118. package/dist-esm/translations/translations.mjs +1 -1
  119. package/dist-esm/translations/translations.mjs.map +2 -2
  120. package/package.json +5 -5
  121. package/src/TLStore.test.ts +644 -0
  122. package/src/TLStore.ts +205 -20
  123. package/src/assets/TLBaseAsset.ts +90 -7
  124. package/src/assets/TLBookmarkAsset.test.ts +96 -0
  125. package/src/assets/TLBookmarkAsset.ts +52 -2
  126. package/src/assets/TLImageAsset.test.ts +213 -0
  127. package/src/assets/TLImageAsset.ts +60 -2
  128. package/src/assets/TLVideoAsset.test.ts +105 -0
  129. package/src/assets/TLVideoAsset.ts +93 -4
  130. package/src/bindings/TLArrowBinding.test.ts +55 -0
  131. package/src/bindings/TLArrowBinding.ts +132 -10
  132. package/src/bindings/TLBaseBinding.ts +140 -3
  133. package/src/createPresenceStateDerivation.test.ts +158 -0
  134. package/src/createPresenceStateDerivation.ts +71 -2
  135. package/src/createTLSchema.test.ts +181 -0
  136. package/src/createTLSchema.ts +164 -7
  137. package/src/index.ts +32 -0
  138. package/src/misc/TLColor.ts +50 -6
  139. package/src/misc/TLCursor.ts +110 -8
  140. package/src/misc/TLHandle.ts +86 -6
  141. package/src/misc/TLOpacity.ts +51 -2
  142. package/src/misc/TLRichText.ts +56 -3
  143. package/src/misc/TLScribble.ts +105 -5
  144. package/src/misc/geometry-types.ts +30 -2
  145. package/src/misc/id-validator.test.ts +50 -0
  146. package/src/misc/id-validator.ts +20 -1
  147. package/src/records/TLAsset.test.ts +234 -0
  148. package/src/records/TLAsset.ts +165 -8
  149. package/src/records/TLBinding.test.ts +22 -0
  150. package/src/records/TLBinding.ts +277 -11
  151. package/src/records/TLCamera.test.ts +19 -0
  152. package/src/records/TLCamera.ts +118 -7
  153. package/src/records/TLDocument.test.ts +35 -0
  154. package/src/records/TLDocument.ts +148 -8
  155. package/src/records/TLInstance.test.ts +201 -0
  156. package/src/records/TLInstance.ts +117 -9
  157. package/src/records/TLPage.test.ts +110 -0
  158. package/src/records/TLPage.ts +106 -8
  159. package/src/records/TLPageState.test.ts +228 -0
  160. package/src/records/TLPageState.ts +88 -7
  161. package/src/records/TLPointer.test.ts +63 -0
  162. package/src/records/TLPointer.ts +105 -7
  163. package/src/records/TLPresence.test.ts +190 -0
  164. package/src/records/TLPresence.ts +99 -5
  165. package/src/records/TLRecord.test.ts +70 -0
  166. package/src/records/TLRecord.ts +43 -1
  167. package/src/records/TLShape.test.ts +232 -0
  168. package/src/records/TLShape.ts +289 -12
  169. package/src/recordsWithProps.test.ts +188 -0
  170. package/src/recordsWithProps.ts +131 -2
  171. package/src/shapes/ShapeWithCrop.test.ts +18 -0
  172. package/src/shapes/ShapeWithCrop.ts +64 -2
  173. package/src/shapes/TLArrowShape.test.ts +505 -0
  174. package/src/shapes/TLArrowShape.ts +188 -10
  175. package/src/shapes/TLBaseShape.test.ts +142 -0
  176. package/src/shapes/TLBaseShape.ts +103 -4
  177. package/src/shapes/TLBookmarkShape.test.ts +122 -0
  178. package/src/shapes/TLBookmarkShape.ts +58 -4
  179. package/src/shapes/TLDrawShape.test.ts +177 -0
  180. package/src/shapes/TLDrawShape.ts +97 -6
  181. package/src/shapes/TLEmbedShape.test.ts +286 -0
  182. package/src/shapes/TLEmbedShape.ts +57 -14
  183. package/src/shapes/TLFrameShape.test.ts +71 -0
  184. package/src/shapes/TLFrameShape.ts +59 -4
  185. package/src/shapes/TLGeoShape.test.ts +247 -0
  186. package/src/shapes/TLGeoShape.ts +103 -7
  187. package/src/shapes/TLGroupShape.test.ts +59 -0
  188. package/src/shapes/TLGroupShape.ts +52 -4
  189. package/src/shapes/TLHighlightShape.test.ts +325 -0
  190. package/src/shapes/TLHighlightShape.ts +79 -4
  191. package/src/shapes/TLImageShape.test.ts +534 -0
  192. package/src/shapes/TLImageShape.ts +105 -5
  193. package/src/shapes/TLLineShape.test.ts +269 -0
  194. package/src/shapes/TLLineShape.ts +128 -8
  195. package/src/shapes/TLNoteShape.test.ts +1568 -0
  196. package/src/shapes/TLNoteShape.ts +97 -4
  197. package/src/shapes/TLTextShape.test.ts +407 -0
  198. package/src/shapes/TLTextShape.ts +94 -4
  199. package/src/shapes/TLVideoShape.test.ts +112 -0
  200. package/src/shapes/TLVideoShape.ts +99 -4
  201. package/src/store-migrations.test.ts +88 -0
  202. package/src/store-migrations.ts +47 -1
  203. package/src/styles/TLColorStyle.test.ts +439 -0
  204. package/src/styles/TLColorStyle.ts +228 -10
  205. package/src/styles/TLDashStyle.ts +54 -2
  206. package/src/styles/TLFillStyle.ts +54 -2
  207. package/src/styles/TLFontStyle.ts +72 -3
  208. package/src/styles/TLHorizontalAlignStyle.ts +55 -2
  209. package/src/styles/TLSizeStyle.ts +54 -2
  210. package/src/styles/TLTextAlignStyle.ts +52 -2
  211. package/src/styles/TLVerticalAlignStyle.ts +52 -2
  212. package/src/translations/translations.test.ts +378 -35
  213. package/src/translations/translations.ts +157 -10
  214. package/src/util-types.ts +51 -1
@@ -203,16 +203,6 @@ const EMBED_DEFINITIONS = [
203
203
  return
204
204
  },
205
205
  },
206
- {
207
- hostnames: ['excalidraw.com'],
208
- fromEmbedUrl: (url: string) => {
209
- const urlObj = safeParseUrl(url)
210
- if (urlObj && urlObj.hash.match(/#room=/)) {
211
- return url
212
- }
213
- return
214
- },
215
- },
216
206
  {
217
207
  hostnames: ['observablehq.com'],
218
208
  fromEmbedUrl: (url: string) => {
@@ -245,17 +235,60 @@ const EMBED_DEFINITIONS = [
245
235
  },
246
236
  ]
247
237
 
248
- /** @public */
238
+ /**
239
+ * Properties for the embed shape, which displays embedded content from external services.
240
+ *
241
+ * @public
242
+ */
249
243
  export interface TLEmbedShapeProps {
244
+ /** Width of the embed shape in pixels */
250
245
  w: number
246
+ /** Height of the embed shape in pixels */
251
247
  h: number
248
+ /** URL of the content to embed (supports YouTube, Figma, CodePen, etc.) */
252
249
  url: string
253
250
  }
254
251
 
255
- /** @public */
252
+ /**
253
+ * An embed shape displays external content like YouTube videos, Figma designs, CodePen demos,
254
+ * and other embeddable content within the tldraw canvas.
255
+ *
256
+ * @public
257
+ * @example
258
+ * ```ts
259
+ * const embedShape: TLEmbedShape = {
260
+ * id: createShapeId(),
261
+ * typeName: 'shape',
262
+ * type: 'embed',
263
+ * x: 200,
264
+ * y: 200,
265
+ * rotation: 0,
266
+ * index: 'a1',
267
+ * parentId: 'page:page1',
268
+ * isLocked: false,
269
+ * opacity: 1,
270
+ * props: {
271
+ * w: 560,
272
+ * h: 315,
273
+ * url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
274
+ * },
275
+ * meta: {}
276
+ * }
277
+ * ```
278
+ */
256
279
  export type TLEmbedShape = TLBaseShape<'embed', TLEmbedShapeProps>
257
280
 
258
- /** @public */
281
+ /**
282
+ * Validation schema for embed shape properties.
283
+ *
284
+ * @public
285
+ * @example
286
+ * ```ts
287
+ * // Validate embed shape properties
288
+ * const isValidUrl = embedShapeProps.url.isValid('https://youtube.com/watch?v=abc123')
289
+ * const isValidSize = embedShapeProps.w.isValid(560)
290
+ * ```
291
+ */
259
292
  export const embedShapeProps: RecordProps<TLEmbedShape> = {
260
293
  w: T.nonZeroNumber,
261
294
  h: T.nonZeroNumber,
@@ -269,9 +302,19 @@ const Versions = createShapePropsMigrationIds('embed', {
269
302
  RemovePermissionOverrides: 4,
270
303
  })
271
304
 
305
+ /**
306
+ * Version identifiers for embed shape migrations.
307
+ *
308
+ * @public
309
+ */
272
310
  export { Versions as embedShapeVersions }
273
311
 
274
- /** @public */
312
+ /**
313
+ * Migration sequence for embed shape properties across different schema versions.
314
+ * Handles URL transformations and removal of deprecated properties.
315
+ *
316
+ * @public
317
+ */
275
318
  export const embedShapeMigrations = createShapePropsMigrationSequence({
276
319
  sequence: [
277
320
  {
@@ -0,0 +1,71 @@
1
+ import { T } from '@tldraw/validate'
2
+ import { describe, expect, it } from 'vitest'
3
+ import { getTestMigration } from '../__tests__/migrationTestUtils'
4
+ import { frameShapeProps, frameShapeVersions } from './TLFrameShape'
5
+
6
+ describe('TLFrameShape', () => {
7
+ describe('frameShapeProps validation', () => {
8
+ it('should validate valid frame props', () => {
9
+ const validProps = {
10
+ w: 400,
11
+ h: 300,
12
+ name: 'Test Frame',
13
+ color: 'blue' as const,
14
+ }
15
+
16
+ const validator = T.object(frameShapeProps)
17
+ expect(() => validator.validate(validProps)).not.toThrow()
18
+ })
19
+
20
+ it('should reject invalid dimensions', () => {
21
+ // Zero and negative values should be rejected
22
+ expect(() => frameShapeProps.w.validate(0)).toThrow()
23
+ expect(() => frameShapeProps.h.validate(-1)).toThrow()
24
+ })
25
+
26
+ it('should reject invalid colors', () => {
27
+ // Invalid color values
28
+ expect(() => frameShapeProps.color.validate('invalid-color')).toThrow()
29
+ expect(() => frameShapeProps.color.validate('')).toThrow()
30
+ })
31
+ })
32
+
33
+ describe('AddColorProp migration', () => {
34
+ const { up, down } = getTestMigration(frameShapeVersions.AddColorProp)
35
+
36
+ it('should add color property with default value "black"', () => {
37
+ const oldRecord = {
38
+ id: 'shape:frame1',
39
+ props: {
40
+ w: 400,
41
+ h: 300,
42
+ name: 'Test Frame',
43
+ },
44
+ }
45
+
46
+ const result = up(oldRecord)
47
+ expect(result.props.color).toBe('black')
48
+ expect(result.props.w).toBe(400)
49
+ expect(result.props.h).toBe(300)
50
+ expect(result.props.name).toBe('Test Frame')
51
+ })
52
+
53
+ it('should remove color property on down migration', () => {
54
+ const newRecord = {
55
+ id: 'shape:frame1',
56
+ props: {
57
+ w: 400,
58
+ h: 300,
59
+ name: 'Test Frame',
60
+ color: 'blue',
61
+ },
62
+ }
63
+
64
+ const result = down(newRecord)
65
+ expect(result.props.color).toBeUndefined()
66
+ expect(result.props.w).toBe(400)
67
+ expect(result.props.h).toBe(300)
68
+ expect(result.props.name).toBe('Test Frame')
69
+ })
70
+ })
71
+ })
@@ -4,18 +4,63 @@ import { RecordProps } from '../recordsWithProps'
4
4
  import { DefaultColorStyle, TLDefaultColorStyle } from '../styles/TLColorStyle'
5
5
  import { TLBaseShape } from './TLBaseShape'
6
6
 
7
- /** @public */
7
+ /**
8
+ * Properties for the frame shape, which provides a container for organizing other shapes.
9
+ *
10
+ * @public
11
+ */
8
12
  export interface TLFrameShapeProps {
13
+ /** Width of the frame in pixels */
9
14
  w: number
15
+ /** Height of the frame in pixels */
10
16
  h: number
17
+ /** Display name for the frame (shown in UI) */
11
18
  name: string
19
+ /** Color style for the frame border and label */
12
20
  color: TLDefaultColorStyle
13
21
  }
14
22
 
15
- /** @public */
23
+ /**
24
+ * A frame shape provides a container for organizing and grouping other shapes.
25
+ * Frames can be used to create sections, organize content, or define specific areas.
26
+ *
27
+ * @public
28
+ * @example
29
+ * ```ts
30
+ * const frameShape: TLFrameShape = {
31
+ * id: createShapeId(),
32
+ * typeName: 'shape',
33
+ * type: 'frame',
34
+ * x: 0,
35
+ * y: 0,
36
+ * rotation: 0,
37
+ * index: 'a1',
38
+ * parentId: 'page:page1',
39
+ * isLocked: false,
40
+ * opacity: 1,
41
+ * props: {
42
+ * w: 400,
43
+ * h: 300,
44
+ * name: 'Header Section',
45
+ * color: 'blue'
46
+ * },
47
+ * meta: {}
48
+ * }
49
+ * ```
50
+ */
16
51
  export type TLFrameShape = TLBaseShape<'frame', TLFrameShapeProps>
17
52
 
18
- /** @public */
53
+ /**
54
+ * Validation schema for frame shape properties.
55
+ *
56
+ * @public
57
+ * @example
58
+ * ```ts
59
+ * // Validate frame properties
60
+ * const isValidName = frameShapeProps.name.isValid('My Frame')
61
+ * const isValidColor = frameShapeProps.color.isValid('red')
62
+ * ```
63
+ */
19
64
  export const frameShapeProps: RecordProps<TLFrameShape> = {
20
65
  w: T.nonZeroNumber,
21
66
  h: T.nonZeroNumber,
@@ -31,9 +76,19 @@ const Versions = createShapePropsMigrationIds('frame', {
31
76
  AddColorProp: 1,
32
77
  })
33
78
 
79
+ /**
80
+ * Version identifiers for frame shape migrations.
81
+ *
82
+ * @public
83
+ */
34
84
  export { Versions as frameShapeVersions }
35
85
 
36
- /** @public */
86
+ /**
87
+ * Migration sequence for frame shape properties across different schema versions.
88
+ * Handles adding color properties to existing frame shapes.
89
+ *
90
+ * @public
91
+ */
37
92
  export const frameShapeMigrations = createShapePropsMigrationSequence({
38
93
  sequence: [
39
94
  {
@@ -0,0 +1,247 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { getTestMigration } from '../__tests__/migrationTestUtils'
3
+ import { toRichText } from '../misc/TLRichText'
4
+ import { GeoShapeGeoStyle, geoShapeProps, geoShapeVersions } from './TLGeoShape'
5
+
6
+ describe('TLGeoShape', () => {
7
+ describe('GeoShapeGeoStyle', () => {
8
+ it('should validate all geometric shape types', () => {
9
+ const validGeoTypes = [
10
+ 'cloud',
11
+ 'rectangle',
12
+ 'ellipse',
13
+ 'triangle',
14
+ 'diamond',
15
+ 'pentagon',
16
+ 'hexagon',
17
+ 'octagon',
18
+ 'star',
19
+ 'rhombus',
20
+ 'rhombus-2',
21
+ 'oval',
22
+ 'trapezoid',
23
+ 'arrow-right',
24
+ 'arrow-left',
25
+ 'arrow-up',
26
+ 'arrow-down',
27
+ 'x-box',
28
+ 'check-box',
29
+ 'heart',
30
+ ]
31
+
32
+ validGeoTypes.forEach((geoType) => {
33
+ expect(() => GeoShapeGeoStyle.validate(geoType)).not.toThrow()
34
+ })
35
+ })
36
+
37
+ it('should reject invalid geometric shape types', () => {
38
+ const invalidGeoTypes = ['square', 'circle', 'invalid-shape', null, undefined]
39
+
40
+ invalidGeoTypes.forEach((geoType) => {
41
+ expect(() => GeoShapeGeoStyle.validate(geoType)).toThrow()
42
+ })
43
+ })
44
+ })
45
+
46
+ describe('geoShapeProps validation schema', () => {
47
+ it('should validate numeric constraints', () => {
48
+ // Test nonZeroNumber validation for w, h, scale
49
+ expect(() => geoShapeProps.w.validate(100)).not.toThrow()
50
+ expect(() => geoShapeProps.h.validate(50)).not.toThrow()
51
+ expect(() => geoShapeProps.scale.validate(1.5)).not.toThrow()
52
+
53
+ expect(() => geoShapeProps.w.validate(0)).toThrow()
54
+ expect(() => geoShapeProps.h.validate(0)).toThrow()
55
+ expect(() => geoShapeProps.scale.validate(0)).toThrow()
56
+
57
+ // Test positiveNumber validation for growY
58
+ expect(() => geoShapeProps.growY.validate(0)).not.toThrow()
59
+ expect(() => geoShapeProps.growY.validate(10)).not.toThrow()
60
+ expect(() => geoShapeProps.growY.validate(-1)).toThrow()
61
+ })
62
+
63
+ it('should validate rich text property', () => {
64
+ expect(() => geoShapeProps.richText.validate(toRichText('test'))).not.toThrow()
65
+ expect(() => geoShapeProps.richText.validate('plain string')).toThrow()
66
+ })
67
+ })
68
+
69
+ describe('geoShapeMigrations - AddUrlProp migration', () => {
70
+ const { up } = getTestMigration(geoShapeVersions.AddUrlProp)
71
+
72
+ it('should add url property with empty string default', () => {
73
+ const oldRecord = {
74
+ props: {
75
+ geo: 'rectangle',
76
+ w: 100,
77
+ h: 80,
78
+ },
79
+ }
80
+
81
+ const result = up(oldRecord)
82
+ expect(result.props.url).toBe('')
83
+ })
84
+ })
85
+
86
+ describe('geoShapeMigrations - AddLabelColor migration', () => {
87
+ const { up } = getTestMigration(geoShapeVersions.AddLabelColor)
88
+
89
+ it('should add labelColor property with default value "black"', () => {
90
+ const oldRecord = {
91
+ props: {
92
+ geo: 'triangle',
93
+ color: 'blue',
94
+ },
95
+ }
96
+
97
+ const result = up(oldRecord)
98
+ expect(result.props.labelColor).toBe('black')
99
+ })
100
+ })
101
+
102
+ describe('geoShapeMigrations - RemoveJustify migration', () => {
103
+ const { up } = getTestMigration(geoShapeVersions.RemoveJustify)
104
+
105
+ it('should convert justify alignment to start', () => {
106
+ const oldRecord = {
107
+ props: {
108
+ align: 'justify',
109
+ },
110
+ }
111
+
112
+ const result = up(oldRecord)
113
+ expect(result.props.align).toBe('start')
114
+ })
115
+
116
+ it('should preserve non-justify alignments', () => {
117
+ const oldRecord = {
118
+ props: {
119
+ align: 'middle',
120
+ },
121
+ }
122
+
123
+ const result = up(oldRecord)
124
+ expect(result.props.align).toBe('middle')
125
+ })
126
+ })
127
+
128
+ describe('geoShapeMigrations - AddVerticalAlign migration', () => {
129
+ const { up } = getTestMigration(geoShapeVersions.AddVerticalAlign)
130
+
131
+ it('should add verticalAlign property with default value "middle"', () => {
132
+ const oldRecord = {
133
+ props: {
134
+ geo: 'rectangle',
135
+ },
136
+ }
137
+
138
+ const result = up(oldRecord)
139
+ expect(result.props.verticalAlign).toBe('middle')
140
+ })
141
+ })
142
+
143
+ describe('geoShapeMigrations - MigrateLegacyAlign migration', () => {
144
+ const { up } = getTestMigration(geoShapeVersions.MigrateLegacyAlign)
145
+
146
+ it('should convert alignment values to legacy versions', () => {
147
+ const testCases = [
148
+ { input: 'start', expected: 'start-legacy' },
149
+ { input: 'end', expected: 'end-legacy' },
150
+ { input: 'middle', expected: 'middle-legacy' },
151
+ { input: 'unknown-align', expected: 'middle-legacy' },
152
+ ]
153
+
154
+ testCases.forEach(({ input, expected }) => {
155
+ const oldRecord = {
156
+ props: {
157
+ align: input,
158
+ },
159
+ }
160
+
161
+ const result = up(oldRecord)
162
+ expect(result.props.align).toBe(expected)
163
+ })
164
+ })
165
+ })
166
+
167
+ describe('geoShapeMigrations - MakeUrlsValid migration', () => {
168
+ const { up } = getTestMigration(geoShapeVersions.MakeUrlsValid)
169
+
170
+ it('should clear invalid URLs', () => {
171
+ const oldRecord = {
172
+ props: {
173
+ url: 'invalid-url',
174
+ },
175
+ }
176
+
177
+ const result = up(oldRecord)
178
+ expect(result.props.url).toBe('')
179
+ })
180
+
181
+ it('should preserve valid URLs', () => {
182
+ const oldRecord = {
183
+ props: {
184
+ url: 'https://example.com',
185
+ },
186
+ }
187
+
188
+ const result = up(oldRecord)
189
+ expect(result.props.url).toBe('https://example.com')
190
+ })
191
+ })
192
+
193
+ describe('geoShapeMigrations - AddScale migration', () => {
194
+ const { up, down } = getTestMigration(geoShapeVersions.AddScale)
195
+
196
+ it('should add scale property with default value 1', () => {
197
+ const oldRecord = {
198
+ props: {
199
+ geo: 'rectangle',
200
+ },
201
+ }
202
+
203
+ const result = up(oldRecord)
204
+ expect(result.props.scale).toBe(1)
205
+ })
206
+
207
+ it('should remove scale property on down migration', () => {
208
+ const newRecord = {
209
+ props: {
210
+ geo: 'rectangle',
211
+ scale: 1.5,
212
+ },
213
+ }
214
+
215
+ const result = down(newRecord)
216
+ expect(result.props.scale).toBeUndefined()
217
+ })
218
+ })
219
+
220
+ describe('geoShapeMigrations - AddRichText migration', () => {
221
+ const { up } = getTestMigration(geoShapeVersions.AddRichText)
222
+
223
+ it('should convert text property to richText', () => {
224
+ const oldRecord = {
225
+ props: {
226
+ text: 'Simple text content',
227
+ },
228
+ }
229
+
230
+ const result = up(oldRecord)
231
+ expect(result.props.richText).toBeDefined()
232
+ expect(result.props.text).toBeUndefined()
233
+ })
234
+
235
+ it('should handle empty text', () => {
236
+ const oldRecord = {
237
+ props: {
238
+ text: '',
239
+ },
240
+ }
241
+
242
+ const result = up(oldRecord)
243
+ expect(result.props.richText).toBeDefined()
244
+ expect(result.props.text).toBeUndefined()
245
+ })
246
+ })
247
+ })
@@ -22,7 +22,21 @@ import {
22
22
  } from '../styles/TLVerticalAlignStyle'
23
23
  import { TLBaseShape } from './TLBaseShape'
24
24
 
25
- /** @public */
25
+ /**
26
+ * Style property defining the geometric shape type for geo shapes.
27
+ * Provides a variety of built-in geometric forms including basic shapes,
28
+ * polygons, arrows, and special shapes.
29
+ *
30
+ * @public
31
+ * @example
32
+ * ```ts
33
+ * // Use in shape props
34
+ * const props = {
35
+ * geo: 'rectangle', // or 'ellipse', 'triangle', etc.
36
+ * // other properties...
37
+ * }
38
+ * ```
39
+ */
26
40
  export const GeoShapeGeoStyle = StyleProp.defineEnum('tldraw:geo', {
27
41
  defaultValue: 'rectangle',
28
42
  values: [
@@ -49,34 +63,105 @@ export const GeoShapeGeoStyle = StyleProp.defineEnum('tldraw:geo', {
49
63
  ],
50
64
  })
51
65
 
52
- /** @public */
66
+ /**
67
+ * Type representing valid geometric shape styles for geo shapes.
68
+ *
69
+ * @public
70
+ */
53
71
  export type TLGeoShapeGeoStyle = T.TypeOf<typeof GeoShapeGeoStyle>
54
72
 
55
- /** @public */
73
+ /**
74
+ * Properties for the geo shape, which renders various geometric forms with styling and text.
75
+ *
76
+ * @public
77
+ */
56
78
  export interface TLGeoShapeProps {
79
+ /** Geometric shape type (rectangle, ellipse, triangle, etc.) */
57
80
  geo: TLGeoShapeGeoStyle
81
+ /** Dash pattern style for the shape outline */
58
82
  dash: TLDefaultDashStyle
83
+ /** URL link associated with the shape */
59
84
  url: string
85
+ /** Width of the shape in pixels */
60
86
  w: number
87
+ /** Height of the shape in pixels */
61
88
  h: number
89
+ /** Additional vertical growth for text content */
62
90
  growY: number
91
+ /** Scale factor applied to the shape */
63
92
  scale: number
64
93
 
65
- // Text properties
94
+ /** Color style for text label */
66
95
  labelColor: TLDefaultColorStyle
96
+ /** Color style for the shape outline */
67
97
  color: TLDefaultColorStyle
98
+ /** Fill style for the shape interior */
68
99
  fill: TLDefaultFillStyle
100
+ /** Size/thickness style for outline and text */
69
101
  size: TLDefaultSizeStyle
102
+ /** Font style for text content */
70
103
  font: TLDefaultFontStyle
104
+ /** Horizontal alignment for text content */
71
105
  align: TLDefaultHorizontalAlignStyle
106
+ /** Vertical alignment for text content */
72
107
  verticalAlign: TLDefaultVerticalAlignStyle
108
+ /** Rich text content displayed within the shape */
73
109
  richText: TLRichText
74
110
  }
75
111
 
76
- /** @public */
112
+ /**
113
+ * A geo shape represents geometric forms like rectangles, ellipses, triangles, and other
114
+ * predefined shapes. Geo shapes support styling, text content, and can act as containers.
115
+ *
116
+ * @public
117
+ * @example
118
+ * ```ts
119
+ * const geoShape: TLGeoShape = {
120
+ * id: createShapeId(),
121
+ * typeName: 'shape',
122
+ * type: 'geo',
123
+ * x: 100,
124
+ * y: 100,
125
+ * rotation: 0,
126
+ * index: 'a1',
127
+ * parentId: 'page:page1',
128
+ * isLocked: false,
129
+ * opacity: 1,
130
+ * props: {
131
+ * geo: 'rectangle',
132
+ * w: 200,
133
+ * h: 100,
134
+ * color: 'black',
135
+ * fill: 'solid',
136
+ * dash: 'solid',
137
+ * size: 'm',
138
+ * font: 'draw',
139
+ * align: 'middle',
140
+ * verticalAlign: 'middle',
141
+ * richText: toRichText('Hello World'),
142
+ * labelColor: 'black',
143
+ * url: '',
144
+ * growY: 0,
145
+ * scale: 1
146
+ * },
147
+ * meta: {}
148
+ * }
149
+ * ```
150
+ */
77
151
  export type TLGeoShape = TLBaseShape<'geo', TLGeoShapeProps>
78
152
 
79
- /** @public */
153
+ /**
154
+ * Validation schema for geo shape properties.
155
+ *
156
+ * @public
157
+ * @example
158
+ * ```ts
159
+ * // Validate geo shape properties
160
+ * const isValidGeo = geoShapeProps.geo.isValid('rectangle')
161
+ * const isValidSize = geoShapeProps.w.isValid(100)
162
+ * const isValidText = geoShapeProps.richText.isValid(toRichText('Hello'))
163
+ * ```
164
+ */
80
165
  export const geoShapeProps: RecordProps<TLGeoShape> = {
81
166
  geo: GeoShapeGeoStyle,
82
167
  dash: DefaultDashStyle,
@@ -110,9 +195,20 @@ const geoShapeVersions = createShapePropsMigrationIds('geo', {
110
195
  AddRichText: 10,
111
196
  })
112
197
 
198
+ /**
199
+ * Version identifiers for geo shape migrations.
200
+ *
201
+ * @public
202
+ */
113
203
  export { geoShapeVersions as geoShapeVersions }
114
204
 
115
- /** @public */
205
+ /**
206
+ * Migration sequence for geo shape properties across different schema versions.
207
+ * Handles evolution of geo shapes including URL support, label colors, alignment changes,
208
+ * and the transition from plain text to rich text.
209
+ *
210
+ * @public
211
+ */
116
212
  export const geoShapeMigrations = createShapePropsMigrationSequence({
117
213
  sequence: [
118
214
  {