tldraw
Version:
A tiny little drawing editor.
280 lines (279 loc) • 10.5 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var EmbedShapeUtil_exports = {};
__export(EmbedShapeUtil_exports, {
EmbedShapeUtil: () => EmbedShapeUtil
});
module.exports = __toCommonJS(EmbedShapeUtil_exports);
var import_jsx_runtime = require("react/jsx-runtime");
var import_editor = require("@tldraw/editor");
var import_defaultEmbedDefinitions = require("../../defaultEmbedDefinitions");
var import_embeds = require("../../utils/embeds/embeds");
var import_BookmarkShapeUtil = require("../bookmark/BookmarkShapeUtil");
var import_bookmarks = require("../bookmark/bookmarks");
var import_rotated_box_shadow = require("../shared/rotated-box-shadow");
const getSandboxPermissions = (permissions) => {
return Object.entries(permissions).filter(([_perm, isEnabled]) => isEnabled).map(([perm]) => perm).join(" ");
};
class EmbedShapeUtil extends import_editor.BaseBoxShapeUtil {
static type = "embed";
static props = import_editor.embedShapeProps;
static migrations = import_editor.embedShapeMigrations;
static embedDefinitions = import_defaultEmbedDefinitions.DEFAULT_EMBED_DEFINITIONS;
canEditWhileLocked(shape) {
const result = this.getEmbedDefinition(shape.props.url);
if (!result) return true;
return result.definition.canEditWhileLocked ?? true;
}
static setEmbedDefinitions(embedDefinitions) {
EmbedShapeUtil.embedDefinitions = embedDefinitions;
}
getEmbedDefinitions() {
return EmbedShapeUtil.embedDefinitions;
}
getEmbedDefinition(url) {
return (0, import_embeds.getEmbedInfo)(EmbedShapeUtil.embedDefinitions, url);
}
getText(shape) {
return shape.props.url;
}
getAriaDescriptor(shape) {
const embedInfo = this.getEmbedDefinition(shape.props.url);
return embedInfo?.definition.title;
}
hideSelectionBoundsFg(shape) {
return !this.canResize(shape);
}
canEdit() {
return true;
}
canResize(shape) {
return !!this.getEmbedDefinition(shape.props.url)?.definition?.doesResize;
}
canEditInReadonly() {
return true;
}
getDefaultProps() {
return {
w: 300,
h: 300,
url: ""
};
}
getGeometry(shape) {
const embedInfo = this.getEmbedDefinition(shape.props.url);
if (!embedInfo?.definition) {
return new import_editor.Rectangle2d({
width: import_bookmarks.BOOKMARK_WIDTH,
height: import_bookmarks.BOOKMARK_JUST_URL_HEIGHT,
isFilled: true
});
}
return super.getGeometry(shape);
}
isAspectRatioLocked(shape) {
const embedInfo = this.getEmbedDefinition(shape.props.url);
return embedInfo?.definition.isAspectRatioLocked ?? false;
}
onResize(shape, info) {
const isAspectRatioLocked = this.isAspectRatioLocked(shape);
const embedInfo = this.getEmbedDefinition(shape.props.url);
let minWidth = embedInfo?.definition.minWidth ?? 200;
let minHeight = embedInfo?.definition.minHeight ?? 200;
if (isAspectRatioLocked) {
const aspectRatio = shape.props.w / shape.props.h;
if (aspectRatio > 1) {
minWidth *= aspectRatio;
} else {
minHeight /= aspectRatio;
}
}
return (0, import_editor.resizeBox)(shape, info, { minWidth, minHeight });
}
component(shape) {
const svgExport = (0, import_editor.useSvgExportContext)();
const { w, h, url } = shape.props;
const isEditing = (0, import_editor.useIsEditing)(shape.id);
const embedInfo = this.getEmbedDefinition(url);
const isHoveringWhileEditingSameShape = (0, import_editor.useValue)(
"is hovering",
() => {
const { editingShapeId, hoveredShapeId } = this.editor.getCurrentPageState();
if (editingShapeId && hoveredShapeId !== editingShapeId) {
const editingShape = this.editor.getShape(editingShapeId);
if (editingShape && this.editor.isShapeOfType(editingShape, "embed")) {
return true;
}
}
return false;
},
[]
);
const pageRotation = this.editor.getShapePageTransform(shape).rotation();
if (svgExport) {
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_editor.HTMLContainer, { className: "tl-embed-container", id: shape.id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"div",
{
className: "tl-embed",
style: {
border: 0,
boxShadow: (0, import_rotated_box_shadow.getRotatedBoxShadow)(pageRotation),
borderRadius: embedInfo?.definition.overrideOutlineRadius ?? 8,
background: embedInfo?.definition.backgroundColor ?? "var(--tl-color-background)",
width: w,
height: h
}
}
) });
}
const isInteractive = isEditing || isHoveringWhileEditingSameShape;
const isIframe = typeof window !== "undefined" && (window !== window.top || window.self !== window.parent);
if (isIframe && embedInfo?.definition.type === "tldraw") return null;
if (embedInfo?.definition.type === "github_gist") {
const idFromGistUrl = embedInfo.url.split("/").pop();
if (!idFromGistUrl) throw Error("No gist id!");
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_editor.HTMLContainer, { className: "tl-embed-container", id: shape.id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
Gist,
{
id: idFromGistUrl,
width: (0, import_editor.toDomPrecision)(w),
height: (0, import_editor.toDomPrecision)(h),
isInteractive,
pageRotation
}
) });
}
const sandbox = getSandboxPermissions({
...import_defaultEmbedDefinitions.embedShapePermissionDefaults,
...embedInfo?.definition.overridePermissions ?? {}
});
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_editor.HTMLContainer, { className: "tl-embed-container", id: shape.id, children: embedInfo?.definition ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"iframe",
{
className: "tl-embed",
sandbox,
src: embedInfo.embedUrl,
width: (0, import_editor.toDomPrecision)(w),
height: (0, import_editor.toDomPrecision)(h),
draggable: false,
frameBorder: "0",
referrerPolicy: "no-referrer-when-downgrade",
tabIndex: isEditing ? 0 : -1,
allowFullScreen: true,
style: {
border: 0,
pointerEvents: isInteractive ? "auto" : "none",
// Fix for safari <https://stackoverflow.com/a/49150908>
zIndex: isInteractive ? "" : "-1",
boxShadow: (0, import_rotated_box_shadow.getRotatedBoxShadow)(pageRotation),
borderRadius: embedInfo?.definition.overrideOutlineRadius ?? 8,
background: embedInfo?.definition.backgroundColor
}
}
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_BookmarkShapeUtil.BookmarkShapeComponent,
{
url,
h,
rotation: pageRotation,
assetId: null,
showImageContainer: false
}
) });
}
indicator(shape) {
const embedInfo = this.getEmbedDefinition(shape.props.url);
return embedInfo?.definition ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"rect",
{
width: (0, import_editor.toDomPrecision)(shape.props.w),
height: (0, import_editor.toDomPrecision)(shape.props.h),
rx: embedInfo?.definition.overrideOutlineRadius ?? 8,
ry: embedInfo?.definition.overrideOutlineRadius ?? 8
}
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_BookmarkShapeUtil.BookmarkIndicatorComponent, { w: import_bookmarks.BOOKMARK_WIDTH, h: import_bookmarks.BOOKMARK_JUST_URL_HEIGHT });
}
useLegacyIndicator() {
return false;
}
getIndicatorPath(shape) {
const path = new Path2D();
const embedInfo = this.getEmbedDefinition(shape.props.url);
if (embedInfo?.definition) {
const radius = embedInfo.definition.overrideOutlineRadius ?? 8;
path.roundRect(0, 0, shape.props.w, shape.props.h, radius);
} else {
path.roundRect(0, 0, import_bookmarks.BOOKMARK_WIDTH, import_bookmarks.BOOKMARK_JUST_URL_HEIGHT, 6);
}
return path;
}
getInterpolatedProps(startShape, endShape, t) {
return {
...t > 0.5 ? endShape.props : startShape.props,
w: (0, import_editor.lerp)(startShape.props.w, endShape.props.w, t),
h: (0, import_editor.lerp)(startShape.props.h, endShape.props.h, t)
};
}
}
function Gist({
id,
isInteractive,
width,
height,
style,
pageRotation
}) {
if (!id.match(/^[0-9a-f]+$/)) throw Error("No gist id!");
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"iframe",
{
className: "tl-embed",
draggable: false,
width: (0, import_editor.toDomPrecision)(width),
height: (0, import_editor.toDomPrecision)(height),
frameBorder: "0",
scrolling: "no",
referrerPolicy: "no-referrer-when-downgrade",
tabIndex: isInteractive ? 0 : -1,
style: {
...style,
pointerEvents: isInteractive ? "all" : "none",
// Fix for safari <https://stackoverflow.com/a/49150908>
zIndex: isInteractive ? "" : "-1",
boxShadow: (0, import_rotated_box_shadow.getRotatedBoxShadow)(pageRotation)
},
srcDoc: `
<html>
<head>
<base target="_blank">
</head>
<body>
<script src=${`https://gist.github.com/${id}.js`}></script>
<style type="text/css">
* { margin: 0px; }
table { height: 100%; background-color: red; }
.gist { background-color: none; height: 100%; }
.gist .gist-file { height: calc(100vh - 2px); padding: 0px; display: grid; grid-template-rows: 1fr auto; }
</style>
</body>
</html>`
}
);
}
//# sourceMappingURL=EmbedShapeUtil.js.map