Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
190 lines
5.7 KiB
TypeScript
190 lines
5.7 KiB
TypeScript
import { JsonObject } from '@tldraw/utils'
|
|
import { T } from '@tldraw/validate'
|
|
import { idValidator } from '../misc/id-validator'
|
|
import { TLBindingId } from '../records/TLBinding'
|
|
import { TLShapeId } from '../records/TLShape'
|
|
import { shapeIdValidator } from '../shapes/TLBaseShape'
|
|
|
|
/**
|
|
* Base interface for all binding types in tldraw. Bindings represent relationships
|
|
* between shapes, such as arrows connecting to other shapes or organizational connections.
|
|
*
|
|
* All default bindings extend this base interface with specific type and property definitions.
|
|
* The binding system enables shapes to maintain relationships that persist through
|
|
* transformations, movements, and other operations.
|
|
*
|
|
* Custom bindings should be defined by augmenting the TLGlobalBindingPropsMap type and getting the binding type from the TLBinding type.
|
|
*
|
|
* @param Type - String literal type identifying the specific binding type (e.g., 'arrow')
|
|
* @param Props - Object containing binding-specific properties and configuration
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* // Define a default binding type
|
|
* interface TLArrowBinding extends TLBaseBinding<'arrow', TLArrowBindingProps> {}
|
|
*
|
|
* interface TLArrowBindingProps {
|
|
* terminal: 'start' | 'end'
|
|
* normalizedAnchor: VecModel
|
|
* isExact: boolean
|
|
* isPrecise: boolean
|
|
* snap: ElbowArrowSnap
|
|
* }
|
|
*
|
|
* // Create a binding instance
|
|
* const arrowBinding: TLArrowBinding = {
|
|
* id: 'binding:abc123',
|
|
* typeName: 'binding',
|
|
* type: 'arrow',
|
|
* fromId: 'shape:source1',
|
|
* toId: 'shape:target1',
|
|
* props: {
|
|
* terminal: 'end',
|
|
* normalizedAnchor: { x: 0.5, y: 0.5 },
|
|
* isExact: false,
|
|
* isPrecise: true,
|
|
* snap: 'edge'
|
|
* },
|
|
* meta: {}
|
|
* }
|
|
* ```
|
|
*
|
|
* @public
|
|
*/
|
|
export interface TLBaseBinding<Type extends string, Props extends object> {
|
|
// using real `extends BaseRecord<'binding', TLBindingId>` introduces a circularity in the types
|
|
// and for that reason those "base members" have to be declared manually here
|
|
readonly id: TLBindingId
|
|
readonly typeName: 'binding'
|
|
|
|
/** The specific type of this binding (e.g., 'arrow', 'custom') */
|
|
type: Type
|
|
/** ID of the source shape in this binding relationship */
|
|
fromId: TLShapeId
|
|
/** ID of the target shape in this binding relationship */
|
|
toId: TLShapeId
|
|
/** Binding-specific properties that define behavior and appearance */
|
|
props: Props
|
|
/** User-defined metadata for extending binding functionality */
|
|
meta: JsonObject
|
|
}
|
|
|
|
/**
|
|
* Validator for binding IDs. Ensures that binding identifiers follow the correct
|
|
* format and type constraints required by the tldraw schema system.
|
|
*
|
|
* Used internally by the schema validation system to verify binding IDs when
|
|
* records are created or modified. All binding IDs must be prefixed with 'binding:'.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* import { bindingIdValidator } from '@tldraw/tlschema'
|
|
*
|
|
* // Validate a binding ID
|
|
* const isValid = bindingIdValidator.isValid('binding:abc123') // true
|
|
* const isInvalid = bindingIdValidator.isValid('shape:abc123') // false
|
|
*
|
|
* // Use in custom validation schema
|
|
* const customBindingValidator = T.object({
|
|
* id: bindingIdValidator,
|
|
* // ... other properties
|
|
* })
|
|
* ```
|
|
*
|
|
* @public
|
|
*/
|
|
export const bindingIdValidator = idValidator<TLBindingId>('binding')
|
|
|
|
/**
|
|
* Creates a runtime validator for a specific binding type. This factory function
|
|
* generates a complete validation schema for custom bindings that extends TLBaseBinding.
|
|
*
|
|
* The validator ensures all binding records conform to the expected structure with
|
|
* proper type safety and runtime validation. It validates the base binding properties
|
|
* (id, type, fromId, toId) along with custom props and meta fields.
|
|
*
|
|
* @param type - The string literal type identifier for this binding (e.g., 'arrow', 'custom')
|
|
* @param props - Optional validation schema for binding-specific properties
|
|
* @param meta - Optional validation schema for metadata fields
|
|
*
|
|
* @returns A validator object that can validate complete binding records
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* import { createBindingValidator } from '@tldraw/tlschema'
|
|
* import { T } from '@tldraw/validate'
|
|
*
|
|
* // Create validator for a custom binding type
|
|
* const myBindingValidator = createBindingValidator(
|
|
* 'myBinding',
|
|
* {
|
|
* strength: T.number,
|
|
* color: T.string,
|
|
* enabled: T.boolean
|
|
* },
|
|
* {
|
|
* createdAt: T.number,
|
|
* author: T.string
|
|
* }
|
|
* )
|
|
*
|
|
* // Validate a binding instance
|
|
* const bindingData = {
|
|
* id: 'binding:123',
|
|
* typeName: 'binding',
|
|
* type: 'myBinding',
|
|
* fromId: 'shape:abc',
|
|
* toId: 'shape:def',
|
|
* props: {
|
|
* strength: 0.8,
|
|
* color: 'red',
|
|
* enabled: true
|
|
* },
|
|
* meta: {
|
|
* createdAt: Date.now(),
|
|
* author: 'user123'
|
|
* }
|
|
* }
|
|
*
|
|
* const isValid = myBindingValidator.isValid(bindingData) // true
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* // Simple binding without custom props or meta
|
|
* const simpleBindingValidator = createBindingValidator('simple')
|
|
*
|
|
* // This will use JsonValue validation for props and meta
|
|
* const binding = {
|
|
* id: 'binding:456',
|
|
* typeName: 'binding',
|
|
* type: 'simple',
|
|
* fromId: 'shape:start',
|
|
* toId: 'shape:end',
|
|
* props: {}, // Any JSON value allowed
|
|
* meta: {} // Any JSON value allowed
|
|
* }
|
|
* ```
|
|
*
|
|
* @public
|
|
*/
|
|
export function createBindingValidator<
|
|
Type extends string,
|
|
Props extends JsonObject,
|
|
Meta extends JsonObject,
|
|
>(
|
|
type: Type,
|
|
props?: { [K in keyof Props]: T.Validatable<Props[K]> },
|
|
meta?: { [K in keyof Meta]: T.Validatable<Meta[K]> }
|
|
) {
|
|
return T.object<TLBaseBinding<Type, Props>>({
|
|
id: bindingIdValidator,
|
|
typeName: T.literal('binding'),
|
|
type: T.literal(type),
|
|
fromId: shapeIdValidator,
|
|
toId: shapeIdValidator,
|
|
props: props ? T.object(props) : (T.jsonValue as any),
|
|
meta: meta ? T.object(meta) : (T.jsonValue as any),
|
|
})
|
|
}
|