@tldraw/tlschema 4.3.0-next.705b6ec94f43 → 4.3.0-next.7f179bd04d6c

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.
@@ -79,12 +79,51 @@ export type TLDefaultShape =
79
79
  */
80
80
  export type TLUnknownShape = TLBaseShape<string, object>
81
81
 
82
+ /** @public */
83
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
84
+ export interface TLGlobalShapePropsMap {}
85
+
86
+ /** @public */
87
+ // prettier-ignore
88
+ export type TLIndexedShapes = {
89
+ // We iterate over a union of augmented keys and default shape types.
90
+ // This allows us to include (or conditionally exclude or override) the default shapes in one go.
91
+ //
92
+ // In the `as` clause we are filtering out disabled shapes.
93
+ [K in keyof TLGlobalShapePropsMap | TLDefaultShape['type'] as K extends TLDefaultShape['type']
94
+ ? // core shapes are always available and cannot be overridden so we just include them
95
+ K extends 'group'
96
+ ? K
97
+ : K extends keyof TLGlobalShapePropsMap
98
+ ? // if it extends a nullish value the user has disabled this shape type so we filter it out with never
99
+ TLGlobalShapePropsMap[K] extends null | undefined
100
+ ? never
101
+ : K
102
+ : K
103
+ : K]: K extends 'group'
104
+ ? // core shapes are always available and cannot be overridden so we just include them
105
+ Extract<TLDefaultShape, { type: K }>
106
+ : K extends TLDefaultShape['type']
107
+ ? // if it's a default shape type we need to check if it's been overridden
108
+ K extends keyof TLGlobalShapePropsMap
109
+ ? // if it has been overriden then use the custom shape definition
110
+ TLBaseShape<K, TLGlobalShapePropsMap[K]>
111
+ : // if it has not been overriden then reuse existing type aliases for better type display
112
+ Extract<TLDefaultShape, { type: K }>
113
+ : // use the custom shape definition
114
+ TLBaseShape<K, TLGlobalShapePropsMap[K & keyof TLGlobalShapePropsMap]>
115
+ }
116
+
82
117
  /**
83
- * The set of all shapes that are available in the editor, including unknown shapes.
118
+ * The set of all shapes that are available in the editor.
84
119
  *
85
120
  * This is the primary shape type used throughout tldraw. It includes both the
86
121
  * built-in default shapes and any custom shapes that might be added.
87
122
  *
123
+ * You can use this type without a type argument to work with any shape, or pass
124
+ * a specific shape type string (e.g., `'geo'`, `'arrow'`, `'text'`) to narrow
125
+ * down to that specific shape type.
126
+ *
88
127
  * @example
89
128
  * ```ts
90
129
  * // Work with any shape in the editor
@@ -95,11 +134,16 @@ export type TLUnknownShape = TLBaseShape<string, object>
95
134
  * y: shape.y + deltaY
96
135
  * }
97
136
  * }
137
+ *
138
+ * // Narrow to a specific shape type by passing the type as a generic argument
139
+ * function getArrowLabel(shape: TLShape<'arrow'>): string {
140
+ * return shape.props.text // TypeScript knows this is a TLArrowShape
141
+ * }
98
142
  * ```
99
143
  *
100
144
  * @public
101
145
  */
102
- export type TLShape = TLDefaultShape | TLUnknownShape
146
+ export type TLShape<K extends keyof TLIndexedShapes = keyof TLIndexedShapes> = TLIndexedShapes[K]
103
147
 
104
148
  /**
105
149
  * A partial version of a shape, useful for updates and patches.
@@ -139,6 +183,57 @@ export type TLShapePartial<T extends TLShape = TLShape> = T extends T
139
183
  } & Partial<Omit<T, 'type' | 'id' | 'props' | 'meta'>>
140
184
  : never
141
185
 
186
+ /**
187
+ * A partial version of a shape, useful for creating shapes.
188
+ *
189
+ * This type represents a shape where all properties except `type` are optional.
190
+ * It's commonly used when creating shapes.
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * // Create a shape
195
+ * const shapeCreate: TLCreateShapePartial = {
196
+ * type: 'geo',
197
+ * x: 100,
198
+ * y: 200
199
+ * }
200
+ *
201
+ * // Create shape properties
202
+ * const propsCreate: TLCreateShapePartial<TLGeoShape> = {
203
+ * type: 'geo',
204
+ * props: {
205
+ * w: 150,
206
+ * h: 100
207
+ * }
208
+ * }
209
+ * ```
210
+ *
211
+ * @public
212
+ */
213
+ export type TLCreateShapePartial<T extends TLShape = TLShape> = T extends T
214
+ ? {
215
+ type: T['type']
216
+ props?: Partial<T['props']>
217
+ meta?: Partial<T['meta']>
218
+ } & Partial<Omit<T, 'type' | 'props' | 'meta'>>
219
+ : never
220
+
221
+ /**
222
+ * Extract a shape type by its props.
223
+ *
224
+ * This utility type takes a props object type and returns the corresponding shape type
225
+ * from the TLShape union whose props match the given type.
226
+ *
227
+ * @example
228
+ * ```ts
229
+ * type MyShape = ExtractShapeByProps<{ w: number; h: number }>
230
+ * // MyShape is now the type of shape(s) that have props with w and h as numbers
231
+ * ```
232
+ *
233
+ * @public
234
+ */
235
+ export type ExtractShapeByProps<P> = Extract<TLShape, { props: P }>
236
+
142
237
  /**
143
238
  * A unique identifier for a shape record.
144
239
  *
@@ -153,7 +248,7 @@ export type TLShapePartial<T extends TLShape = TLShape> = T extends T
153
248
  *
154
249
  * @public
155
250
  */
