Tldraw/packages/tlschema/src/shapes/TLLineShape.ts

163 wiersze
3.8 KiB
TypeScript

import { IndexKey, getIndices, objectMapFromEntries, sortByIndex } from '@tldraw/utils'
import { T } from '@tldraw/validate'
import {
RETIRED_DOWN_MIGRATION,
createShapePropsMigrationIds,
createShapePropsMigrationSequence,
} from '../records/TLShape'
import { StyleProp } from '../styles/StyleProp'
import { DefaultColorStyle } from '../styles/TLColorStyle'
import { DefaultDashStyle } from '../styles/TLDashStyle'
import { DefaultSizeStyle } from '../styles/TLSizeStyle'
import { ShapePropsType, TLBaseShape } from './TLBaseShape'
/** @public */
export const LineShapeSplineStyle = StyleProp.defineEnum('tldraw:spline', {
defaultValue: 'line',
values: ['cubic', 'line'],
})
/** @public */
export type TLLineShapeSplineStyle = T.TypeOf<typeof LineShapeSplineStyle>
const lineShapePointValidator = T.object({
id: T.string,
index: T.indexKey,
x: T.number,
y: T.number,
})
/** @public */
export const lineShapeProps = {
color: DefaultColorStyle,
dash: DefaultDashStyle,
size: DefaultSizeStyle,
spline: LineShapeSplineStyle,
points: T.dict(T.string, lineShapePointValidator),
}
/** @public */
export type TLLineShapeProps = ShapePropsType<typeof lineShapeProps>
/** @public */
export type TLLineShape = TLBaseShape<'line', TLLineShapeProps>
/** @internal */
export const lineShapeVersions = createShapePropsMigrationIds('line', {
AddSnapHandles: 1,
RemoveExtraHandleProps: 2,
HandlesToPoints: 3,
PointIndexIds: 4,
})
/** @internal */
export const lineShapeMigrations = createShapePropsMigrationSequence({
sequence: [
{
id: lineShapeVersions.AddSnapHandles,
up: (props) => {
for (const handle of Object.values(props.handles)) {
;(handle as any).canSnap = true
}
},
down: RETIRED_DOWN_MIGRATION,
},
{
id: lineShapeVersions.RemoveExtraHandleProps,
up: (props) => {
props.handles = objectMapFromEntries(
Object.values(props.handles).map((handle: any) => [
handle.index,
{
x: handle.x,
y: handle.y,
},
])
)
},
down: (props) => {
const handles = Object.entries(props.handles)
.map(([index, handle]: any) => ({ index, ...handle }))
.sort(sortByIndex)
props.handles = Object.fromEntries(
handles.map((handle, i) => {
const id =
i === 0 ? 'start' : i === handles.length - 1 ? 'end' : `handle:${handle.index}`
return [
id,
{
id,
type: 'vertex',
canBind: false,
canSnap: true,
index: handle.index,
x: handle.x,
y: handle.y,
},
]
})
)
},
},
{
id: lineShapeVersions.HandlesToPoints,
up: (props) => {
const sortedHandles = (
Object.entries(props.handles) as [IndexKey, { x: number; y: number }][]
)
.map(([index, { x, y }]) => ({ x, y, index }))
.sort(sortByIndex)
props.points = sortedHandles.map(({ x, y }) => ({ x, y }))
delete props.handles
},
down: (props) => {
const indices = getIndices(props.points.length)
props.handles = Object.fromEntries(
props.points.map((handle: { x: number; y: number }, i: number) => {
const index = indices[i]
return [
index,
{
x: handle.x,
y: handle.y,
},
]
})
)
delete props.points
},
},
{
id: lineShapeVersions.PointIndexIds,
up: (props) => {
const indices = getIndices(props.points.length)
props.points = Object.fromEntries(
props.points.map((point: { x: number; y: number }, i: number) => {
const id = indices[i]
return [
id,
{
id: id,
index: id,
x: point.x,
y: point.y,
},
]
})
)
},
down: (props) => {
const sortedHandles = (
Object.values(props.points) as { x: number; y: number; index: IndexKey }[]
).sort(sortByIndex)
props.points = sortedHandles.map(({ x, y }) => ({ x, y }))
},
},
],
})