tldraw
Version:
A tiny little drawing editor.
251 lines (250 loc) • 9.16 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 HighlightShapeUtil_exports = {};
__export(HighlightShapeUtil_exports, {
HighlightShapeUtil: () => HighlightShapeUtil
});
module.exports = __toCommonJS(HighlightShapeUtil_exports);
var import_jsx_runtime = require("react/jsx-runtime");
var import_editor = require("@tldraw/editor");
var import_getPath = require("../draw/getPath");
var import_default_shape_constants = require("../shared/default-shape-constants");
var import_getStrokeOutlinePoints = require("../shared/freehand/getStrokeOutlinePoints");
var import_getStrokePoints = require("../shared/freehand/getStrokePoints");
var import_setStrokePointRadii = require("../shared/freehand/setStrokePointRadii");
var import_svg = require("../shared/freehand/svg");
var import_interpolate_props = require("../shared/interpolate-props");
var import_useColorSpace = require("../shared/useColorSpace");
var import_useDefaultColorTheme = require("../shared/useDefaultColorTheme");
const OVERLAY_OPACITY = 0.35;
const UNDERLAY_OPACITY = 0.82;
class HighlightShapeUtil extends import_editor.ShapeUtil {
static type = "highlight";
static props = import_editor.highlightShapeProps;
static migrations = import_editor.highlightShapeMigrations;
hideResizeHandles(shape) {
return getIsDot(shape);
}
hideRotateHandle(shape) {
return getIsDot(shape);
}
hideSelectionBoundsFg(shape) {
return getIsDot(shape);
}
getDefaultProps() {
return {
segments: [],
color: "black",
size: "m",
isComplete: false,
isPen: false,
scale: 1
};
}
getGeometry(shape) {
const strokeWidth = getStrokeWidth(shape);
if (getIsDot(shape)) {
return new import_editor.Circle2d({
x: -strokeWidth / 2,
y: -strokeWidth / 2,
radius: strokeWidth / 2,
isFilled: true
});
}
const { strokePoints, sw } = getHighlightStrokePoints(shape, strokeWidth, true);
const opts = (0, import_getPath.getHighlightFreehandSettings)({ strokeWidth: sw, showAsComplete: true });
(0, import_setStrokePointRadii.setStrokePointRadii)(strokePoints, opts);
return new import_editor.Polygon2d({
points: (0, import_getStrokeOutlinePoints.getStrokeOutlinePoints)(strokePoints, opts),
isFilled: true
});
}
component(shape) {
const forceSolid = useHighlightForceSolid(this.editor, shape);
const strokeWidth = getStrokeWidth(shape);
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_editor.SVGContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
HighlightRenderer,
{
shape,
forceSolid,
strokeWidth,
opacity: OVERLAY_OPACITY
}
) });
}
backgroundComponent(shape) {
const forceSolid = useHighlightForceSolid(this.editor, shape);
const strokeWidth = getStrokeWidth(shape);
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_editor.SVGContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
HighlightRenderer,
{
shape,
forceSolid,
strokeWidth,
opacity: UNDERLAY_OPACITY
}
) });
}
indicator(shape) {
const forceSolid = useHighlightForceSolid(this.editor, shape);
const strokeWidth = getStrokeWidth(shape);
const { strokePoints, sw } = getHighlightStrokePoints(shape, strokeWidth, forceSolid);
const allPointsFromSegments = (0, import_getPath.getPointsFromSegments)(shape.props.segments);
let strokePath;
if (strokePoints.length < 2) {
strokePath = getIndicatorDot(allPointsFromSegments[0], sw);
} else {
strokePath = (0, import_svg.getSvgPathFromStrokePoints)(strokePoints, false);
}
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: strokePath });
}
toSvg(shape) {
const strokeWidth = getStrokeWidth(shape);
const forceSolid = strokeWidth < 1.5;
const scaleFactor = 1 / shape.props.scale;
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { transform: `scale(${scaleFactor})`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
HighlightRenderer,
{
forceSolid,
strokeWidth,
shape,
opacity: OVERLAY_OPACITY
}
) });
}
toBackgroundSvg(shape) {
const strokeWidth = getStrokeWidth(shape);
const forceSolid = strokeWidth < 1.5;
const scaleFactor = 1 / shape.props.scale;
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { transform: `scale(${scaleFactor})`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
HighlightRenderer,
{
forceSolid,
strokeWidth,
shape,
opacity: UNDERLAY_OPACITY
}
) });
}
onResize(shape, info) {
const { scaleX, scaleY } = info;
const newSegments = [];
for (const segment of shape.props.segments) {
newSegments.push({
...segment,
points: segment.points.map(({ x, y, z }) => {
return {
x: scaleX * x,
y: scaleY * y,
z
};
})
});
}
return {
props: {
segments: newSegments
}
};
}
getInterpolatedProps(startShape, endShape, t) {
return {
...t > 0.5 ? endShape.props : startShape.props,
...endShape.props,
segments: (0, import_interpolate_props.interpolateSegments)(startShape.props.segments, endShape.props.segments, t),
scale: (0, import_editor.lerp)(startShape.props.scale, endShape.props.scale, t)
};
}
}
function getShapeDot(point) {
const r = 0.1;
return `M ${point.x} ${point.y} m -${r}, 0 a ${r},${r} 0 1,0 ${r * 2},0 a ${r},${r} 0 1,0 -${r * 2},0`;
}
function getIndicatorDot(point, sw) {
const r = sw / 2;
return `M ${point.x} ${point.y} m -${r}, 0 a ${r},${r} 0 1,0 ${r * 2},0 a ${r},${r} 0 1,0 -${r * 2},0`;
}
function getHighlightStrokePoints(shape, strokeWidth, forceSolid) {
const allPointsFromSegments = (0, import_getPath.getPointsFromSegments)(shape.props.segments);
const showAsComplete = shape.props.isComplete || (0, import_editor.last)(shape.props.segments)?.type === "straight";
let sw = strokeWidth;
if (!forceSolid && !shape.props.isPen && allPointsFromSegments.length === 1) {
sw += (0, import_editor.rng)(shape.id)() * (strokeWidth / 6);
}
const options = (0, import_getPath.getHighlightFreehandSettings)({
strokeWidth: sw,
showAsComplete
});
const strokePoints = (0, import_getStrokePoints.getStrokePoints)(allPointsFromSegments, options);
return { strokePoints, sw };
}
function getStrokeWidth(shape) {
return import_default_shape_constants.FONT_SIZES[shape.props.size] * 1.12 * shape.props.scale;
}
function getIsDot(shape) {
return shape.props.segments.length === 1 && shape.props.segments[0].points.length < 2;
}
function HighlightRenderer({
strokeWidth,
forceSolid,
shape,
opacity
}) {
const theme = (0, import_useDefaultColorTheme.useDefaultColorTheme)();
const allPointsFromSegments = (0, import_getPath.getPointsFromSegments)(shape.props.segments);
let sw = strokeWidth;
if (!forceSolid && !shape.props.isPen && allPointsFromSegments.length === 1) {
sw += (0, import_editor.rng)(shape.id)() * (sw / 6);
}
const options = (0, import_getPath.getHighlightFreehandSettings)({
strokeWidth: sw,
showAsComplete: shape.props.isComplete || (0, import_editor.last)(shape.props.segments)?.type === "straight"
});
const strokePoints = (0, import_getStrokePoints.getStrokePoints)(allPointsFromSegments, options);
const solidStrokePath = strokePoints.length > 1 ? (0, import_svg.getSvgPathFromStrokePoints)(strokePoints, false) : getShapeDot(shape.props.segments[0].points[0]);
const colorSpace = (0, import_useColorSpace.useColorSpace)();
const color = theme[shape.props.color].highlight[colorSpace];
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"path",
{
d: solidStrokePath,
strokeLinecap: "round",
fill: "none",
pointerEvents: "all",
stroke: color,
strokeWidth: sw,
opacity
}
);
}
function useHighlightForceSolid(editor, shape) {
return (0, import_editor.useValue)(
"forceSolid",
() => {
const sw = getStrokeWidth(shape);
const zoomLevel = editor.getZoomLevel();
if (sw / zoomLevel < 1.5) {
return true;
}
return false;
},
[editor]
);
}
//# sourceMappingURL=HighlightShapeUtil.js.map