UNPKG

tldraw

Version:

A tiny little drawing editor.

283 lines (282 loc) • 10.2 kB
"use strict"; 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"); class HighlightShapeUtil extends import_editor.ShapeUtil { static type = "highlight"; static props = import_editor.highlightShapeProps; static migrations = import_editor.highlightShapeMigrations; options = { maxPointsPerShape: 600, underlayOpacity: 0.82, overlayOpacity: 0.35 }; 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, scaleX: 1, scaleY: 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: this.options.overlayOpacity } ) }); } 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: this.options.underlayOpacity } ) }); } indicator(shape) { const forceSolid = useHighlightForceSolid(this.editor, shape); const strokeWidth = getStrokeWidth(shape); const { strokePoints, sw } = getHighlightStrokePoints(shape, strokeWidth, forceSolid); const allPointsFromSegments = (0, import_getPath.getPointsFromDrawSegments)( shape.props.segments, shape.props.scaleX, shape.props.scaleY ); 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 }); } useLegacyIndicator() { return false; } getIndicatorPath(shape) { const strokeWidth = getStrokeWidth(shape); const zoomLevel = this.editor.getEfficientZoomLevel(); const forceSolid = strokeWidth / zoomLevel < 1.5; const { strokePoints, sw } = getHighlightStrokePoints(shape, strokeWidth, forceSolid); const allPointsFromSegments = (0, import_getPath.getPointsFromDrawSegments)( shape.props.segments, shape.props.scaleX, shape.props.scaleY ); let strokePath; if (strokePoints.length < 2) { strokePath = getIndicatorDot(allPointsFromSegments[0], sw); } else { strokePath = (0, import_svg.getSvgPathFromStrokePoints)(strokePoints, false); } return new Path2D(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: this.options.overlayOpacity } ) }); } 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: this.options.underlayOpacity } ) }); } onResize(shape, info) { const newScaleX = info.scaleX * shape.props.scaleX; const newScaleY = info.scaleY * shape.props.scaleY; if (newScaleX === 0 || newScaleY === 0) return; return { props: { scaleX: newScaleX, scaleY: newScaleY } }; } 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.getPointsFromDrawSegments)( shape.props.segments, shape.props.scaleX, shape.props.scaleY ); 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].path.length < 24; } function HighlightRenderer({ strokeWidth, forceSolid, shape, opacity }) { const theme = (0, import_useDefaultColorTheme.useDefaultColorTheme)(); const allPointsFromSegments = (0, import_getPath.getPointsFromDrawSegments)( shape.props.segments, shape.props.scaleX, shape.props.scaleY ); 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(allPointsFromSegments[0]); const colorSpace = (0, import_useColorSpace.useColorSpace)(); const color = (0, import_editor.getColorValue)( theme, shape.props.color, colorSpace === "p3" ? "highlightP3" : "highlightSrgb" ); 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.getEfficientZoomLevel(); if (sw / zoomLevel < 1.5) { return true; } return false; }, [editor] ); } //# sourceMappingURL=HighlightShapeUtil.js.map