@tldraw/tlschema 4.1.0-canary.8ac9e489017b → 4.1.0-canary.95d46c96eb30
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 +4412 -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.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 +4412 -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.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 +82 -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 -4
- 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
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Migration, createMigrationSequence } from '@tldraw/store'
|
|
2
|
+
import { T } from '@tldraw/validate'
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
4
|
+
import { SchemaPropsInfo } from './createTLSchema'
|
|
5
|
+
import {
|
|
6
|
+
TLPropsMigration,
|
|
7
|
+
TLPropsMigrations,
|
|
8
|
+
createPropsMigration,
|
|
9
|
+
processPropsMigrations,
|
|
10
|
+
} from './recordsWithProps'
|
|
11
|
+
|
|
12
|
+
describe('createPropsMigration', () => {
|
|
13
|
+
it('should create a store migration from props migration', () => {
|
|
14
|
+
const propsMigration: TLPropsMigration = {
|
|
15
|
+
id: 'com.test.shape.custom/1' as const,
|
|
16
|
+
up: (props: any) => ({ ...props, newProp: 'added' }),
|
|
17
|
+
down: (props: any) => {
|
|
18
|
+
const { newProp: _newProp, ...rest } = props
|
|
19
|
+
return rest
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const storeMigration = createPropsMigration('shape', 'custom', propsMigration)
|
|
24
|
+
|
|
25
|
+
expect(storeMigration.id).toBe('com.test.shape.custom/1')
|
|
26
|
+
expect(storeMigration.scope).toBe('record')
|
|
27
|
+
expect(typeof storeMigration.up).toBe('function')
|
|
28
|
+
expect(typeof storeMigration.down).toBe('function')
|
|
29
|
+
expect(typeof (storeMigration as any).filter).toBe('function')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should apply up migration to record props', () => {
|
|
33
|
+
const propsMigration: TLPropsMigration = {
|
|
34
|
+
id: 'com.test.shape.custom/1' as const,
|
|
35
|
+
up: (props: any) => ({ ...props, newProp: 'added' }),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const storeMigration = createPropsMigration('shape', 'custom', propsMigration)
|
|
39
|
+
|
|
40
|
+
const record = {
|
|
41
|
+
id: 'shape:test' as const,
|
|
42
|
+
typeName: 'shape' as const,
|
|
43
|
+
type: 'custom' as const,
|
|
44
|
+
props: { oldProp: 'value' },
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
storeMigration.up(record as any)
|
|
48
|
+
|
|
49
|
+
expect(record.props).toEqual({
|
|
50
|
+
oldProp: 'value',
|
|
51
|
+
newProp: 'added',
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('should apply down migration to record props', () => {
|
|
56
|
+
const propsMigration: TLPropsMigration = {
|
|
57
|
+
id: 'com.test.shape.custom/1' as const,
|
|
58
|
+
up: (props: any) => ({ ...props, newProp: 'added' }),
|
|
59
|
+
down: (props: any) => {
|
|
60
|
+
const { newProp: _newProp, ...rest } = props
|
|
61
|
+
return rest
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const storeMigration = createPropsMigration('shape', 'custom', propsMigration)
|
|
66
|
+
|
|
67
|
+
const record = {
|
|
68
|
+
id: 'shape:test' as const,
|
|
69
|
+
typeName: 'shape' as const,
|
|
70
|
+
type: 'custom' as const,
|
|
71
|
+
props: { oldProp: 'value', newProp: 'added' },
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (storeMigration.down) {
|
|
75
|
+
storeMigration.down(record as any)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
expect(record.props).toEqual({ oldProp: 'value' })
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('processPropsMigrations', () => {
|
|
83
|
+
it('should throw error for mismatched sequenceId', () => {
|
|
84
|
+
const wrongSequenceId = createMigrationSequence({
|
|
85
|
+
sequenceId: 'com.wrong.shape.custom', // Wrong sequence ID
|
|
86
|
+
retroactive: true,
|
|
87
|
+
sequence: [],
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const records: Record<string, SchemaPropsInfo> = {
|
|
91
|
+
custom: {
|
|
92
|
+
props: { width: T.number },
|
|
93
|
+
migrations: wrongSequenceId,
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
expect(() => processPropsMigrations('shape', records)).toThrow(`sequenceId mismatch for custom`)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should process TLPropsMigrations format', () => {
|
|
101
|
+
const propsMigrations: TLPropsMigrations = {
|
|
102
|
+
sequence: [
|
|
103
|
+
{
|
|
104
|
+
id: 'com.tldraw.shape.custom/1',
|
|
105
|
+
up: (props: any) => ({ ...props, v: 1 }),
|
|
106
|
+
down: (props: any) => {
|
|
107
|
+
const { v: _v, ...rest } = props
|
|
108
|
+
return rest
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'com.tldraw.shape.custom/2',
|
|
113
|
+
dependsOn: ['com.tldraw.shape.custom/1'],
|
|
114
|
+
up: (props: any) => ({ ...props, v: 2 }),
|
|
115
|
+
down: (props: any) => ({ ...props, v: 1 }),
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const records: Record<string, SchemaPropsInfo> = {
|
|
121
|
+
custom: {
|
|
122
|
+
props: { width: T.number },
|
|
123
|
+
migrations: propsMigrations,
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const result = processPropsMigrations('shape', records)
|
|
128
|
+
|
|
129
|
+
expect(result).toHaveLength(1)
|
|
130
|
+
expect(result[0].sequenceId).toBe('com.tldraw.shape.custom')
|
|
131
|
+
expect(result[0].retroactive).toBe(true)
|
|
132
|
+
expect(result[0].sequence).toHaveLength(2)
|
|
133
|
+
|
|
134
|
+
// Check that props migrations were converted to store migrations
|
|
135
|
+
const storeMigrations = result[0].sequence as Migration[]
|
|
136
|
+
expect(storeMigrations[0].id).toBe('com.tldraw.shape.custom/1')
|
|
137
|
+
expect(storeMigrations[0].scope).toBe('record')
|
|
138
|
+
expect(storeMigrations[1].id).toBe('com.tldraw.shape.custom/2')
|
|
139
|
+
expect(storeMigrations[1].dependsOn).toEqual(['com.tldraw.shape.custom/1'])
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('should process legacy migrations format', () => {
|
|
143
|
+
const legacyMigrations = {
|
|
144
|
+
migrators: {
|
|
145
|
+
1: {
|
|
146
|
+
up: vi.fn((record: any) => ({ ...record, version: 1 })),
|
|
147
|
+
down: vi.fn((record: any) => {
|
|
148
|
+
const { version: _version, ...rest } = record
|
|
149
|
+
return rest
|
|
150
|
+
}),
|
|
151
|
+
},
|
|
152
|
+
2: {
|
|
153
|
+
up: vi.fn((record: any) => ({ ...record, version: 2 })),
|
|
154
|
+
down: vi.fn((record: any) => ({ ...record, version: 1 })),
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const records: Record<string, SchemaPropsInfo> = {
|
|
160
|
+
legacy: {
|
|
161
|
+
props: { width: T.number },
|
|
162
|
+
migrations: legacyMigrations as any,
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const result = processPropsMigrations('shape', records)
|
|
167
|
+
|
|
168
|
+
expect(result).toHaveLength(1)
|
|
169
|
+
expect(result[0].sequenceId).toBe('com.tldraw.shape.legacy')
|
|
170
|
+
expect(result[0].retroactive).toBe(true)
|
|
171
|
+
expect(result[0].sequence).toHaveLength(2)
|
|
172
|
+
|
|
173
|
+
// Check legacy migration conversion
|
|
174
|
+
const migrations = result[0].sequence as Migration[]
|
|
175
|
+
expect(migrations[0].id).toBe('com.tldraw.shape.legacy/1')
|
|
176
|
+
expect(migrations[0].scope).toBe('record')
|
|
177
|
+
expect(migrations[1].id).toBe('com.tldraw.shape.legacy/2')
|
|
178
|
+
expect(migrations[1].scope).toBe('record')
|
|
179
|
+
|
|
180
|
+
// Test filter function - cast to the specific migration type that has filter
|
|
181
|
+
const recordMigration = migrations[0] as any
|
|
182
|
+
const matchingRecord = { typeName: 'shape', type: 'legacy' }
|
|
183
|
+
const nonMatchingRecord = { typeName: 'shape', type: 'other' }
|
|
184
|
+
|
|
185
|
+
expect(recordMigration.filter(matchingRecord)).toBe(true)
|
|
186
|
+
expect(recordMigration.filter(nonMatchingRecord)).toBe(false)
|
|
187
|
+
})
|
|
188
|
+
})
|
package/src/recordsWithProps.ts
CHANGED
|
@@ -11,18 +11,78 @@ import { MakeUndefinedOptional, assert } from '@tldraw/utils'
|
|
|
11
11
|
import { T } from '@tldraw/validate'
|
|
12
12
|
import { SchemaPropsInfo } from './createTLSchema'
|
|
13
13
|
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Maps a record's property types to their corresponding validators.
|
|
16
|
+
*
|
|
17
|
+
* This utility type takes a record type with a `props` object and creates
|
|
18
|
+
* a mapping where each property key maps to a validator for that property's type.
|
|
19
|
+
* This is used to define validation schemas for record properties.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* interface MyShape extends TLBaseShape<'custom', { width: number; color: string }> {}
|
|
24
|
+
*
|
|
25
|
+
* // Define validators for the shape properties
|
|
26
|
+
* const myShapeProps: RecordProps<MyShape> = {
|
|
27
|
+
* width: T.number,
|
|
28
|
+
* color: T.string
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
15
34
|
export type RecordProps<R extends UnknownRecord & { props: object }> = {
|
|
16
35
|
[K in keyof R['props']]: T.Validatable<R['props'][K]>
|
|
17
36
|
}
|
|
18
37
|
|
|
19
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Extracts the TypeScript types from a record properties configuration.
|
|
40
|
+
*
|
|
41
|
+
* Takes a configuration object where values are validators and returns the
|
|
42
|
+
* corresponding TypeScript types, with undefined values made optional.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const shapePropsConfig = {
|
|
47
|
+
* width: T.number,
|
|
48
|
+
* height: T.number,
|
|
49
|
+
* color: T.optional(T.string)
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* type ShapeProps = RecordPropsType<typeof shapePropsConfig>
|
|
53
|
+
* // Result: { width: number; height: number; color?: string }
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
20
58
|
export type RecordPropsType<Config extends Record<string, T.Validatable<any>>> =
|
|
21
59
|
MakeUndefinedOptional<{
|
|
22
60
|
[K in keyof Config]: T.TypeOf<Config[K]>
|
|
23
61
|
}>
|
|
24
62
|
|
|
25
63
|
/**
|
|
64
|
+
* A migration definition for shape or record properties.
|
|
65
|
+
*
|
|
66
|
+
* Defines how to transform record properties when migrating between schema versions.
|
|
67
|
+
* Each migration has an `up` function to upgrade data and an optional `down` function
|
|
68
|
+
* to downgrade data if needed.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* const addColorMigration: TLPropsMigration = {
|
|
73
|
+
* id: 'com.myapp.shape.custom/1.0.0',
|
|
74
|
+
* up: (props) => {
|
|
75
|
+
* // Add a default color property
|
|
76
|
+
* return { ...props, color: 'black' }
|
|
77
|
+
* },
|
|
78
|
+
* down: (props) => {
|
|
79
|
+
* // Remove the color property
|
|
80
|
+
* const { color, ...rest } = props
|
|
81
|
+
* return rest
|
|
82
|
+
* }
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
26
86
|
* @public
|
|
27
87
|
*/
|
|
28
88
|
export interface TLPropsMigration {
|
|
@@ -43,12 +103,57 @@ export interface TLPropsMigration {
|
|
|
43
103
|
}
|
|
44
104
|
|
|
45
105
|
/**
|
|
106
|
+
* A sequence of property migrations for a record type.
|
|
107
|
+
*
|
|
108
|
+
* Contains an ordered array of migrations that should be applied to transform
|
|
109
|
+
* record properties from one version to another. Migrations can include both
|
|
110
|
+
* property-specific migrations and standalone dependency declarations.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* const myShapeMigrations: TLPropsMigrations = {
|
|
115
|
+
* sequence: [
|
|
116
|
+
* {
|
|
117
|
+
* id: 'com.myapp.shape.custom/1.0.0',
|
|
118
|
+
* up: (props) => ({ ...props, version: 1 })
|
|
119
|
+
* },
|
|
120
|
+
* {
|
|
121
|
+
* id: 'com.myapp.shape.custom/2.0.0',
|
|
122
|
+
* up: (props) => ({ ...props, newFeature: true })
|
|
123
|
+
* }
|
|
124
|
+
* ]
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
46
128
|
* @public
|
|
47
129
|
*/
|
|
48
130
|
export interface TLPropsMigrations {
|
|
49
131
|
readonly sequence: Array<StandaloneDependsOn | TLPropsMigration>
|
|
50
132
|
}
|
|
51
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Processes property migrations for all record types in a schema.
|
|
136
|
+
*
|
|
137
|
+
* Takes a collection of record configurations and converts their migrations
|
|
138
|
+
* into proper migration sequences that can be used by the store system.
|
|
139
|
+
* Handles different migration formats including legacy migrations.
|
|
140
|
+
*
|
|
141
|
+
* @param typeName - The base type name for the records (e.g., 'shape', 'binding')
|
|
142
|
+
* @param records - Record of type names to their schema configuration
|
|
143
|
+
* @returns Array of processed migration sequences
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* const shapeRecords = {
|
|
148
|
+
* geo: { props: geoProps, migrations: geoMigrations },
|
|
149
|
+
* arrow: { props: arrowProps, migrations: arrowMigrations }
|
|
150
|
+
* }
|
|
151
|
+
*
|
|
152
|
+
* const sequences = processPropsMigrations('shape', shapeRecords)
|
|
153
|
+
* ```
|
|
154
|
+
*
|
|
155
|
+
* @internal
|
|
156
|
+
*/
|
|
52
157
|
export function processPropsMigrations<R extends UnknownRecord & { type: string; props: object }>(
|
|
53
158
|
typeName: R['typeName'],
|
|
54
159
|
records: Record<string, SchemaPropsInfo>
|
|
@@ -118,6 +223,30 @@ export function processPropsMigrations<R extends UnknownRecord & { type: string;
|
|
|
118
223
|
return result
|
|
119
224
|
}
|
|
120
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Creates a store migration from a props migration definition.
|
|
228
|
+
*
|
|
229
|
+
* Converts a high-level property migration into a low-level store migration
|
|
230
|
+
* that can be applied to records. The resulting migration will only affect
|
|
231
|
+
* records of the specified type and subtype.
|
|
232
|
+
*
|
|
233
|
+
* @param typeName - The base type name (e.g., 'shape', 'binding')
|
|
234
|
+
* @param subType - The specific subtype (e.g., 'geo', 'arrow')
|
|
235
|
+
* @param m - The property migration definition
|
|
236
|
+
* @returns A store migration that applies the property transformation
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* const propsMigration: TLPropsMigration = {
|
|
241
|
+
* id: 'com.myapp.shape.custom/1.0.0',
|
|
242
|
+
* up: (props) => ({ ...props, color: 'blue' })
|
|
243
|
+
* }
|
|
244
|
+
*
|
|
245
|
+
* const storeMigration = createPropsMigration('shape', 'custom', propsMigration)
|
|
246
|
+
* ```
|
|
247
|
+
*
|
|
248
|
+
* @internal
|
|
249
|
+
*/
|
|
121
250
|
export function createPropsMigration<R extends UnknownRecord & { type: string; props: object }>(
|
|
122
251
|
typeName: R['typeName'],
|
|
123
252
|
subType: R['type'],
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { TLShapeCrop } from './ShapeWithCrop'
|
|
3
|
+
|
|
4
|
+
describe('TLShapeCrop', () => {
|
|
5
|
+
test('should calculate crop dimensions correctly', () => {
|
|
6
|
+
const crop: TLShapeCrop = {
|
|
7
|
+
topLeft: { x: 0.2, y: 0.3 },
|
|
8
|
+
bottomRight: { x: 0.8, y: 0.7 },
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// This tests the actual utility of the crop interface - calculating dimensions
|
|
12
|
+
const width = crop.bottomRight.x - crop.topLeft.x
|
|
13
|
+
const height = crop.bottomRight.y - crop.topLeft.y
|
|
14
|
+
|
|
15
|
+
expect(width).toBeCloseTo(0.6)
|
|
16
|
+
expect(height).toBeCloseTo(0.4)
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -1,12 +1,74 @@
|
|
|
1
1
|
import { VecModel } from '../misc/geometry-types'
|
|
2
2
|
import { TLBaseShape } from './TLBaseShape'
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Defines cropping parameters for shapes that support cropping.
|
|
6
|
+
*
|
|
7
|
+
* Specifies the visible area of an asset (like an image or video) within a shape.
|
|
8
|
+
* The crop is defined by top-left and bottom-right coordinates in normalized space (0-1),
|
|
9
|
+
* where (0,0) is the top-left of the original asset and (1,1) is the bottom-right.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* // Crop the center 50% of an image
|
|
14
|
+
* const centerCrop: TLShapeCrop = {
|
|
15
|
+
* topLeft: { x: 0.25, y: 0.25 },
|
|
16
|
+
* bottomRight: { x: 0.75, y: 0.75 }
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* // Crop for a circular image shape
|
|
20
|
+
* const circleCrop: TLShapeCrop = {
|
|
21
|
+
* topLeft: { x: 0, y: 0 },
|
|
22
|
+
* bottomRight: { x: 1, y: 1 },
|
|
23
|
+
* isCircle: true
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
5
29
|
export interface TLShapeCrop {
|
|
6
30
|
topLeft: VecModel
|
|
7
31
|
bottomRight: VecModel
|
|
8
32
|
isCircle?: boolean
|
|
9
33
|
}
|
|
10
34
|
|
|
11
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* A shape type that supports cropping functionality.
|
|
37
|
+
*
|
|
38
|
+
* This type represents any shape that can display cropped content, typically media shapes
|
|
39
|
+
* like images and videos. The shape has width, height, and optional crop parameters.
|
|
40
|
+
* When crop is null, the entire asset is displayed.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* // An image shape with cropping
|
|
45
|
+
* const croppedImageShape: ShapeWithCrop = {
|
|
46
|
+
* id: 'shape:image1',
|
|
47
|
+
* type: 'image',
|
|
48
|
+
* x: 100,
|
|
49
|
+
* y: 200,
|
|
50
|
+
* // ... other base shape properties
|
|
51
|
+
* props: {
|
|
52
|
+
* w: 300,
|
|
53
|
+
* h: 200,
|
|
54
|
+
* crop: {
|
|
55
|
+
* topLeft: { x: 0.1, y: 0.1 },
|
|
56
|
+
* bottomRight: { x: 0.9, y: 0.9 }
|
|
57
|
+
* }
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
*
|
|
61
|
+
* // A shape without cropping (shows full asset)
|
|
62
|
+
* const fullImageShape: ShapeWithCrop = {
|
|
63
|
+
* // ... shape properties
|
|
64
|
+
* props: {
|
|
65
|
+
* w: 400,
|
|
66
|
+
* h: 300,
|
|
67
|
+
* crop: null // Shows entire asset
|
|
68
|
+
* }
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @public
|
|
73
|
+
*/
|
|
12
74
|
export type ShapeWithCrop = TLBaseShape<string, { w: number; h: number; crop: TLShapeCrop | null }>
|