156
- export type TLShapeId = RecordId<TLUnknownShape>
251
+ export type TLShapeId = RecordId<TLShape>
157
252
 
158
253
  /**
159
254
  * The ID of a shape's parent, which can be either a page or another shape.
@@ -195,7 +290,7 @@ export const rootShapeVersions = createMigrationIds('com.tldraw.shape', {
195
290
  HoistOpacity: 2,
196
291
  AddMeta: 3,
197
292
  AddWhite: 4,
198
- } as const)
293
+ })
199
294
 
200
295
  /**
201
296
  * Migration sequence for the root shape record type.
@@ -469,7 +564,7 @@ export function createShapePropsMigrationIds<
469
564
  * @internal
470
565
  */
471
566
  export function createShapeRecordType(shapes: Record<string, SchemaPropsInfo>) {
472
- return createRecordType<TLShape>('shape', {
567
+ return createRecordType('shape', {
473
568
  scope: 'document',
474
569
  validator: T.model(
475
570
  'shape',
@@ -1,5 +1,5 @@
1
1
  import { VecModel } from '../misc/geometry-types'
2
- import { TLBaseShape } from './TLBaseShape'
2
+ import { ExtractShapeByProps } from '../records/TLShape'
3
3
 
4
4
  /**
5
5
  * Defines cropping parameters for shapes that support cropping.
@@ -71,4 +71,4 @@ export interface TLShapeCrop {
71
71
  *
72
72
  * @public
73
73
  */
74
- export type ShapeWithCrop = TLBaseShape<string, { w: number; h: number; crop: TLShapeCrop | null }>
74
+ export type ShapeWithCrop = ExtractShapeByProps<{ w: number; h: number; crop: TLShapeCrop | null }>
@@ -1,4 +1,3 @@
1
- import { BaseRecord } from '@tldraw/store'
2
1
  import { IndexKey, JsonObject } from '@tldraw/utils'
3
2
  import { T } from '@tldraw/validate'
4
3
  import { TLOpacityType, opacityValidator } from '../misc/TLOpacity'
@@ -9,18 +8,37 @@ import { TLParentId, TLShapeId } from '../records/TLShape'
9
8
  * Base interface for all shapes in tldraw.
10
9
  *
11
10
  * 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.
11
+ * specific type. Every default shape extends this base with additional type-specific properties.
12
+ *
13
+ * Custom shapes should be defined by augmenting the TLGlobalShapePropsMap type and getting the shape type from the TLShape type.
13
14
  *
14
15
  * @example
15
16
  * ```ts
16
- * // Define a custom shape type
17
- * interface MyCustomShape extends TLBaseShape<'custom', { size: number; color: string }> {}
17
+ * // Define a default shape type
18
+ * interface TLArrowShape extends TLBaseShape<'arrow', {
19
+ * kind: TLArrowShapeKind
20
+ * labelColor: TLDefaultColorStyle
21
+ * color: TLDefaultColorStyle
22
+ * fill: TLDefaultFillStyle
23
+ * dash: TLDefaultDashStyle
24
+ * size: TLDefaultSizeStyle
25
+ * arrowheadStart: TLArrowShapeArrowheadStyle
26
+ * arrowheadEnd: TLArrowShapeArrowheadStyle
27
+ * font: TLDefaultFontStyle
28
+ * start: VecModel
29
+ * end: VecModel
30
+ * bend: number
31
+ * richText: TLRichText
32
+ * labelPosition: number
33
+ * scale: number
34
+ * elbowMidPoint: number
35
+ * }> {}
18
36
  *
19
37
  * // Create a shape instance
20
- * const myShape: MyCustomShape = {
38
+ * const arrowShape: TLArrowShape = {
21
39
  * id: 'shape:abc123',
22
40
  * typeName: 'shape',
23
- * type: 'custom',
41
+ * type: 'arrow',
24
42
  * x: 100,
25
43
  * y: 200,
26
44
  * rotation: 0,
@@ -29,8 +47,10 @@ import { TLParentId, TLShapeId } from '../records/TLShape'
29
47
  * isLocked: false,
30
48
  * opacity: 1,
31
49
  * props: {
32
- * size: 50,
33
- * color: 'blue'
50
+ * kind: 'arc',
51
+ * start: { x: 0, y: 0 },
52
+ * end: { x: 100, y: 100 },
53
+ * // ... other props
34
54
  * },
35
55
  * meta: {}
36
56
  * }
@@ -38,8 +58,12 @@ import { TLParentId, TLShapeId } from '../records/TLShape'
38
58
  *
39
59
  * @public
40
60
  */
41
- export interface TLBaseShape<Type extends string, Props extends object>
42
- extends BaseRecord<'shape', TLShapeId> {
61
+ export interface TLBaseShape<Type extends string, Props extends object> {
62
+ // using real `extends BaseRecord<'shape', TLShapeId>` introduces a circularity in the types
63
+ // and for that reason those "base members" have to be declared manually here
64
+ readonly id: TLShapeId
65
+ readonly typeName: 'shape'
66
+
43
67
  type: Type
44
68
  x: number
45
69
  y: number
@@ -72,7 +72,8 @@ export const storeMigrations = createMigrationSequence({
72
72
  for (const [id, record] of objectMapEntries(store)) {
73
73
  if (
74
74
  record.typeName === 'shape' &&
75
- ((record as TLShape).type === 'icon' || (record as TLShape).type === 'code')
75
+ 'type' in record &&
76
+ (record.type === 'icon' || record.type === 'code')
76
77
  ) {
77
78
  delete store[id]
78
79
  }