@tldraw/tlschema
Version:
tldraw infinite canvas SDK (schema).
8 lines (7 loc) • 6.1 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../src/shapes/TLBaseShape.ts"],
"sourcesContent": ["import { IndexKey, JsonObject } from '@tldraw/utils'\nimport { T } from '@tldraw/validate'\nimport { idValidator } from '../misc/id-validator'\nimport { TLOpacityType, opacityValidator } from '../misc/TLOpacity'\nimport { TLParentId, TLShapeId } from '../records/TLShape'\n\n/**\n * Base interface for all shapes in tldraw.\n *\n * This interface defines the common properties that all shapes share, regardless of their\n * specific type. Every default shape extends this base with additional type-specific properties.\n *\n * Custom shapes should be defined by augmenting the TLGlobalShapePropsMap type and getting the shape type from the TLShape type.\n *\n * @example\n * ```ts\n * // Define a default shape type\n * interface TLArrowShape extends TLBaseShape<'arrow', {\n * kind: TLArrowShapeKind\n * labelColor: TLDefaultColorStyle\n * color: TLDefaultColorStyle\n * fill: TLDefaultFillStyle\n * dash: TLDefaultDashStyle\n * size: TLDefaultSizeStyle\n * arrowheadStart: TLArrowShapeArrowheadStyle\n * arrowheadEnd: TLArrowShapeArrowheadStyle\n * font: TLDefaultFontStyle\n * start: VecModel\n * end: VecModel\n * bend: number\n * richText: TLRichText\n * labelPosition: number\n * scale: number\n * elbowMidPoint: number\n * }> {}\n *\n * // Create a shape instance\n * const arrowShape: TLArrowShape = {\n * id: 'shape:abc123',\n * typeName: 'shape',\n * type: 'arrow',\n * x: 100,\n * y: 200,\n * rotation: 0,\n * index: 'a1',\n * parentId: 'page:main',\n * isLocked: false,\n * opacity: 1,\n * props: {\n * kind: 'arc',\n * start: { x: 0, y: 0 },\n * end: { x: 100, y: 100 },\n * // ... other props\n * },\n * meta: {}\n * }\n * ```\n *\n * @public\n */\nexport interface TLBaseShape<Type extends string, Props extends object> {\n\t// using real `extends BaseRecord<'shape', TLShapeId>` introduces a circularity in the types\n\t// and for that reason those \"base members\" have to be declared manually here\n\treadonly id: TLShapeId\n\treadonly typeName: 'shape'\n\n\ttype: Type\n\tx: number\n\ty: number\n\trotation: number\n\tindex: IndexKey\n\tparentId: TLParentId\n\tisLocked: boolean\n\topacity: TLOpacityType\n\tprops: Props\n\tmeta: JsonObject\n}\n\n/**\n * Validator for parent IDs, ensuring they follow the correct format.\n *\n * Parent IDs must start with either \"page:\" (for shapes directly on a page)\n * or \"shape:\" (for shapes inside other shapes like frames or groups).\n *\n * @example\n * ```ts\n * // Valid parent IDs\n * const pageParent = parentIdValidator.validate('page:main') // \u2713\n * const shapeParent = parentIdValidator.validate('shape:frame1') // \u2713\n *\n * // Invalid parent ID (throws error)\n * const invalid = parentIdValidator.validate('invalid:123') // \u2717\n * ```\n *\n * @public\n */\nexport const parentIdValidator = T.string.refine((id) => {\n\tif (!id.startsWith('page:') && !id.startsWith('shape:')) {\n\t\tthrow new Error('Parent ID must start with \"page:\" or \"shape:\"')\n\t}\n\treturn id as TLParentId\n})\n\n/**\n * Validator for shape IDs, ensuring they follow the \"shape:\" format.\n *\n * @example\n * ```ts\n * const validId = shapeIdValidator.validate('shape:abc123') // \u2713\n * const invalidId = shapeIdValidator.validate('page:abc123') // \u2717 throws error\n * ```\n *\n * @public\n */\nexport const shapeIdValidator = idValidator<TLShapeId>('shape')\n\n/**\n * Creates a validator for a specific shape type.\n *\n * This function generates a complete validator that can validate shape records\n * of the specified type, including both the base shape properties and any\n * custom properties and metadata specific to that shape type.\n *\n * @param type - The string literal type for this shape (e.g., 'geo', 'arrow')\n * @param props - Optional validator configuration for shape-specific properties\n * @param meta - Optional validator configuration for shape-specific metadata\n * @returns A validator that can validate complete shape records of the specified type\n *\n * @example\n * ```ts\n * // Create a validator for a custom shape type\n * const customShapeValidator = createShapeValidator('custom', {\n * width: T.number,\n * height: T.number,\n * color: T.string\n * })\n *\n * // Use the validator to validate shape data\n * const shapeData = {\n * id: 'shape:abc123',\n * typeName: 'shape',\n * type: 'custom',\n * x: 100,\n * y: 200,\n * // ... other base properties\n * props: {\n * width: 150,\n * height: 100,\n * color: 'red'\n * }\n * }\n *\n * const validatedShape = customShapeValidator.validate(shapeData)\n * ```\n *\n * @public\n */\nexport function createShapeValidator<\n\tType extends string,\n\tProps extends JsonObject,\n\tMeta extends JsonObject,\n>(\n\ttype: Type,\n\tprops?: { [K in keyof Props]: T.Validatable<Props[K]> },\n\tmeta?: { [K in keyof Meta]: T.Validatable<Meta[K]> }\n) {\n\treturn T.object<TLBaseShape<Type, Props>>({\n\t\tid: shapeIdValidator,\n\t\ttypeName: T.literal('shape'),\n\t\tx: T.number,\n\t\ty: T.number,\n\t\trotation: T.number,\n\t\tindex: T.indexKey,\n\t\tparentId: parentIdValidator,\n\t\ttype: T.literal(type),\n\t\tisLocked: T.boolean,\n\t\topacity: opacityValidator,\n\t\tprops: props ? T.object(props) : (T.jsonValue as any),\n\t\tmeta: meta ? T.object(meta) : (T.jsonValue as any),\n\t})\n}\n"],
"mappings": "AACA,SAAS,SAAS;AAClB,SAAS,mBAAmB;AAC5B,SAAwB,wBAAwB;AA6FzC,MAAM,oBAAoB,EAAE,OAAO,OAAO,CAAC,OAAO;AACxD,MAAI,CAAC,GAAG,WAAW,OAAO,KAAK,CAAC,GAAG,WAAW,QAAQ,GAAG;AACxD,UAAM,IAAI,MAAM,+CAA+C;AAAA,EAChE;AACA,SAAO;AACR,CAAC;AAaM,MAAM,mBAAmB,YAAuB,OAAO;AA2CvD,SAAS,qBAKf,MACA,OACA,MACC;AACD,SAAO,EAAE,OAAiC;AAAA,IACzC,IAAI;AAAA,IACJ,UAAU,EAAE,QAAQ,OAAO;AAAA,IAC3B,GAAG,EAAE;AAAA,IACL,GAAG,EAAE;AAAA,IACL,UAAU,EAAE;AAAA,IACZ,OAAO,EAAE;AAAA,IACT,UAAU;AAAA,IACV,MAAM,EAAE,QAAQ,IAAI;AAAA,IACpB,UAAU,EAAE;AAAA,IACZ,SAAS;AAAA,IACT,OAAO,QAAQ,EAAE,OAAO,KAAK,IAAK,EAAE;AAAA,IACpC,MAAM,OAAO,EAAE,OAAO,IAAI,IAAK,EAAE;AAAA,EAClC,CAAC;AACF;",
"names": []
}