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

183 wiersze
4.1 KiB
TypeScript

import { defineMigrations } from '@tldraw/store'
import { T } from '@tldraw/validate'
import { vecModelValidator } from '../misc/geometry-types'
import { StyleProp } from '../styles/StyleProp'
import { DefaultColorStyle, DefaultLabelColorStyle } from '../styles/TLColorStyle'
import { DefaultDashStyle } from '../styles/TLDashStyle'
import { DefaultFillStyle } from '../styles/TLFillStyle'
import { DefaultFontStyle } from '../styles/TLFontStyle'
import { DefaultSizeStyle } from '../styles/TLSizeStyle'
import { ShapePropsType, TLBaseShape, shapeIdValidator } from './TLBaseShape'
const arrowheadTypes = [
'arrow',
'triangle',
'square',
'dot',
'pipe',
'diamond',
'inverted',
'bar',
'none',
] as const
/** @public */
export const ArrowShapeArrowheadStartStyle = StyleProp.defineEnum('tldraw:arrowheadStart', {
defaultValue: 'none',
values: arrowheadTypes,
})
/** @public */
export const ArrowShapeArrowheadEndStyle = StyleProp.defineEnum('tldraw:arrowheadEnd', {
defaultValue: 'arrow',
values: arrowheadTypes,
})
/** @public */
export type TLArrowShapeArrowheadStyle = T.TypeOf<typeof ArrowShapeArrowheadStartStyle>
/** @public */
const ArrowShapeTerminal = T.union('type', {
binding: T.object({
type: T.literal('binding'),
boundShapeId: shapeIdValidator,
normalizedAnchor: vecModelValidator,
isExact: T.boolean,
isPrecise: T.boolean,
}),
point: T.object({
type: T.literal('point'),
x: T.number,
y: T.number,
}),
})
/** @public */
export type TLArrowShapeTerminal = T.TypeOf<typeof ArrowShapeTerminal>
/** @public */
export const arrowShapeProps = {
labelColor: DefaultLabelColorStyle,
color: DefaultColorStyle,
fill: DefaultFillStyle,
dash: DefaultDashStyle,
size: DefaultSizeStyle,
arrowheadStart: ArrowShapeArrowheadStartStyle,
arrowheadEnd: ArrowShapeArrowheadEndStyle,
font: DefaultFontStyle,
start: ArrowShapeTerminal,
end: ArrowShapeTerminal,
bend: T.number,
text: T.string,
labelPosition: T.number,
}
/** @public */
export type TLArrowShapeProps = ShapePropsType<typeof arrowShapeProps>
/** @public */
export type TLArrowShape = TLBaseShape<'arrow', TLArrowShapeProps>
export const ArrowMigrationVersions = {
AddLabelColor: 1,
AddIsPrecise: 2,
AddLabelPosition: 3,
} as const
/** @internal */
export const arrowShapeMigrations = defineMigrations({
currentVersion: ArrowMigrationVersions.AddLabelPosition,
migrators: {
[ArrowMigrationVersions.AddLabelColor]: {
up: (record) => {
return {
...record,
props: {
...record.props,
labelColor: 'black',
},
}
},
down: (record) => {
const { labelColor: _, ...props } = record.props
return {
...record,
props,
}
},
},
[ArrowMigrationVersions.AddIsPrecise]: {
up: (record) => {
const { start, end } = record.props
return {
...record,
props: {
...record.props,
start:
(start as TLArrowShapeTerminal).type === 'binding'
? {
...start,
isPrecise: !(
start.normalizedAnchor.x === 0.5 && start.normalizedAnchor.y === 0.5
),
}
: start,
end:
(end as TLArrowShapeTerminal).type === 'binding'
? {
...end,
isPrecise: !(end.normalizedAnchor.x === 0.5 && end.normalizedAnchor.y === 0.5),
}
: end,
},
}
},
down: (record: any) => {
const { start, end } = record.props
const nStart = { ...start }
const nEnd = { ...end }
if (nStart.type === 'binding') {
if (!nStart.isPrecise) {
nStart.normalizedAnchor = { x: 0.5, y: 0.5 }
}
delete nStart.isPrecise
}
if (nEnd.type === 'binding') {
if (!nEnd.isPrecise) {
nEnd.normalizedAnchor = { x: 0.5, y: 0.5 }
}
delete nEnd.isPrecise
}
return {
...record,
props: {
...record.props,
start: nStart,
end: nEnd,
},
}
},
},
[ArrowMigrationVersions.AddLabelPosition]: {
up: (record) => {
return {
...record,
props: {
...record.props,
labelPosition: 0.5,
},
}
},
down: (record) => {
const { labelPosition: _, ...props } = record.props
return {
...record,
props,
}
},
},
},
})