@tldraw/tlschema
Version:
tldraw infinite canvas SDK (schema).
229 lines (228 loc) • 6.72 kB
JavaScript
import { createMigrationSequence } from "@tldraw/store";
import { structuredClone } from "@tldraw/utils";
import { T } from "@tldraw/validate";
import { vecModelValidator } from "../misc/geometry-types.mjs";
import { richTextValidator, toRichText } from "../misc/TLRichText.mjs";
import { createBindingId } from "../records/TLBinding.mjs";
import { createShapePropsMigrationIds } from "../records/TLShape.mjs";
import { createPropsMigration } from "../recordsWithProps.mjs";
import { StyleProp } from "../styles/StyleProp.mjs";
import {
DefaultColorStyle,
DefaultLabelColorStyle
} from "../styles/TLColorStyle.mjs";
import { DefaultDashStyle } from "../styles/TLDashStyle.mjs";
import { DefaultFillStyle } from "../styles/TLFillStyle.mjs";
import { DefaultFontStyle } from "../styles/TLFontStyle.mjs";
import { DefaultSizeStyle } from "../styles/TLSizeStyle.mjs";
const arrowKinds = ["arc", "elbow"];
const ArrowShapeKindStyle = StyleProp.defineEnum("tldraw:arrowKind", {
defaultValue: "arc",
values: arrowKinds
});
const arrowheadTypes = [
"arrow",
"triangle",
"square",
"dot",
"pipe",
"diamond",
"inverted",
"bar",
"none"
];
const ArrowShapeArrowheadStartStyle = StyleProp.defineEnum("tldraw:arrowheadStart", {
defaultValue: "none",
values: arrowheadTypes
});
const ArrowShapeArrowheadEndStyle = StyleProp.defineEnum("tldraw:arrowheadEnd", {
defaultValue: "arrow",
values: arrowheadTypes
});
const arrowShapeProps = {
kind: ArrowShapeKindStyle,
labelColor: DefaultLabelColorStyle,
color: DefaultColorStyle,
fill: DefaultFillStyle,
dash: DefaultDashStyle,
size: DefaultSizeStyle,
arrowheadStart: ArrowShapeArrowheadStartStyle,
arrowheadEnd: ArrowShapeArrowheadEndStyle,
font: DefaultFontStyle,
start: vecModelValidator,
end: vecModelValidator,
bend: T.number,
richText: richTextValidator,
labelPosition: T.number,
scale: T.nonZeroNumber,
elbowMidPoint: T.number
};
const arrowShapeVersions = createShapePropsMigrationIds("arrow", {
AddLabelColor: 1,
AddIsPrecise: 2,
AddLabelPosition: 3,
ExtractBindings: 4,
AddScale: 5,
AddElbow: 6,
AddRichText: 7,
AddRichTextAttrs: 8
});
function propsMigration(migration) {
return createPropsMigration("shape", "arrow", migration);
}
const arrowShapeMigrations = createMigrationSequence({
sequenceId: "com.tldraw.shape.arrow",
retroactive: false,
sequence: [
propsMigration({
id: arrowShapeVersions.AddLabelColor,
up: (props) => {
props.labelColor = "black";
},
down: "retired"
}),
propsMigration({
id: arrowShapeVersions.AddIsPrecise,
up: ({ start, end }) => {
if (start.type === "binding") {
start.isPrecise = !(start.normalizedAnchor.x === 0.5 && start.normalizedAnchor.y === 0.5);
}
if (end.type === "binding") {
end.isPrecise = !(end.normalizedAnchor.x === 0.5 && end.normalizedAnchor.y === 0.5);
}
},
down: ({ start, end }) => {
if (start.type === "binding") {
if (!start.isPrecise) {
start.normalizedAnchor = { x: 0.5, y: 0.5 };
}
delete start.isPrecise;
}
if (end.type === "binding") {
if (!end.isPrecise) {
end.normalizedAnchor = { x: 0.5, y: 0.5 };
}
delete end.isPrecise;
}
}
}),
propsMigration({
id: arrowShapeVersions.AddLabelPosition,
up: (props) => {
props.labelPosition = 0.5;
},
down: (props) => {
delete props.labelPosition;
}
}),
{
id: arrowShapeVersions.ExtractBindings,
scope: "storage",
up: (storage) => {
const updates = [];
for (const record of storage.values()) {
if (record.typeName !== "shape" || record.type !== "arrow") continue;
const arrow = record;
const newArrow = structuredClone(arrow);
const { start, end } = arrow.props;
if (start.type === "binding") {
const id = createBindingId();
const binding = {
typeName: "binding",
id,
type: "arrow",
fromId: arrow.id,
toId: start.boundShapeId,
meta: {},
props: {
terminal: "start",
normalizedAnchor: start.normalizedAnchor,
isExact: start.isExact,
isPrecise: start.isPrecise
}
};
updates.push([id, binding]);
newArrow.props.start = { x: 0, y: 0 };
} else {
delete newArrow.props.start.type;
}
if (end.type === "binding") {
const id = createBindingId();
const binding = {
typeName: "binding",
id,
type: "arrow",
fromId: arrow.id,
toId: end.boundShapeId,
meta: {},
props: {
terminal: "end",
normalizedAnchor: end.normalizedAnchor,
isExact: end.isExact,
isPrecise: end.isPrecise
}
};
updates.push([id, binding]);
newArrow.props.end = { x: 0, y: 0 };
} else {
delete newArrow.props.end.type;
}
updates.push([arrow.id, newArrow]);
}
for (const [id, record] of updates) {
storage.set(id, record);
}
}
},
propsMigration({
id: arrowShapeVersions.AddScale,
up: (props) => {
props.scale = 1;
},
down: (props) => {
delete props.scale;
}
}),
propsMigration({
id: arrowShapeVersions.AddElbow,
up: (props) => {
props.kind = "arc";
props.elbowMidPoint = 0.5;
},
down: (props) => {
delete props.kind;
delete props.elbowMidPoint;
}
}),
propsMigration({
id: arrowShapeVersions.AddRichText,
up: (props) => {
props.richText = toRichText(props.text);
delete props.text;
}
// N.B. Explicitly no down state so that we force clients to update.
// down: (props) => {
// delete props.richText
// },
}),
propsMigration({
id: arrowShapeVersions.AddRichTextAttrs,
up: (_props) => {
},
down: (props) => {
if (props.richText && "attrs" in props.richText) {
delete props.richText.attrs;
}
}
})
]
});
export {
ArrowShapeArrowheadEndStyle,
ArrowShapeArrowheadStartStyle,
ArrowShapeKindStyle,
arrowShapeMigrations,
arrowShapeProps,
arrowShapeVersions
};
//# sourceMappingURL=TLArrowShape.mjs.map