@tldraw/tlschema
Version:
tldraw infinite canvas SDK (schema).
187 lines (179 loc) • 5.43 kB
text/typescript
import { T } from '@tldraw/validate'
import { b64Vecs } from '../misc/b64Vecs'
import { createShapePropsMigrationIds, createShapePropsMigrationSequence } from '../records/TLShape'
import { RecordProps } from '../recordsWithProps'
import { DefaultColorStyle, TLDefaultColorStyle } from '../styles/TLColorStyle'
import { DefaultSizeStyle, TLDefaultSizeStyle } from '../styles/TLSizeStyle'
import { TLBaseShape } from './TLBaseShape'
import { DrawShapeSegment, TLDrawShapeSegment } from './TLDrawShape'
/**
* Properties for a highlight shape. Highlight shapes represent highlighting strokes made with
* a highlighting tool, typically used to emphasize or mark up content.
*
* @public
* @example
* ```ts
* const highlightProps: TLHighlightShapeProps = {
* color: 'yellow',
* size: 'm',
* segments: [{ type: 'straight', points: [{ x: 0, y: 0, z: 0.5 }] }],
* isComplete: true,
* isPen: false,
* scale: 1
* }
* ```
*/
export interface TLHighlightShapeProps {
/** The color style of the highlight stroke */
color: TLDefaultColorStyle
/** The size style determining the thickness of the highlight stroke */
size: TLDefaultSizeStyle
/** Array of segments that make up the highlight stroke path */
segments: TLDrawShapeSegment[]
/** Whether the highlight stroke has been completed by the user */
isComplete: boolean
/** Whether the highlight was drawn with a pen/stylus (affects rendering style) */
isPen: boolean
/** Scale factor applied to the highlight shape for display */
scale: number
/** Horizontal scale factor for lazy resize */
scaleX: number
/** Vertical scale factor for lazy resize */
scaleY: number
}
/**
* A highlight shape representing a highlighting stroke drawn by the user. Highlight shapes
* are typically semi-transparent and used for marking up or emphasizing content on the canvas.
*
* @public
* @example
* ```ts
* const highlightShape: TLHighlightShape = {
* id: 'shape:highlight1',
* type: 'highlight',
* x: 100,
* y: 50,
* rotation: 0,
* index: 'a1',
* parentId: 'page:main',
* isLocked: false,
* opacity: 0.7,
* props: {
* color: 'yellow',
* size: 'l',
* segments: [],
* isComplete: false,
* isPen: false,
* scale: 1
* },
* meta: {},
* typeName: 'shape'
* }
* ```
*/
export type TLHighlightShape = TLBaseShape<'highlight', TLHighlightShapeProps>
/**
* Validation schema for highlight shape properties. Defines the runtime validation rules
* for all properties of highlight shapes.
*
* @public
* @example
* ```ts
* import { highlightShapeProps } from '@tldraw/tlschema'
*
* // Used internally by the validation system
* const validator = T.object(highlightShapeProps)
* const validatedProps = validator.validate(someHighlightProps)
* ```
*/
/** @public */
export const highlightShapeProps: RecordProps<TLHighlightShape> = {
color: DefaultColorStyle,
size: DefaultSizeStyle,
segments: T.arrayOf(DrawShapeSegment),
isComplete: T.boolean,
isPen: T.boolean,
scale: T.nonZeroNumber,
scaleX: T.nonZeroFiniteNumber,
scaleY: T.nonZeroFiniteNumber,
}
const Versions = createShapePropsMigrationIds('highlight', {
AddScale: 1,
Base64: 2,
LegacyPointsConversion: 3,
})
/**
* Version identifiers for highlight shape migrations. These version numbers track
* schema changes over time to enable proper data migration.
*
* @public
*/
export { Versions as highlightShapeVersions }
/**
* Migration sequence for highlight shapes. Handles schema evolution over time by defining
* how to upgrade and downgrade highlight shape data between different versions.
*
* @public
*/
export const highlightShapeMigrations = createShapePropsMigrationSequence({
sequence: [
{
id: Versions.AddScale,
up: (props) => {
props.scale = 1
},
down: (props) => {
delete props.scale
},
},
{
id: Versions.Base64,
up: (props) => {
// Convert VecModel[] arrays directly to delta-encoded base64 in 'path'
props.segments = props.segments.map((segment: any) => {
if (segment.path !== undefined) return segment
const { points, ...rest } = segment
const vecModels = Array.isArray(points) ? points : b64Vecs._legacyDecodePoints(points)
return {
...rest,
path: b64Vecs.encodePoints(vecModels),
}
})
props.scaleX = props.scaleX ?? 1
props.scaleY = props.scaleY ?? 1
},
down: (props) => {
// Convert delta-encoded 'path' back to VecModel[] arrays in 'points'
props.segments = props.segments.map((segment: any) => {
const { path, ...rest } = segment
return {
...rest,
points: b64Vecs.decodePoints(path),
}
})
delete props.scaleX
delete props.scaleY
},
},
{
id: Versions.LegacyPointsConversion,
up: (props) => {
// Handle legacy data that was already migrated to v2 with absolute Float16 in 'points'
// Convert 'points' to delta-encoded 'path'
props.segments = props.segments.map((segment: any) => {
// If segment already has 'path', it's already in the new format
if (segment.path !== undefined) return segment
const { points, ...rest } = segment
const vecModels = Array.isArray(points) ? points : b64Vecs._legacyDecodePoints(points)
return {
...rest,
path: b64Vecs.encodePoints(vecModels),
}
})
},
down: (_props) => {
// handled by the previous down migration
},
},
],
})