@tldraw/tlschema 4.1.0-canary.9f9255bd7a83 → 4.1.0-canary.a152954244d2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/TLStore.js +3 -10
- package/dist-cjs/TLStore.js.map +2 -2
- package/dist-cjs/assets/TLBaseAsset.js.map +2 -2
- package/dist-cjs/assets/TLBookmarkAsset.js.map +2 -2
- package/dist-cjs/assets/TLImageAsset.js.map +2 -2
- package/dist-cjs/assets/TLVideoAsset.js.map +2 -2
- package/dist-cjs/bindings/TLArrowBinding.js.map +2 -2
- package/dist-cjs/bindings/TLBaseBinding.js.map +2 -2
- package/dist-cjs/createPresenceStateDerivation.js.map +2 -2
- package/dist-cjs/createTLSchema.js.map +2 -2
- package/dist-cjs/index.d.ts +4416 -223
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/misc/TLColor.js.map +2 -2
- package/dist-cjs/misc/TLCursor.js.map +2 -2
- package/dist-cjs/misc/TLHandle.js.map +2 -2
- package/dist-cjs/misc/TLOpacity.js.map +2 -2
- package/dist-cjs/misc/TLRichText.js.map +2 -2
- package/dist-cjs/misc/TLScribble.js.map +2 -2
- package/dist-cjs/misc/geometry-types.js.map +2 -2
- package/dist-cjs/misc/id-validator.js.map +2 -2
- package/dist-cjs/records/TLAsset.js.map +2 -2
- package/dist-cjs/records/TLBinding.js.map +2 -2
- package/dist-cjs/records/TLCamera.js.map +2 -2
- package/dist-cjs/records/TLDocument.js.map +2 -2
- package/dist-cjs/records/TLInstance.js.map +2 -2
- package/dist-cjs/records/TLPage.js.map +2 -2
- package/dist-cjs/records/TLPageState.js.map +2 -2
- package/dist-cjs/records/TLPointer.js.map +2 -2
- package/dist-cjs/records/TLPresence.js.map +2 -2
- package/dist-cjs/records/TLRecord.js.map +1 -1
- package/dist-cjs/records/TLShape.js.map +2 -2
- package/dist-cjs/recordsWithProps.js.map +2 -2
- package/dist-cjs/shapes/ShapeWithCrop.js.map +1 -1
- package/dist-cjs/shapes/TLArrowShape.js.map +2 -2
- package/dist-cjs/shapes/TLBaseShape.js.map +2 -2
- package/dist-cjs/shapes/TLBookmarkShape.js.map +2 -2
- package/dist-cjs/shapes/TLDrawShape.js.map +2 -2
- package/dist-cjs/shapes/TLEmbedShape.js +0 -10
- package/dist-cjs/shapes/TLEmbedShape.js.map +2 -2
- package/dist-cjs/shapes/TLFrameShape.js.map +2 -2
- package/dist-cjs/shapes/TLGeoShape.js.map +2 -2
- package/dist-cjs/shapes/TLGroupShape.js.map +2 -2
- package/dist-cjs/shapes/TLHighlightShape.js.map +2 -2
- package/dist-cjs/shapes/TLImageShape.js.map +2 -2
- package/dist-cjs/shapes/TLLineShape.js.map +2 -2
- package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
- package/dist-cjs/shapes/TLTextShape.js.map +2 -2
- package/dist-cjs/shapes/TLVideoShape.js.map +2 -2
- package/dist-cjs/store-migrations.js.map +2 -2
- package/dist-cjs/styles/TLColorStyle.js.map +2 -2
- package/dist-cjs/styles/TLDashStyle.js.map +2 -2
- package/dist-cjs/styles/TLFillStyle.js.map +2 -2
- package/dist-cjs/styles/TLFontStyle.js.map +2 -2
- package/dist-cjs/styles/TLHorizontalAlignStyle.js.map +2 -2
- package/dist-cjs/styles/TLSizeStyle.js.map +2 -2
- package/dist-cjs/styles/TLTextAlignStyle.js.map +2 -2
- package/dist-cjs/styles/TLVerticalAlignStyle.js.map +2 -2
- package/dist-cjs/translations/translations.js +1 -1
- package/dist-cjs/translations/translations.js.map +2 -2
- package/dist-cjs/util-types.js.map +1 -1
- package/dist-esm/TLStore.mjs +3 -10
- package/dist-esm/TLStore.mjs.map +2 -2
- package/dist-esm/assets/TLBaseAsset.mjs.map +2 -2
- package/dist-esm/assets/TLBookmarkAsset.mjs.map +2 -2
- package/dist-esm/assets/TLImageAsset.mjs.map +2 -2
- package/dist-esm/assets/TLVideoAsset.mjs.map +2 -2
- package/dist-esm/bindings/TLArrowBinding.mjs.map +2 -2
- package/dist-esm/bindings/TLBaseBinding.mjs.map +2 -2
- package/dist-esm/createPresenceStateDerivation.mjs.map +2 -2
- package/dist-esm/createTLSchema.mjs.map +2 -2
- package/dist-esm/index.d.mts +4416 -223
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/misc/TLColor.mjs.map +2 -2
- package/dist-esm/misc/TLCursor.mjs.map +2 -2
- package/dist-esm/misc/TLHandle.mjs.map +2 -2
- package/dist-esm/misc/TLOpacity.mjs.map +2 -2
- package/dist-esm/misc/TLRichText.mjs.map +2 -2
- package/dist-esm/misc/TLScribble.mjs.map +2 -2
- package/dist-esm/misc/geometry-types.mjs.map +2 -2
- package/dist-esm/misc/id-validator.mjs.map +2 -2
- package/dist-esm/records/TLAsset.mjs.map +2 -2
- package/dist-esm/records/TLBinding.mjs.map +2 -2
- package/dist-esm/records/TLCamera.mjs.map +2 -2
- package/dist-esm/records/TLDocument.mjs.map +2 -2
- package/dist-esm/records/TLInstance.mjs.map +2 -2
- package/dist-esm/records/TLPage.mjs.map +2 -2
- package/dist-esm/records/TLPageState.mjs.map +2 -2
- package/dist-esm/records/TLPointer.mjs.map +2 -2
- package/dist-esm/records/TLPresence.mjs.map +2 -2
- package/dist-esm/records/TLShape.mjs.map +2 -2
- package/dist-esm/recordsWithProps.mjs.map +2 -2
- package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
- package/dist-esm/shapes/TLBaseShape.mjs.map +2 -2
- package/dist-esm/shapes/TLBookmarkShape.mjs.map +2 -2
- package/dist-esm/shapes/TLDrawShape.mjs.map +2 -2
- package/dist-esm/shapes/TLEmbedShape.mjs +0 -10
- package/dist-esm/shapes/TLEmbedShape.mjs.map +2 -2
- package/dist-esm/shapes/TLFrameShape.mjs.map +2 -2
- package/dist-esm/shapes/TLGeoShape.mjs.map +2 -2
- package/dist-esm/shapes/TLGroupShape.mjs.map +2 -2
- package/dist-esm/shapes/TLHighlightShape.mjs.map +2 -2
- package/dist-esm/shapes/TLImageShape.mjs.map +2 -2
- package/dist-esm/shapes/TLLineShape.mjs.map +2 -2
- package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
- package/dist-esm/shapes/TLTextShape.mjs.map +2 -2
- package/dist-esm/shapes/TLVideoShape.mjs.map +2 -2
- package/dist-esm/store-migrations.mjs.map +2 -2
- package/dist-esm/styles/TLColorStyle.mjs.map +2 -2
- package/dist-esm/styles/TLDashStyle.mjs.map +2 -2
- package/dist-esm/styles/TLFillStyle.mjs.map +2 -2
- package/dist-esm/styles/TLFontStyle.mjs.map +2 -2
- package/dist-esm/styles/TLHorizontalAlignStyle.mjs.map +2 -2
- package/dist-esm/styles/TLSizeStyle.mjs.map +2 -2
- package/dist-esm/styles/TLTextAlignStyle.mjs.map +2 -2
- package/dist-esm/styles/TLVerticalAlignStyle.mjs.map +2 -2
- package/dist-esm/translations/translations.mjs +1 -1
- package/dist-esm/translations/translations.mjs.map +2 -2
- package/package.json +5 -5
- package/src/TLStore.test.ts +644 -0
- package/src/TLStore.ts +205 -20
- package/src/assets/TLBaseAsset.ts +90 -7
- package/src/assets/TLBookmarkAsset.test.ts +96 -0
- package/src/assets/TLBookmarkAsset.ts +52 -2
- package/src/assets/TLImageAsset.test.ts +213 -0
- package/src/assets/TLImageAsset.ts +60 -2
- package/src/assets/TLVideoAsset.test.ts +105 -0
- package/src/assets/TLVideoAsset.ts +93 -4
- package/src/bindings/TLArrowBinding.test.ts +55 -0
- package/src/bindings/TLArrowBinding.ts +132 -10
- package/src/bindings/TLBaseBinding.ts +140 -3
- package/src/createPresenceStateDerivation.test.ts +158 -0
- package/src/createPresenceStateDerivation.ts +71 -2
- package/src/createTLSchema.test.ts +181 -0
- package/src/createTLSchema.ts +164 -7
- package/src/index.ts +32 -0
- package/src/misc/TLColor.ts +50 -6
- package/src/misc/TLCursor.ts +110 -8
- package/src/misc/TLHandle.ts +86 -6
- package/src/misc/TLOpacity.ts +51 -2
- package/src/misc/TLRichText.ts +56 -3
- package/src/misc/TLScribble.ts +105 -5
- package/src/misc/geometry-types.ts +30 -2
- package/src/misc/id-validator.test.ts +50 -0
- package/src/misc/id-validator.ts +20 -1
- package/src/records/TLAsset.test.ts +234 -0
- package/src/records/TLAsset.ts +165 -8
- package/src/records/TLBinding.test.ts +22 -0
- package/src/records/TLBinding.ts +277 -11
- package/src/records/TLCamera.test.ts +19 -0
- package/src/records/TLCamera.ts +118 -7
- package/src/records/TLDocument.test.ts +35 -0
- package/src/records/TLDocument.ts +148 -8
- package/src/records/TLInstance.test.ts +201 -0
- package/src/records/TLInstance.ts +117 -9
- package/src/records/TLPage.test.ts +110 -0
- package/src/records/TLPage.ts +106 -8
- package/src/records/TLPageState.test.ts +228 -0
- package/src/records/TLPageState.ts +88 -7
- package/src/records/TLPointer.test.ts +63 -0
- package/src/records/TLPointer.ts +105 -7
- package/src/records/TLPresence.test.ts +190 -0
- package/src/records/TLPresence.ts +99 -5
- package/src/records/TLRecord.test.ts +70 -0
- package/src/records/TLRecord.ts +43 -1
- package/src/records/TLShape.test.ts +232 -0
- package/src/records/TLShape.ts +289 -12
- package/src/recordsWithProps.test.ts +188 -0
- package/src/recordsWithProps.ts +131 -2
- package/src/shapes/ShapeWithCrop.test.ts +18 -0
- package/src/shapes/ShapeWithCrop.ts +64 -2
- package/src/shapes/TLArrowShape.test.ts +505 -0
- package/src/shapes/TLArrowShape.ts +188 -10
- package/src/shapes/TLBaseShape.test.ts +142 -0
- package/src/shapes/TLBaseShape.ts +103 -4
- package/src/shapes/TLBookmarkShape.test.ts +122 -0
- package/src/shapes/TLBookmarkShape.ts +58 -4
- package/src/shapes/TLDrawShape.test.ts +177 -0
- package/src/shapes/TLDrawShape.ts +97 -6
- package/src/shapes/TLEmbedShape.test.ts +286 -0
- package/src/shapes/TLEmbedShape.ts +57 -14
- package/src/shapes/TLFrameShape.test.ts +71 -0
- package/src/shapes/TLFrameShape.ts +59 -4
- package/src/shapes/TLGeoShape.test.ts +247 -0
- package/src/shapes/TLGeoShape.ts +103 -7
- package/src/shapes/TLGroupShape.test.ts +59 -0
- package/src/shapes/TLGroupShape.ts +52 -4
- package/src/shapes/TLHighlightShape.test.ts +325 -0
- package/src/shapes/TLHighlightShape.ts +79 -4
- package/src/shapes/TLImageShape.test.ts +534 -0
- package/src/shapes/TLImageShape.ts +105 -5
- package/src/shapes/TLLineShape.test.ts +269 -0
- package/src/shapes/TLLineShape.ts +128 -8
- package/src/shapes/TLNoteShape.test.ts +1568 -0
- package/src/shapes/TLNoteShape.ts +97 -4
- package/src/shapes/TLTextShape.test.ts +407 -0
- package/src/shapes/TLTextShape.ts +94 -4
- package/src/shapes/TLVideoShape.test.ts +112 -0
- package/src/shapes/TLVideoShape.ts +99 -4
- package/src/store-migrations.test.ts +88 -0
- package/src/store-migrations.ts +47 -1
- package/src/styles/TLColorStyle.test.ts +439 -0
- package/src/styles/TLColorStyle.ts +228 -10
- package/src/styles/TLDashStyle.ts +54 -2
- package/src/styles/TLFillStyle.ts +54 -2
- package/src/styles/TLFontStyle.ts +72 -3
- package/src/styles/TLHorizontalAlignStyle.ts +55 -2
- package/src/styles/TLSizeStyle.ts +54 -2
- package/src/styles/TLTextAlignStyle.ts +52 -2
- package/src/styles/TLVerticalAlignStyle.ts +52 -2
- package/src/translations/translations.test.ts +378 -35
- package/src/translations/translations.ts +157 -10
- package/src/util-types.ts +51 -1
|
@@ -18,13 +18,45 @@ import { DefaultSizeStyle, TLDefaultSizeStyle } from '../styles/TLSizeStyle'
|
|
|
18
18
|
import { TLBaseShape } from './TLBaseShape'
|
|
19
19
|
|
|
20
20
|
const arrowKinds = ['arc', 'elbow'] as const
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Style property for arrow shape kind, determining how the arrow is drawn.
|
|
23
|
+
*
|
|
24
|
+
* Arrows can be drawn as arcs (curved) or elbows (angled with straight segments).
|
|
25
|
+
* This affects the visual appearance and behavior of arrow shapes.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* // Create an arrow with arc style (curved)
|
|
30
|
+
* const arcArrow: TLArrowShape = {
|
|
31
|
+
* // ... other properties
|
|
32
|
+
* props: {
|
|
33
|
+
* kind: 'arc',
|
|
34
|
+
* // ... other props
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
*
|
|
38
|
+
* // Create an arrow with elbow style (angled)
|
|
39
|
+
* const elbowArrow: TLArrowShape = {
|
|
40
|
+
* // ... other properties
|
|
41
|
+
* props: {
|
|
42
|
+
* kind: 'elbow',
|
|
43
|
+
* // ... other props
|
|
44
|
+
* }
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
22
50
|
export const ArrowShapeKindStyle = StyleProp.defineEnum('tldraw:arrowKind', {
|
|
23
51
|
defaultValue: 'arc',
|
|
24
52
|
values: arrowKinds,
|
|
25
53
|
})
|
|
26
54
|
|
|
27
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* The type representing arrow shape kinds.
|
|
57
|
+
*
|
|
58
|
+
* @public
|
|
59
|
+
*/
|
|
28
60
|
export type TLArrowShapeKind = T.TypeOf<typeof ArrowShapeKindStyle>
|
|
29
61
|
|
|
30
62
|
const arrowheadTypes = [
|
|
@@ -39,22 +71,96 @@ const arrowheadTypes = [
|
|
|
39
71
|
'none',
|
|
40
72
|
] as const
|
|
41
73
|
|
|
42
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Style property for the arrowhead at the start of an arrow.
|
|
76
|
+
*
|
|
77
|
+
* Defines the visual style of the arrowhead at the beginning of the arrow path.
|
|
78
|
+
* Can be one of several predefined styles or none for no arrowhead.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* // Arrow with no start arrowhead but triangle end arrowhead
|
|
83
|
+
* const arrow: TLArrowShape = {
|
|
84
|
+
* // ... other properties
|
|
85
|
+
* props: {
|
|
86
|
+
* arrowheadStart: 'none',
|
|
87
|
+
* arrowheadEnd: 'triangle',
|
|
88
|
+
* // ... other props
|
|
89
|
+
* }
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @public
|
|
94
|
+
*/
|
|
43
95
|
export const ArrowShapeArrowheadStartStyle = StyleProp.defineEnum('tldraw:arrowheadStart', {
|
|
44
96
|
defaultValue: 'none',
|
|
45
97
|
values: arrowheadTypes,
|
|
46
98
|
})
|
|
47
99
|
|
|
48
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* Style property for the arrowhead at the end of an arrow.
|
|
102
|
+
*
|
|
103
|
+
* Defines the visual style of the arrowhead at the end of the arrow path.
|
|
104
|
+
* Defaults to 'arrow' style, giving arrows their characteristic pointed appearance.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* // Arrow with different start and end arrowheads
|
|
109
|
+
* const doubleArrow: TLArrowShape = {
|
|
110
|
+
* // ... other properties
|
|
111
|
+
* props: {
|
|
112
|
+
* arrowheadStart: 'triangle',
|
|
113
|
+
* arrowheadEnd: 'diamond',
|
|
114
|
+
* // ... other props
|
|
115
|
+
* }
|
|
116
|
+
* }
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* @public
|
|
120
|
+
*/
|
|
49
121
|
export const ArrowShapeArrowheadEndStyle = StyleProp.defineEnum('tldraw:arrowheadEnd', {
|
|
50
122
|
defaultValue: 'arrow',
|
|
51
123
|
values: arrowheadTypes,
|
|
52
124
|
})
|
|
53
125
|
|
|
54
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* The type representing arrowhead styles for both start and end of arrows.
|
|
128
|
+
*
|
|
129
|
+
* @public
|
|
130
|
+
*/
|
|
55
131
|
export type TLArrowShapeArrowheadStyle = T.TypeOf<typeof ArrowShapeArrowheadStartStyle>
|
|
56
132
|
|
|
57
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* Properties specific to arrow shapes.
|
|
135
|
+
*
|
|
136
|
+
* Defines all the configurable aspects of an arrow shape, including visual styling,
|
|
137
|
+
* geometry, text labeling, and positioning. Arrows can connect two points and
|
|
138
|
+
* optionally display text labels.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* const arrowProps: TLArrowShapeProps = {
|
|
143
|
+
* kind: 'arc',
|
|
144
|
+
* labelColor: 'black',
|
|
145
|
+
* color: 'blue',
|
|
146
|
+
* fill: 'none',
|
|
147
|
+
* dash: 'solid',
|
|
148
|
+
* size: 'm',
|
|
149
|
+
* arrowheadStart: 'none',
|
|
150
|
+
* arrowheadEnd: 'arrow',
|
|
151
|
+
* font: 'draw',
|
|
152
|
+
* start: { x: 0, y: 0 },
|
|
153
|
+
* end: { x: 100, y: 100 },
|
|
154
|
+
* bend: 0.2,
|
|
155
|
+
* richText: toRichText('Label'),
|
|
156
|
+
* labelPosition: 0.5,
|
|
157
|
+
* scale: 1,
|
|
158
|
+
* elbowMidPoint: 0.5
|
|
159
|
+
* }
|
|
160
|
+
* ```
|
|
161
|
+
*
|
|
162
|
+
* @public
|
|
163
|
+
*/
|
|
58
164
|
export interface TLArrowShapeProps {
|
|
59
165
|
kind: TLArrowShapeKind
|
|
60
166
|
labelColor: TLDefaultColorStyle
|
|
@@ -74,10 +180,59 @@ export interface TLArrowShapeProps {
|
|
|
74
180
|
elbowMidPoint: number
|
|
75
181
|
}
|
|
76
182
|
|
|
77
|
-
/**
|
|
183
|
+
/**
|
|
184
|
+
* A complete arrow shape record.
|
|
185
|
+
*
|
|
186
|
+
* Combines the base shape interface with arrow-specific properties to create
|
|
187
|
+
* a full arrow shape that can be stored and manipulated in the editor.
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```ts
|
|
191
|
+
* const arrowShape: TLArrowShape = {
|
|
192
|
+
* id: 'shape:arrow123',
|
|
193
|
+
* typeName: 'shape',
|
|
194
|
+
* type: 'arrow',
|
|
195
|
+
* x: 100,
|
|
196
|
+
* y: 200,
|
|
197
|
+
* rotation: 0,
|
|
198
|
+
* index: 'a1',
|
|
199
|
+
* parentId: 'page:main',
|
|
200
|
+
* isLocked: false,
|
|
201
|
+
* opacity: 1,
|
|
202
|
+
* props: {
|
|
203
|
+
* kind: 'arc',
|
|
204
|
+
* start: { x: 0, y: 0 },
|
|
205
|
+
* end: { x: 150, y: 100 },
|
|
206
|
+
* // ... other props
|
|
207
|
+
* },
|
|
208
|
+
* meta: {}
|
|
209
|
+
* }
|
|
210
|
+
* ```
|
|
211
|
+
*
|
|
212
|
+
* @public
|
|
213
|
+
*/
|
|
78
214
|
export type TLArrowShape = TLBaseShape<'arrow', TLArrowShapeProps>
|
|
79
215
|
|
|
80
|
-
/**
|
|
216
|
+
/**
|
|
217
|
+
* Validation configuration for arrow shape properties.
|
|
218
|
+
*
|
|
219
|
+
* Defines the validators for each property of an arrow shape, ensuring that
|
|
220
|
+
* arrow shape data is valid and conforms to the expected types and constraints.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```ts
|
|
224
|
+
* // The validators ensure proper typing and validation
|
|
225
|
+
* const validator = T.object(arrowShapeProps)
|
|
226
|
+
* const validArrowProps = validator.validate({
|
|
227
|
+
* kind: 'arc',
|
|
228
|
+
* start: { x: 0, y: 0 },
|
|
229
|
+
* end: { x: 100, y: 50 },
|
|
230
|
+
* // ... other required properties
|
|
231
|
+
* })
|
|
232
|
+
* ```
|
|
233
|
+
*
|
|
234
|
+
* @public
|
|
235
|
+
*/
|
|
81
236
|
export const arrowShapeProps: RecordProps<TLArrowShape> = {
|
|
82
237
|
kind: ArrowShapeKindStyle,
|
|
83
238
|
labelColor: DefaultLabelColorStyle,
|
|
@@ -97,7 +252,22 @@ export const arrowShapeProps: RecordProps<TLArrowShape> = {
|
|
|
97
252
|
elbowMidPoint: T.number,
|
|
98
253
|
}
|
|
99
254
|
|
|
100
|
-
/**
|
|
255
|
+
/**
|
|
256
|
+
* Migration version identifiers for arrow shape properties.
|
|
257
|
+
*
|
|
258
|
+
* These track the evolution of the arrow shape schema over time, with each
|
|
259
|
+
* version representing a specific change to the arrow shape structure or properties.
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```ts
|
|
263
|
+
* // Used internally for migration system
|
|
264
|
+
* if (version < arrowShapeVersions.AddLabelColor) {
|
|
265
|
+
* // Apply label color migration
|
|
266
|
+
* }
|
|
267
|
+
* ```
|
|
268
|
+
*
|
|
269
|
+
* @public
|
|
270
|
+
*/
|
|
101
271
|
export const arrowShapeVersions = createShapePropsMigrationIds('arrow', {
|
|
102
272
|
AddLabelColor: 1,
|
|
103
273
|
AddIsPrecise: 2,
|
|
@@ -112,7 +282,15 @@ function propsMigration(migration: TLPropsMigration) {
|
|
|
112
282
|
return createPropsMigration<TLArrowShape>('shape', 'arrow', migration)
|
|
113
283
|
}
|
|
114
284
|
|
|
115
|
-
/**
|
|
285
|
+
/**
|
|
286
|
+
* Complete migration sequence for arrow shapes.
|
|
287
|
+
*
|
|
288
|
+
* Defines all the migrations needed to transform arrow shape data from older
|
|
289
|
+
* versions to the current version. Each migration handles a specific schema change,
|
|
290
|
+
* ensuring backward compatibility and smooth data evolution.
|
|
291
|
+
*
|
|
292
|
+
* @public
|
|
293
|
+
*/
|
|
116
294
|
export const arrowShapeMigrations = createMigrationSequence({
|
|
117
295
|
sequenceId: 'com.tldraw.shape.arrow',
|
|
118
296
|
retroactive: false,
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { T } from '@tldraw/validate'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { createShapeValidator, parentIdValidator, shapeIdValidator } from './TLBaseShape'
|
|
4
|
+
|
|
5
|
+
describe('TLBaseShape', () => {
|
|
6
|
+
describe('parentIdValidator', () => {
|
|
7
|
+
it('should accept valid page and shape parent IDs', () => {
|
|
8
|
+
expect(() => parentIdValidator.validate('page:main')).not.toThrow()
|
|
9
|
+
expect(() => parentIdValidator.validate('shape:frame1')).not.toThrow()
|
|
10
|
+
expect(parentIdValidator.validate('page:main')).toBe('page:main')
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('should reject invalid parent ID prefixes', () => {
|
|
14
|
+
expect(() => parentIdValidator.validate('invalid:123')).toThrow(
|
|
15
|
+
'Parent ID must start with "page:" or "shape:"'
|
|
16
|
+
)
|
|
17
|
+
expect(() => parentIdValidator.validate('asset:123')).toThrow(
|
|
18
|
+
'Parent ID must start with "page:" or "shape:"'
|
|
19
|
+
)
|
|
20
|
+
expect(() => parentIdValidator.validate('page-main')).toThrow(
|
|
21
|
+
'Parent ID must start with "page:" or "shape:"'
|
|
22
|
+
)
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe('shapeIdValidator', () => {
|
|
27
|
+
it('should accept valid shape IDs', () => {
|
|
28
|
+
expect(() => shapeIdValidator.validate('shape:abc123')).not.toThrow()
|
|
29
|
+
expect(shapeIdValidator.validate('shape:test')).toBe('shape:test')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should reject non-shape IDs', () => {
|
|
33
|
+
expect(() => shapeIdValidator.validate('page:123')).toThrow(
|
|
34
|
+
'shape ID must start with "shape:"'
|
|
35
|
+
)
|
|
36
|
+
expect(() => shapeIdValidator.validate('asset:123')).toThrow(
|
|
37
|
+
'shape ID must start with "shape:"'
|
|
38
|
+
)
|
|
39
|
+
expect(() => shapeIdValidator.validate('shape-abc123')).toThrow(
|
|
40
|
+
'shape ID must start with "shape:"'
|
|
41
|
+
)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('createShapeValidator', () => {
|
|
46
|
+
it('should create validator for shape with no custom props', () => {
|
|
47
|
+
const validator = createShapeValidator('simple')
|
|
48
|
+
|
|
49
|
+
const validShape = {
|
|
50
|
+
id: 'shape:simple123',
|
|
51
|
+
typeName: 'shape',
|
|
52
|
+
type: 'simple',
|
|
53
|
+
x: 100,
|
|
54
|
+
y: 200,
|
|
55
|
+
rotation: 0.5,
|
|
56
|
+
index: 'a1',
|
|
57
|
+
parentId: 'page:main',
|
|
58
|
+
isLocked: false,
|
|
59
|
+
opacity: 0.8,
|
|
60
|
+
props: {},
|
|
61
|
+
meta: {},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
expect(() => validator.validate(validShape)).not.toThrow()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('should create validator for shape with custom props', () => {
|
|
68
|
+
const validator = createShapeValidator('custom', {
|
|
69
|
+
width: T.number,
|
|
70
|
+
height: T.number,
|
|
71
|
+
color: T.string,
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const validShape = {
|
|
75
|
+
id: 'shape:custom456',
|
|
76
|
+
typeName: 'shape',
|
|
77
|
+
type: 'custom',
|
|
78
|
+
x: 50,
|
|
79
|
+
y: 75,
|
|
80
|
+
rotation: 1.0,
|
|
81
|
+
index: 'a2',
|
|
82
|
+
parentId: 'shape:frame1',
|
|
83
|
+
isLocked: true,
|
|
84
|
+
opacity: 0.5,
|
|
85
|
+
props: {
|
|
86
|
+
width: 150,
|
|
87
|
+
height: 100,
|
|
88
|
+
color: 'blue',
|
|
89
|
+
},
|
|
90
|
+
meta: {},
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
expect(() => validator.validate(validShape)).not.toThrow()
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('should reject shapes with wrong type', () => {
|
|
97
|
+
const validator = createShapeValidator('test')
|
|
98
|
+
|
|
99
|
+
const invalidShape = {
|
|
100
|
+
id: 'shape:test',
|
|
101
|
+
typeName: 'shape',
|
|
102
|
+
type: 'wrong',
|
|
103
|
+
x: 0,
|
|
104
|
+
y: 0,
|
|
105
|
+
rotation: 0,
|
|
106
|
+
index: 'a1',
|
|
107
|
+
parentId: 'page:main',
|
|
108
|
+
isLocked: false,
|
|
109
|
+
opacity: 1,
|
|
110
|
+
props: {},
|
|
111
|
+
meta: {},
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
expect(() => validator.validate(invalidShape)).toThrow()
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('should reject shapes with invalid custom props', () => {
|
|
118
|
+
const validator = createShapeValidator('custom', {
|
|
119
|
+
width: T.number,
|
|
120
|
+
height: T.number,
|
|
121
|
+
color: T.string,
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const invalidShape = {
|
|
125
|
+
id: 'shape:custom123',
|
|
126
|
+
typeName: 'shape',
|
|
127
|
+
type: 'custom',
|
|
128
|
+
x: 0,
|
|
129
|
+
y: 0,
|
|
130
|
+
rotation: 0,
|
|
131
|
+
index: 'a1',
|
|
132
|
+
parentId: 'page:main',
|
|
133
|
+
isLocked: false,
|
|
134
|
+
opacity: 1,
|
|
135
|
+
meta: {},
|
|
136
|
+
props: { width: '100' }, // Wrong type - should be number
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
expect(() => validator.validate(invalidShape)).toThrow()
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
})
|
|
@@ -5,7 +5,39 @@ import { TLOpacityType, opacityValidator } from '../misc/TLOpacity'
|
|
|
5
5
|
import { idValidator } from '../misc/id-validator'
|
|
6
6
|
import { TLParentId, TLShapeId } from '../records/TLShape'
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Base interface for all shapes in tldraw.
|
|
10
|
+
*
|
|
11
|
+
* This interface defines the common properties that all shapes share, regardless of their
|
|
12
|
+
* specific type. Every shape extends this base with additional type-specific properties.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* // Define a custom shape type
|
|
17
|
+
* interface MyCustomShape extends TLBaseShape<'custom', { size: number; color: string }> {}
|
|
18
|
+
*
|
|
19
|
+
* // Create a shape instance
|
|
20
|
+
* const myShape: MyCustomShape = {
|
|
21
|
+
* id: 'shape:abc123',
|
|
22
|
+
* typeName: 'shape',
|
|
23
|
+
* type: 'custom',
|
|
24
|
+
* x: 100,
|
|
25
|
+
* y: 200,
|
|
26
|
+
* rotation: 0,
|
|
27
|
+
* index: 'a1',
|
|
28
|
+
* parentId: 'page:main',
|
|
29
|
+
* isLocked: false,
|
|
30
|
+
* opacity: 1,
|
|
31
|
+
* props: {
|
|
32
|
+
* size: 50,
|
|
33
|
+
* color: 'blue'
|
|
34
|
+
* },
|
|
35
|
+
* meta: {}
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
9
41
|
export interface TLBaseShape<Type extends string, Props extends object>
|
|
10
42
|
extends BaseRecord<'shape', TLShapeId> {
|
|
11
43
|
type: Type
|
|
@@ -20,7 +52,24 @@ export interface TLBaseShape<Type extends string, Props extends object>
|
|
|
20
52
|
meta: JsonObject
|
|
21
53
|
}
|
|
22
54
|
|
|
23
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Validator for parent IDs, ensuring they follow the correct format.
|
|
57
|
+
*
|
|
58
|
+
* Parent IDs must start with either "page:" (for shapes directly on a page)
|
|
59
|
+
* or "shape:" (for shapes inside other shapes like frames or groups).
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* // Valid parent IDs
|
|
64
|
+
* const pageParent = parentIdValidator.validate('page:main') // ✓
|
|
65
|
+
* const shapeParent = parentIdValidator.validate('shape:frame1') // ✓
|
|
66
|
+
*
|
|
67
|
+
* // Invalid parent ID (throws error)
|
|
68
|
+
* const invalid = parentIdValidator.validate('invalid:123') // ✗
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
24
73
|
export const parentIdValidator = T.string.refine((id) => {
|
|
25
74
|
if (!id.startsWith('page:') && !id.startsWith('shape:')) {
|
|
26
75
|
throw new Error('Parent ID must start with "page:" or "shape:"')
|
|
@@ -28,10 +77,60 @@ export const parentIdValidator = T.string.refine((id) => {
|
|
|
28
77
|
return id as TLParentId
|
|
29
78
|
})
|
|
30
79
|
|
|
31
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* Validator for shape IDs, ensuring they follow the "shape:" format.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* const validId = shapeIdValidator.validate('shape:abc123') // ✓
|
|
86
|
+
* const invalidId = shapeIdValidator.validate('page:abc123') // ✗ throws error
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @public
|
|
90
|
+
*/
|
|
32
91
|
export const shapeIdValidator = idValidator<TLShapeId>('shape')
|
|
33
92
|
|
|
34
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* Creates a validator for a specific shape type.
|
|
95
|
+
*
|
|
96
|
+
* This function generates a complete validator that can validate shape records
|
|
97
|
+
* of the specified type, including both the base shape properties and any
|
|
98
|
+
* custom properties and metadata specific to that shape type.
|
|
99
|
+
*
|
|
100
|
+
* @param type - The string literal type for this shape (e.g., 'geo', 'arrow')
|
|
101
|
+
* @param props - Optional validator configuration for shape-specific properties
|
|
102
|
+
* @param meta - Optional validator configuration for shape-specific metadata
|
|
103
|
+
* @returns A validator that can validate complete shape records of the specified type
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* // Create a validator for a custom shape type
|
|
108
|
+
* const customShapeValidator = createShapeValidator('custom', {
|
|
109
|
+
* width: T.number,
|
|
110
|
+
* height: T.number,
|
|
111
|
+
* color: T.string
|
|
112
|
+
* })
|
|
113
|
+
*
|
|
114
|
+
* // Use the validator to validate shape data
|
|
115
|
+
* const shapeData = {
|
|
116
|
+
* id: 'shape:abc123',
|
|
117
|
+
* typeName: 'shape',
|
|
118
|
+
* type: 'custom',
|
|
119
|
+
* x: 100,
|
|
120
|
+
* y: 200,
|
|
121
|
+
* // ... other base properties
|
|
122
|
+
* props: {
|
|
123
|
+
* width: 150,
|
|
124
|
+
* height: 100,
|
|
125
|
+
* color: 'red'
|
|
126
|
+
* }
|
|
127
|
+
* }
|
|
128
|
+
*
|
|
129
|
+
* const validatedShape = customShapeValidator.validate(shapeData)
|
|
130
|
+
* ```
|
|
131
|
+
*
|
|
132
|
+
* @public
|
|
133
|
+
*/
|
|
35
134
|
export function createShapeValidator<
|
|
36
135
|
Type extends string,
|
|
37
136
|
Props extends JsonObject,
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { getTestMigration } from '../__tests__/migrationTestUtils'
|
|
3
|
+
import { bookmarkShapeProps, bookmarkShapeVersions } from './TLBookmarkShape'
|
|
4
|
+
|
|
5
|
+
describe('TLBookmarkShape', () => {
|
|
6
|
+
describe('bookmarkShapeProps validation', () => {
|
|
7
|
+
it('should validate width as nonZeroNumber', () => {
|
|
8
|
+
// Valid non-zero positive numbers
|
|
9
|
+
expect(() => bookmarkShapeProps.w.validate(0.1)).not.toThrow()
|
|
10
|
+
expect(() => bookmarkShapeProps.w.validate(100)).not.toThrow()
|
|
11
|
+
|
|
12
|
+
// Invalid: zero and negative
|
|
13
|
+
expect(() => bookmarkShapeProps.w.validate(0)).toThrow()
|
|
14
|
+
expect(() => bookmarkShapeProps.w.validate(-1)).toThrow()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('should validate height as nonZeroNumber', () => {
|
|
18
|
+
// Valid non-zero positive numbers
|
|
19
|
+
expect(() => bookmarkShapeProps.h.validate(0.1)).not.toThrow()
|
|
20
|
+
expect(() => bookmarkShapeProps.h.validate(100)).not.toThrow()
|
|
21
|
+
|
|
22
|
+
// Invalid: zero and negative
|
|
23
|
+
expect(() => bookmarkShapeProps.h.validate(0)).toThrow()
|
|
24
|
+
expect(() => bookmarkShapeProps.h.validate(-1)).toThrow()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('should validate assetId as nullable asset ID', () => {
|
|
28
|
+
// Valid asset IDs
|
|
29
|
+
expect(() => bookmarkShapeProps.assetId.validate(null)).not.toThrow()
|
|
30
|
+
expect(() => bookmarkShapeProps.assetId.validate('asset:bookmark123')).not.toThrow()
|
|
31
|
+
|
|
32
|
+
// Invalid asset IDs
|
|
33
|
+
expect(() => bookmarkShapeProps.assetId.validate('shape:notasset')).toThrow()
|
|
34
|
+
expect(() => bookmarkShapeProps.assetId.validate('bookmark123')).toThrow()
|
|
35
|
+
expect(() => bookmarkShapeProps.assetId.validate(undefined)).toThrow()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should validate url as linkUrl', () => {
|
|
39
|
+
// Valid URLs
|
|
40
|
+
expect(() => bookmarkShapeProps.url.validate('')).not.toThrow()
|
|
41
|
+
expect(() => bookmarkShapeProps.url.validate('https://example.com')).not.toThrow()
|
|
42
|
+
|
|
43
|
+
// Invalid URLs
|
|
44
|
+
expect(() => bookmarkShapeProps.url.validate('not-a-url')).toThrow()
|
|
45
|
+
expect(() => bookmarkShapeProps.url.validate('javascript:alert("xss")')).toThrow()
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe('NullAssetId migration', () => {
|
|
50
|
+
const { up, down } = getTestMigration(bookmarkShapeVersions.NullAssetId)
|
|
51
|
+
|
|
52
|
+
it('should add assetId as null when undefined', () => {
|
|
53
|
+
const oldRecord = {
|
|
54
|
+
props: {
|
|
55
|
+
w: 300,
|
|
56
|
+
h: 320,
|
|
57
|
+
url: 'https://example.com',
|
|
58
|
+
// assetId undefined
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const result = up(oldRecord)
|
|
63
|
+
expect(result.props.assetId).toBeNull()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should preserve existing assetId when present', () => {
|
|
67
|
+
const oldRecord = {
|
|
68
|
+
props: {
|
|
69
|
+
w: 300,
|
|
70
|
+
h: 320,
|
|
71
|
+
url: 'https://example.com',
|
|
72
|
+
assetId: 'asset:existing123',
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const result = up(oldRecord)
|
|
77
|
+
expect(result.props.assetId).toBe('asset:existing123')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should throw on retired down migration', () => {
|
|
81
|
+
expect(() => down({})).toThrow()
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('MakeUrlsValid migration', () => {
|
|
86
|
+
const { up, down } = getTestMigration(bookmarkShapeVersions.MakeUrlsValid)
|
|
87
|
+
|
|
88
|
+
it('should set invalid URLs to empty string', () => {
|
|
89
|
+
const oldRecord = {
|
|
90
|
+
props: {
|
|
91
|
+
w: 300,
|
|
92
|
+
h: 320,
|
|
93
|
+
assetId: null,
|
|
94
|
+
url: 'not-a-valid-url',
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const result = up(oldRecord)
|
|
99
|
+
expect(result.props.url).toBe('')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should preserve valid URLs', () => {
|
|
103
|
+
const oldRecord = {
|
|
104
|
+
props: {
|
|
105
|
+
w: 300,
|
|
106
|
+
h: 320,
|
|
107
|
+
assetId: null,
|
|
108
|
+
url: 'https://example.com',
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const result = up(oldRecord)
|
|
113
|
+
expect(result.props.url).toBe('https://example.com')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('should be noop for down migration', () => {
|
|
117
|
+
const newRecord = { props: { url: 'https://example.com' } }
|
|
118
|
+
const result = down(newRecord)
|
|
119
|
+
expect(result).toEqual(newRecord)
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
})
|