@tldraw/tlschema
Version:
A tiny little drawing app (schema).
483 lines (482 loc) • 13.2 kB
JavaScript
import {
createMigrationIds,
createRecordMigrationSequence,
createRecordType
} from "@tldraw/store";
import { filterEntries } from "@tldraw/utils";
import { T } from "@tldraw/validate";
import { boxModelValidator } from "../misc/geometry-types.mjs";
import { idValidator } from "../misc/id-validator.mjs";
import { cursorValidator } from "../misc/TLCursor.mjs";
import { opacityValidator } from "../misc/TLOpacity.mjs";
import { scribbleValidator } from "../misc/TLScribble.mjs";
import { pageIdValidator } from "./TLPage.mjs";
const shouldKeyBePreservedBetweenSessions = {
// This object defines keys that should be preserved across calls to loadSnapshot()
id: false,
// meta
typeName: false,
// meta
currentPageId: false,
// does not preserve because who knows if the page still exists
opacityForNextShape: false,
// does not preserve because it's a temporary state
stylesForNextShape: false,
// does not preserve because it's a temporary state
followingUserId: false,
// does not preserve because it's a temporary state
highlightedUserIds: false,
// does not preserve because it's a temporary state
brush: false,
// does not preserve because it's a temporary state
cursor: false,
// does not preserve because it's a temporary state
scribbles: false,
// does not preserve because it's a temporary state
isFocusMode: true,
// preserves because it's a user preference
isDebugMode: true,
// preserves because it's a user preference
isToolLocked: true,
// preserves because it's a user preference
exportBackground: true,
// preserves because it's a user preference
screenBounds: true,
// preserves because it's capturing the user's screen state
insets: true,
// preserves because it's capturing the user's screen state
zoomBrush: false,
// does not preserve because it's a temporary state
chatMessage: false,
// does not preserve because it's a temporary state
isChatting: false,
// does not preserve because it's a temporary state
isPenMode: false,
// does not preserve because it's a temporary state
isGridMode: true,
// preserves because it's a user preference
isFocused: true,
// preserves because obviously
devicePixelRatio: true,
// preserves because it captures the user's screen state
isCoarsePointer: true,
// preserves because it captures the user's screen state
isHoveringCanvas: false,
// does not preserve because it's a temporary state
openMenus: false,
// does not preserve because it's a temporary state
isChangingStyle: false,
// does not preserve because it's a temporary state
isReadonly: true,
// preserves because it's a config option
meta: false,
// does not preserve because who knows what's in there, leave it up to sdk users to save and reinstate
duplicateProps: false
//
};
function pluckPreservingValues(val) {
return val ? filterEntries(val, (key) => {
return shouldKeyBePreservedBetweenSessions[key];
}) : null;
}
const instanceIdValidator = idValidator("instance");
function createInstanceRecordType(stylesById) {
const stylesForNextShapeValidators = {};
for (const [id, style] of stylesById) {
stylesForNextShapeValidators[id] = T.optional(style);
}
const instanceTypeValidator = T.model(
"instance",
T.object({
typeName: T.literal("instance"),
id: idValidator("instance"),
currentPageId: pageIdValidator,
followingUserId: T.string.nullable(),
brush: boxModelValidator.nullable(),
opacityForNextShape: opacityValidator,
stylesForNextShape: T.object(stylesForNextShapeValidators),
cursor: cursorValidator,
scribbles: T.arrayOf(scribbleValidator),
isFocusMode: T.boolean,
isDebugMode: T.boolean,
isToolLocked: T.boolean,
exportBackground: T.boolean,
screenBounds: boxModelValidator,
insets: T.arrayOf(T.boolean),
zoomBrush: boxModelValidator.nullable(),
isPenMode: T.boolean,
isGridMode: T.boolean,
chatMessage: T.string,
isChatting: T.boolean,
highlightedUserIds: T.arrayOf(T.string),
isFocused: T.boolean,
devicePixelRatio: T.number,
isCoarsePointer: T.boolean,
isHoveringCanvas: T.boolean.nullable(),
openMenus: T.arrayOf(T.string),
isChangingStyle: T.boolean,
isReadonly: T.boolean,
meta: T.jsonValue,
duplicateProps: T.object({
shapeIds: T.arrayOf(idValidator("shape")),
offset: T.object({
x: T.number,
y: T.number
})
}).nullable()
})
);
return createRecordType("instance", {
validator: instanceTypeValidator,
scope: "session",
ephemeralKeys: {
currentPageId: false,
meta: false,
followingUserId: true,
opacityForNextShape: true,
stylesForNextShape: true,
brush: true,
cursor: true,
scribbles: true,
isFocusMode: true,
isDebugMode: true,
isToolLocked: true,
exportBackground: true,
screenBounds: true,
insets: true,
zoomBrush: true,
isPenMode: true,
isGridMode: true,
chatMessage: true,
isChatting: true,
highlightedUserIds: true,
isFocused: true,
devicePixelRatio: true,
isCoarsePointer: true,
isHoveringCanvas: true,
openMenus: true,
isChangingStyle: true,
isReadonly: true,
duplicateProps: true
}
}).withDefaultProperties(
() => ({
followingUserId: null,
opacityForNextShape: 1,
stylesForNextShape: {},
brush: null,
scribbles: [],
cursor: {
type: "default",
rotation: 0
},
isFocusMode: false,
exportBackground: false,
isDebugMode: false,
isToolLocked: false,
screenBounds: { x: 0, y: 0, w: 1080, h: 720 },
insets: [false, false, false, false],
zoomBrush: null,
isGridMode: false,
isPenMode: false,
chatMessage: "",
isChatting: false,
highlightedUserIds: [],
isFocused: false,
devicePixelRatio: typeof window === "undefined" ? 1 : window.devicePixelRatio,
isCoarsePointer: false,
isHoveringCanvas: null,
openMenus: [],
isChangingStyle: false,
isReadonly: false,
meta: {},
duplicateProps: null
})
);
}
const instanceVersions = createMigrationIds("com.tldraw.instance", {
AddTransparentExportBgs: 1,
RemoveDialog: 2,
AddToolLockMode: 3,
RemoveExtraPropsForNextShape: 4,
AddLabelColor: 5,
AddFollowingUserId: 6,
RemoveAlignJustify: 7,
AddZoom: 8,
AddVerticalAlign: 9,
AddScribbleDelay: 10,
RemoveUserId: 11,
AddIsPenModeAndIsGridMode: 12,
HoistOpacity: 13,
AddChat: 14,
AddHighlightedUserIds: 15,
ReplacePropsForNextShapeWithStylesForNextShape: 16,
AddMeta: 17,
RemoveCursorColor: 18,
AddLonelyProperties: 19,
ReadOnlyReadonly: 20,
AddHoveringCanvas: 21,
AddScribbles: 22,
AddInset: 23,
AddDuplicateProps: 24,
RemoveCanMoveCamera: 25
});
const instanceMigrations = createRecordMigrationSequence({
sequenceId: "com.tldraw.instance",
recordType: "instance",
sequence: [
{
id: instanceVersions.AddTransparentExportBgs,
up: (instance) => {
return { ...instance, exportBackground: true };
}
},
{
id: instanceVersions.RemoveDialog,
up: ({ dialog: _, ...instance }) => {
return instance;
}
},
{
id: instanceVersions.AddToolLockMode,
up: (instance) => {
return { ...instance, isToolLocked: false };
}
},
{
id: instanceVersions.RemoveExtraPropsForNextShape,
up: ({ propsForNextShape, ...instance }) => {
return {
...instance,
propsForNextShape: Object.fromEntries(
Object.entries(propsForNextShape).filter(
([key]) => [
"color",
"labelColor",
"dash",
"fill",
"size",
"font",
"align",
"verticalAlign",
"icon",
"geo",
"arrowheadStart",
"arrowheadEnd",
"spline"
].includes(key)
)
)
};
}
},
{
id: instanceVersions.AddLabelColor,
up: ({ propsForNextShape, ...instance }) => {
return {
...instance,
propsForNextShape: {
...propsForNextShape,
labelColor: "black"
}
};
}
},
{
id: instanceVersions.AddFollowingUserId,
up: (instance) => {
return { ...instance, followingUserId: null };
}
},
{
id: instanceVersions.RemoveAlignJustify,
up: (instance) => {
let newAlign = instance.propsForNextShape.align;
if (newAlign === "justify") {
newAlign = "start";
}
return {
...instance,
propsForNextShape: {
...instance.propsForNextShape,
align: newAlign
}
};
}
},
{
id: instanceVersions.AddZoom,
up: (instance) => {
return { ...instance, zoomBrush: null };
}
},
{
id: instanceVersions.AddVerticalAlign,
up: (instance) => {
return {
...instance,
propsForNextShape: {
...instance.propsForNextShape,
verticalAlign: "middle"
}
};
}
},
{
id: instanceVersions.AddScribbleDelay,
up: (instance) => {
if (instance.scribble !== null) {
return { ...instance, scribble: { ...instance.scribble, delay: 0 } };
}
return { ...instance };
}
},
{
id: instanceVersions.RemoveUserId,
up: ({ userId: _, ...instance }) => {
return instance;
}
},
{
id: instanceVersions.AddIsPenModeAndIsGridMode,
up: (instance) => {
return { ...instance, isPenMode: false, isGridMode: false };
}
},
{
id: instanceVersions.HoistOpacity,
up: ({ propsForNextShape: { opacity, ...propsForNextShape }, ...instance }) => {
return { ...instance, opacityForNextShape: Number(opacity ?? "1"), propsForNextShape };
}
},
{
id: instanceVersions.AddChat,
up: (instance) => {
return { ...instance, chatMessage: "", isChatting: false };
}
},
{
id: instanceVersions.AddHighlightedUserIds,
up: (instance) => {
return { ...instance, highlightedUserIds: [] };
}
},
{
id: instanceVersions.ReplacePropsForNextShapeWithStylesForNextShape,
up: ({ propsForNextShape: _, ...instance }) => {
return { ...instance, stylesForNextShape: {} };
}
},
{
id: instanceVersions.AddMeta,
up: (record) => {
return {
...record,
meta: {}
};
}
},
{
id: instanceVersions.RemoveCursorColor,
up: (record) => {
const { color: _, ...cursor } = record.cursor;
return {
...record,
cursor
};
}
},
{
id: instanceVersions.AddLonelyProperties,
up: (record) => {
return {
...record,
canMoveCamera: true,
isFocused: false,
devicePixelRatio: 1,
isCoarsePointer: false,
openMenus: [],
isChangingStyle: false,
isReadOnly: false
};
}
},
{
id: instanceVersions.ReadOnlyReadonly,
up: ({ isReadOnly: _isReadOnly, ...record }) => {
return {
...record,
isReadonly: _isReadOnly
};
}
},
{
id: instanceVersions.AddHoveringCanvas,
up: (record) => {
return {
...record,
isHoveringCanvas: null
};
}
},
{
id: instanceVersions.AddScribbles,
up: ({ scribble: _, ...record }) => {
return {
...record,
scribbles: []
};
}
},
{
id: instanceVersions.AddInset,
up: (record) => {
return {
...record,
insets: [false, false, false, false]
};
},
down: ({ insets: _, ...record }) => {
return {
...record
};
}
},
{
id: instanceVersions.AddDuplicateProps,
up: (record) => {
return {
...record,
duplicateProps: null
};
},
down: ({ duplicateProps: _, ...record }) => {
return {
...record
};
}
},
{
id: instanceVersions.RemoveCanMoveCamera,
up: ({ canMoveCamera: _, ...record }) => {
return {
...record
};
},
down: (instance) => {
return { ...instance, canMoveCamera: true };
}
}
]
});
const TLINSTANCE_ID = "instance:instance";
export {
TLINSTANCE_ID,
createInstanceRecordType,
instanceIdValidator,
instanceMigrations,
instanceVersions,
pluckPreservingValues,
shouldKeyBePreservedBetweenSessions
};
//# sourceMappingURL=TLInstance.mjs.map