@tldraw/tlschema
Version:
tldraw infinite canvas SDK (schema).
213 lines (212 loc) • 5.93 kB
JavaScript
import { createRecordMigrationSequence } from "@tldraw/store";
import { T } from "@tldraw/validate";
import { idValidator } from "../misc/id-validator.mjs";
import { richTextValidator } from "../misc/TLRichText.mjs";
import { createCustomRecordId, isCustomRecordId } from "./TLCustomRecord.mjs";
const emojiValidator = T.string.check((value) => {
if (value.length === 0 || value.length > 64) {
throw new T.ValidationError(`Expected an emoji of 1-64 characters, got ${value.length}`);
}
});
const commentAnchorValidator = T.union("type", {
shape: T.object({
type: T.literal("shape"),
shapeId: idValidator("shape"),
x: T.number,
y: T.number,
isPrecise: T.boolean
}),
point: T.object({
type: T.literal("point"),
x: T.number,
y: T.number
}),
region: T.object({
type: T.literal("region"),
x: T.number,
y: T.number,
w: T.number,
h: T.number,
pinX: T.number.optional(),
pinY: T.number.optional()
}),
page: T.object({
type: T.literal("page")
})
});
function createCommentGuardMigrations(typeName, extra = []) {
return createRecordMigrationSequence({
sequenceId: `com.tldraw.${typeName}`,
recordType: typeName,
retroactive: true,
sequence: [
{
id: `com.tldraw.${typeName}/1`,
up: (record) => record
},
...extra
]
});
}
const commentThreadRecordConfig = {
scope: "document",
migrations: createCommentGuardMigrations("comment-thread", [
{
// Shape anchors gained normalized x/y + isPrecise; existing ones were imprecise (top-right).
id: "com.tldraw.comment-thread/2",
up: (record) => {
const anchor = record.anchor;
if (anchor?.type === "shape" && anchor.x === void 0) {
;
record.anchor = { ...anchor, x: 1, y: 0, isPrecise: false };
}
return record;
},
down: (record) => {
const anchor = record.anchor;
if (anchor?.type === "shape") {
;
record.anchor = { type: "shape", shapeId: anchor.shapeId };
}
return record;
}
},
{
// Region anchors gained an optional pin corner (where the creating drag released).
id: "com.tldraw.comment-thread/3",
up: (record) => record,
down: (record) => {
const anchor = record.anchor;
if (anchor?.type === "region") {
const { pinX: _pinX, pinY: _pinY, ...rest } = anchor;
record.anchor = rest;
}
return record;
}
}
]),
validator: T.object({
id: idValidator("comment-thread"),
typeName: T.literal("comment-thread"),
pageId: idValidator("page"),
anchor: commentAnchorValidator,
createdBy: T.string,
createdAt: T.number,
resolved: T.object({ at: T.number, by: T.string }).nullable(),
isDeleted: T.boolean,
meta: T.jsonValue
})
};
const commentRecordConfig = {
scope: "document",
migrations: createCommentGuardMigrations("comment"),
validator: T.object({
id: idValidator("comment"),
typeName: T.literal("comment"),
threadId: idValidator("comment-thread"),
pageId: idValidator("page"),
authorId: T.string,
createdAt: T.number,
editedAt: T.number.nullable(),
body: richTextValidator,
isDeleted: T.boolean,
meta: T.jsonValue
})
};
const commentReactionRecordConfig = {
scope: "document",
migrations: createCommentGuardMigrations("comment-reaction"),
validator: T.object({
id: idValidator("comment-reaction"),
typeName: T.literal("comment-reaction"),
commentId: idValidator("comment"),
threadId: idValidator("comment-thread"),
pageId: idValidator("page"),
userId: T.string,
emoji: emojiValidator,
createdAt: T.number,
meta: T.jsonValue
})
};
const commentSchemaRecords = {
"comment-thread": commentThreadRecordConfig,
comment: commentRecordConfig,
"comment-reaction": commentReactionRecordConfig
};
function createCommentThreadId(id) {
return createCustomRecordId("comment-thread", id);
}
function createCommentId(id) {
return createCustomRecordId("comment", id);
}
function createCommentReactionId(commentId, userId, emoji) {
return createCustomRecordId(
"comment-reaction",
`${encodeURIComponent(commentId)}:${encodeURIComponent(userId)}:${encodeURIComponent(emoji)}`
);
}
function isCommentThreadId(id) {
return isCustomRecordId("comment-thread", id);
}
function isCommentId(id) {
return isCustomRecordId("comment", id);
}
function isCommentReactionId(id) {
return isCustomRecordId("comment-reaction", id);
}
function createCommentThread(props) {
return {
id: createCommentThreadId(),
typeName: "comment-thread",
pageId: props.pageId,
anchor: props.anchor,
createdBy: props.createdBy,
createdAt: props.now ?? Date.now(),
resolved: null,
isDeleted: false,
meta: props.meta ?? {}
};
}
function createComment(props) {
return {
id: createCommentId(),
typeName: "comment",
threadId: props.threadId,
pageId: props.pageId,
authorId: props.authorId,
createdAt: props.now ?? Date.now(),
editedAt: null,
body: props.body,
isDeleted: false,
meta: props.meta ?? {}
};
}
function createCommentReaction(props) {
return {
id: createCommentReactionId(props.commentId, props.userId, props.emoji),
typeName: "comment-reaction",
commentId: props.commentId,
threadId: props.threadId,
pageId: props.pageId,
userId: props.userId,
emoji: props.emoji,
createdAt: props.now ?? Date.now(),
meta: props.meta ?? {}
};
}
export {
commentReactionRecordConfig,
commentRecordConfig,
commentSchemaRecords,
commentThreadRecordConfig,
createComment,
createCommentId,
createCommentReaction,
createCommentReactionId,
createCommentThread,
createCommentThreadId,
isCommentId,
isCommentReactionId,
isCommentThreadId
};
//# sourceMappingURL=TLComment.mjs.map