UNPKG

tldraw

Version:

A tiny little drawing editor.

809 lines (808 loc) • 28.2 kB
import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import { Arc2d, Box, Edge2d, Group2d, Rectangle2d, SVGContainer, ShapeUtil, Vec, WeakCache, arrowShapeMigrations, arrowShapeProps, getDefaultColorTheme, getPerfectDashProps, lerp, mapObjectMapValues, maybeSnapToGrid, structuredClone, toDomPrecision, track, useEditor, useIsEditing, useSharedSafeId, useValue } from "@tldraw/editor"; import { updateArrowTerminal } from "../../bindings/arrow/ArrowBindingUtil.mjs"; import { ShapeFill } from "../shared/ShapeFill.mjs"; import { SvgTextLabel } from "../shared/SvgTextLabel.mjs"; import { TextLabel } from "../shared/TextLabel.mjs"; import { ARROW_LABEL_PADDING, STROKE_SIZES, TEXT_PROPS } from "../shared/default-shape-constants.mjs"; import { getFillDefForCanvas, getFillDefForExport, getFontDefForExport } from "../shared/defaultStyleDefs.mjs"; import { useDefaultColorTheme } from "../shared/useDefaultColorTheme.mjs"; import { getArrowLabelFontSize, getArrowLabelPosition } from "./arrowLabel.mjs"; import { getArrowheadPathForType } from "./arrowheads.mjs"; import { getCurvedArrowHandlePath, getSolidCurvedArrowPath, getSolidStraightArrowPath, getStraightArrowHandlePath } from "./arrowpaths.mjs"; import { createOrUpdateArrowBinding, getArrowBindings, getArrowInfo, getArrowTerminalsInArrowSpace, removeArrowBinding } from "./shared.mjs"; var ARROW_HANDLES = /* @__PURE__ */ ((ARROW_HANDLES2) => { ARROW_HANDLES2["START"] = "start"; ARROW_HANDLES2["MIDDLE"] = "middle"; ARROW_HANDLES2["END"] = "end"; return ARROW_HANDLES2; })(ARROW_HANDLES || {}); class ArrowShapeUtil extends ShapeUtil { static type = "arrow"; static props = arrowShapeProps; static migrations = arrowShapeMigrations; canEdit() { return true; } canBind({ toShapeType }) { return toShapeType !== "arrow"; } canSnap() { return false; } hideResizeHandles() { return true; } hideRotateHandle() { return true; } hideSelectionBoundsBg() { return true; } hideSelectionBoundsFg() { return true; } canBeLaidOut(shape) { const bindings = getArrowBindings(this.editor, shape); return !bindings.start && !bindings.end; } getDefaultProps() { return { dash: "draw", size: "m", fill: "none", color: "black", labelColor: "black", bend: 0, start: { x: 0, y: 0 }, end: { x: 2, y: 0 }, arrowheadStart: "none", arrowheadEnd: "arrow", text: "", labelPosition: 0.5, font: "draw", scale: 1 }; } getGeometry(shape) { const info = getArrowInfo(this.editor, shape); const debugGeom = []; const bodyGeom = info.isStraight ? new Edge2d({ start: Vec.From(info.start.point), end: Vec.From(info.end.point) }) : new Arc2d({ center: Vec.Cast(info.handleArc.center), start: Vec.Cast(info.start.point), end: Vec.Cast(info.end.point), sweepFlag: info.bodyArc.sweepFlag, largeArcFlag: info.bodyArc.largeArcFlag }); let labelGeom; if (shape.props.text.trim()) { const labelPosition = getArrowLabelPosition(this.editor, shape); debugGeom.push(...labelPosition.debugGeom); labelGeom = new Rectangle2d({ x: labelPosition.box.x, y: labelPosition.box.y, width: labelPosition.box.w, height: labelPosition.box.h, isFilled: true, isLabel: true }); } return new Group2d({ children: [...(labelGeom ? [bodyGeom, labelGeom] : [bodyGeom]), ...debugGeom] }); } getHandles(shape) { const info = getArrowInfo(this.editor, shape); return [ { id: "start" /* START */, type: "vertex", index: "a0", x: info.start.handle.x, y: info.start.handle.y }, { id: "middle" /* MIDDLE */, type: "virtual", index: "a2", x: info.middle.x, y: info.middle.y }, { id: "end" /* END */, type: "vertex", index: "a3", x: info.end.handle.x, y: info.end.handle.y } ].filter(Boolean); } getText(shape) { return shape.props.text; } onHandleDrag(shape, { handle, isPrecise }) { const handleId = handle.id; const bindings = getArrowBindings(this.editor, shape); if (handleId === "middle" /* MIDDLE */) { const { start, end } = getArrowTerminalsInArrowSpace(this.editor, shape, bindings); const delta = Vec.Sub(end, start); const v = Vec.Per(delta); const med = Vec.Med(end, start); const A = Vec.Sub(med, v); const B = Vec.Add(med, v); const point2 = Vec.NearestPointOnLineSegment(A, B, handle, false); let bend = Vec.Dist(point2, med); if (Vec.Clockwise(point2, end, med)) bend *= -1; return { id: shape.id, type: shape.type, props: { bend } }; } const update = { id: shape.id, type: "arrow", props: {} }; const currentBinding = bindings[handleId]; const otherHandleId = handleId === "start" /* START */ ? "end" /* END */ : "start" /* START */; const otherBinding = bindings[otherHandleId]; if (this.editor.inputs.ctrlKey) { removeArrowBinding(this.editor, shape, handleId); update.props[handleId] = { x: handle.x, y: handle.y }; return update; } const point = this.editor.getShapePageTransform(shape.id).applyToPoint(handle); const target = this.editor.getShapeAtPoint(point, { hitInside: true, hitFrameInside: true, margin: 0, filter: (targetShape) => { return !targetShape.isLocked && this.editor.canBindShapes({ fromShape: shape, toShape: targetShape, binding: "arrow" }); } }); if (!target) { removeArrowBinding(this.editor, shape, handleId); const newPoint = maybeSnapToGrid(new Vec(handle.x, handle.y), this.editor); update.props[handleId] = { x: newPoint.x, y: newPoint.y }; return update; } const targetGeometry = this.editor.getShapeGeometry(target); const targetBounds = Box.ZeroFix(targetGeometry.bounds); const pageTransform = this.editor.getShapePageTransform(update.id); const pointInPageSpace = pageTransform.applyToPoint(handle); const pointInTargetSpace = this.editor.getPointInShapeSpace(target, pointInPageSpace); let precise = isPrecise; if (!precise) { if (!currentBinding || currentBinding && target.id !== currentBinding.toId) { precise = this.editor.inputs.pointerVelocity.len() < 0.5; } } if (!isPrecise) { if (!targetGeometry.isClosed) { precise = true; } if (otherBinding && target.id === otherBinding.toId && otherBinding.props.isPrecise) { precise = true; } } const normalizedAnchor = { x: (pointInTargetSpace.x - targetBounds.minX) / targetBounds.width, y: (pointInTargetSpace.y - targetBounds.minY) / targetBounds.height }; if (precise) { if (Vec.Dist(pointInTargetSpace, targetBounds.center) < Math.max(4, Math.min(Math.min(targetBounds.width, targetBounds.height) * 0.15, 16)) / this.editor.getZoomLevel()) { normalizedAnchor.x = 0.5; normalizedAnchor.y = 0.5; } } const b = { terminal: handleId, normalizedAnchor, isPrecise: precise, isExact: this.editor.inputs.altKey }; createOrUpdateArrowBinding(this.editor, shape, target.id, b); this.editor.setHintingShapes([target.id]); const newBindings = getArrowBindings(this.editor, shape); if (newBindings.start && newBindings.end && newBindings.start.toId === newBindings.end.toId) { if (Vec.Equals(newBindings.start.props.normalizedAnchor, newBindings.end.props.normalizedAnchor)) { createOrUpdateArrowBinding(this.editor, shape, newBindings.end.toId, { ...newBindings.end.props, normalizedAnchor: { x: newBindings.end.props.normalizedAnchor.x + 0.05, y: newBindings.end.props.normalizedAnchor.y } }); } } return update; } onTranslateStart(shape) { const bindings = getArrowBindings(this.editor, shape); const terminalsInArrowSpace = getArrowTerminalsInArrowSpace(this.editor, shape, bindings); const shapePageTransform = this.editor.getShapePageTransform(shape.id); const selectedShapeIds = this.editor.getSelectedShapeIds(); if (bindings.start && (selectedShapeIds.includes(bindings.start.toId) || this.editor.isAncestorSelected(bindings.start.toId)) || bindings.end && (selectedShapeIds.includes(bindings.end.toId) || this.editor.isAncestorSelected(bindings.end.toId))) { return; } shapeAtTranslationStart.set(shape, { pagePosition: shapePageTransform.applyToPoint(shape), terminalBindings: mapObjectMapValues(terminalsInArrowSpace, (terminalName, point) => { const binding = bindings[terminalName]; if (!binding) return null; return { binding, shapePosition: point, pagePosition: shapePageTransform.applyToPoint(point) }; }) }); if (bindings.start) { updateArrowTerminal({ editor: this.editor, arrow: shape, terminal: "start", useHandle: true }); shape = this.editor.getShape(shape.id); } if (bindings.end) { updateArrowTerminal({ editor: this.editor, arrow: shape, terminal: "end", useHandle: true }); } for (const handleName of ["start" /* START */, "end" /* END */]) { const binding = bindings[handleName]; if (!binding) continue; this.editor.updateBinding({ ...binding, props: { ...binding.props, isPrecise: true } }); } return; } onTranslate(initialShape, shape) { const atTranslationStart = shapeAtTranslationStart.get(initialShape); if (!atTranslationStart) return; const shapePageTransform = this.editor.getShapePageTransform(shape.id); const pageDelta = Vec.Sub( shapePageTransform.applyToPoint(shape), atTranslationStart.pagePosition ); for (const terminalBinding of Object.values(atTranslationStart.terminalBindings)) { if (!terminalBinding) continue; const newPagePoint = Vec.Add(terminalBinding.pagePosition, Vec.Mul(pageDelta, 0.5)); const newTarget = this.editor.getShapeAtPoint(newPagePoint, { hitInside: true, hitFrameInside: true, margin: 0, filter: (targetShape) => { return !targetShape.isLocked && this.editor.canBindShapes({ fromShape: shape, toShape: targetShape, binding: "arrow" }); } }); if (newTarget?.id === terminalBinding.binding.toId) { const targetBounds = Box.ZeroFix(this.editor.getShapeGeometry(newTarget).bounds); const pointInTargetSpace = this.editor.getPointInShapeSpace(newTarget, newPagePoint); const normalizedAnchor = { x: (pointInTargetSpace.x - targetBounds.minX) / targetBounds.width, y: (pointInTargetSpace.y - targetBounds.minY) / targetBounds.height }; createOrUpdateArrowBinding(this.editor, shape, newTarget.id, { ...terminalBinding.binding.props, normalizedAnchor, isPrecise: true }); } else { removeArrowBinding(this.editor, shape, terminalBinding.binding.props.terminal); } } } _resizeInitialBindings = new WeakCache(); onResize(shape, info) { const { scaleX, scaleY } = info; const bindings = this._resizeInitialBindings.get( shape, () => getArrowBindings(this.editor, shape) ); const terminals = getArrowTerminalsInArrowSpace(this.editor, shape, bindings); const { start, end } = structuredClone(shape.props); let { bend } = shape.props; if (!bindings.start) { start.x = terminals.start.x * scaleX; start.y = terminals.start.y * scaleY; } if (!bindings.end) { end.x = terminals.end.x * scaleX; end.y = terminals.end.y * scaleY; } const mx = Math.abs(scaleX); const my = Math.abs(scaleY); const startNormalizedAnchor = bindings?.start ? Vec.From(bindings.start.props.normalizedAnchor) : null; const endNormalizedAnchor = bindings?.end ? Vec.From(bindings.end.props.normalizedAnchor) : null; if (scaleX < 0 && scaleY >= 0) { if (bend !== 0) { bend *= -1; bend *= Math.max(mx, my); } if (startNormalizedAnchor) { startNormalizedAnchor.x = 1 - startNormalizedAnchor.x; } if (endNormalizedAnchor) { endNormalizedAnchor.x = 1 - endNormalizedAnchor.x; } } else if (scaleX >= 0 && scaleY < 0) { if (bend !== 0) { bend *= -1; bend *= Math.max(mx, my); } if (startNormalizedAnchor) { startNormalizedAnchor.y = 1 - startNormalizedAnchor.y; } if (endNormalizedAnchor) { endNormalizedAnchor.y = 1 - endNormalizedAnchor.y; } } else if (scaleX >= 0 && scaleY >= 0) { if (bend !== 0) { bend *= Math.max(mx, my); } } else if (scaleX < 0 && scaleY < 0) { if (bend !== 0) { bend *= Math.max(mx, my); } if (startNormalizedAnchor) { startNormalizedAnchor.x = 1 - startNormalizedAnchor.x; startNormalizedAnchor.y = 1 - startNormalizedAnchor.y; } if (endNormalizedAnchor) { endNormalizedAnchor.x = 1 - endNormalizedAnchor.x; endNormalizedAnchor.y = 1 - endNormalizedAnchor.y; } } if (bindings.start && startNormalizedAnchor) { createOrUpdateArrowBinding(this.editor, shape, bindings.start.toId, { ...bindings.start.props, normalizedAnchor: startNormalizedAnchor.toJson() }); } if (bindings.end && endNormalizedAnchor) { createOrUpdateArrowBinding(this.editor, shape, bindings.end.toId, { ...bindings.end.props, normalizedAnchor: endNormalizedAnchor.toJson() }); } const next = { props: { start, end, bend } }; return next; } onDoubleClickHandle(shape, handle) { switch (handle.id) { case "start" /* START */: { return { id: shape.id, type: shape.type, props: { ...shape.props, arrowheadStart: shape.props.arrowheadStart === "none" ? "arrow" : "none" } }; } case "end" /* END */: { return { id: shape.id, type: shape.type, props: { ...shape.props, arrowheadEnd: shape.props.arrowheadEnd === "none" ? "arrow" : "none" } }; } } } component(shape) { const theme = useDefaultColorTheme(); const onlySelectedShape = this.editor.getOnlySelectedShape(); const shouldDisplayHandles = this.editor.isInAny( "select.idle", "select.pointing_handle", "select.dragging_handle", "select.translating", "arrow.dragging" ) && !this.editor.getIsReadonly(); const info = getArrowInfo(this.editor, shape); if (!info?.isValid) return null; const labelPosition = getArrowLabelPosition(this.editor, shape); const isSelected = shape.id === this.editor.getOnlySelectedShapeId(); const isEditing = this.editor.getEditingShapeId() === shape.id; const showArrowLabel = isEditing || shape.props.text; return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx(SVGContainer, { style: { minWidth: 50, minHeight: 50 }, children: /* @__PURE__ */ jsx( ArrowSvg, { shape, shouldDisplayHandles: shouldDisplayHandles && onlySelectedShape?.id === shape.id } ) }), showArrowLabel && /* @__PURE__ */ jsx( TextLabel, { shapeId: shape.id, classNamePrefix: "tl-arrow", type: "arrow", font: shape.props.font, fontSize: getArrowLabelFontSize(shape), lineHeight: TEXT_PROPS.lineHeight, align: "middle", verticalAlign: "middle", text: shape.props.text, labelColor: theme[shape.props.labelColor].solid, textWidth: labelPosition.box.w - ARROW_LABEL_PADDING * 2 * shape.props.scale, isSelected, padding: 0, style: { transform: `translate(${labelPosition.box.center.x}px, ${labelPosition.box.center.y}px)` } } ) ] }); } indicator(shape) { const isEditing = useIsEditing(shape.id); const clipPathId = useSharedSafeId(shape.id + "_clip"); const info = getArrowInfo(this.editor, shape); if (!info) return null; const { start, end } = getArrowTerminalsInArrowSpace(this.editor, shape, info?.bindings); const geometry = this.editor.getShapeGeometry(shape); const bounds = geometry.bounds; const labelGeometry = shape.props.text.trim() ? geometry.children[1] : null; if (Vec.Equals(start, end)) return null; const strokeWidth = STROKE_SIZES[shape.props.size] * shape.props.scale; const as = info.start.arrowhead && getArrowheadPathForType(info, "start", strokeWidth); const ae = info.end.arrowhead && getArrowheadPathForType(info, "end", strokeWidth); const path = info.isStraight ? getSolidStraightArrowPath(info) : getSolidCurvedArrowPath(info); const includeClipPath = as && info.start.arrowhead !== "arrow" || ae && info.end.arrowhead !== "arrow" || !!labelGeometry; if (isEditing && labelGeometry) { return /* @__PURE__ */ jsx( "rect", { x: toDomPrecision(labelGeometry.x), y: toDomPrecision(labelGeometry.y), width: labelGeometry.w, height: labelGeometry.h, rx: 3.5 * shape.props.scale, ry: 3.5 * shape.props.scale } ); } const clipStartArrowhead = !(info.start.arrowhead === "none" || info.start.arrowhead === "arrow"); const clipEndArrowhead = !(info.end.arrowhead === "none" || info.end.arrowhead === "arrow"); return /* @__PURE__ */ jsxs("g", { children: [ includeClipPath && /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx( ArrowClipPath, { hasText: shape.props.text.trim().length > 0, bounds, labelBounds: labelGeometry ? labelGeometry.getBounds() : new Box(0, 0, 0, 0), as: clipStartArrowhead && as ? as : "", ae: clipEndArrowhead && ae ? ae : "" } ) }), /* @__PURE__ */ jsxs( "g", { style: { clipPath: includeClipPath ? `url(#${clipPathId})` : void 0, WebkitClipPath: includeClipPath ? `url(#${clipPathId})` : void 0 }, children: [ includeClipPath && /* @__PURE__ */ jsx( "rect", { x: bounds.minX - 100, y: bounds.minY - 100, width: bounds.width + 200, height: bounds.height + 200, opacity: 0 } ), /* @__PURE__ */ jsx("path", { d: path }) ] } ), as && /* @__PURE__ */ jsx("path", { d: as }), ae && /* @__PURE__ */ jsx("path", { d: ae }), labelGeometry && /* @__PURE__ */ jsx( "rect", { x: toDomPrecision(labelGeometry.x), y: toDomPrecision(labelGeometry.y), width: labelGeometry.w, height: labelGeometry.h, rx: 3.5, ry: 3.5 } ) ] }); } onEditEnd(shape) { const { id, type, props: { text } } = shape; if (text.trimEnd() !== shape.props.text) { this.editor.updateShapes([ { id, type, props: { text: text.trimEnd() } } ]); } } toSvg(shape, ctx) { ctx.addExportDef(getFillDefForExport(shape.props.fill)); if (shape.props.text) ctx.addExportDef(getFontDefForExport(shape.props.font)); const theme = getDefaultColorTheme(ctx); const scaleFactor = 1 / shape.props.scale; return /* @__PURE__ */ jsxs("g", { transform: `scale(${scaleFactor})`, children: [ /* @__PURE__ */ jsx(ArrowSvg, { shape, shouldDisplayHandles: false }), /* @__PURE__ */ jsx( SvgTextLabel, { fontSize: getArrowLabelFontSize(shape), font: shape.props.font, align: "middle", verticalAlign: "middle", text: shape.props.text, labelColor: theme[shape.props.labelColor].solid, bounds: getArrowLabelPosition(this.editor, shape).box.clone().expandBy(-ARROW_LABEL_PADDING * shape.props.scale), padding: 0 } ) ] }); } getCanvasSvgDefs() { return [ getFillDefForCanvas(), { key: `arrow:dot`, component: ArrowheadDotDef }, { key: `arrow:cross`, component: ArrowheadCrossDef } ]; } getInterpolatedProps(startShape, endShape, progress) { return { ...(progress > 0.5 ? endShape.props : startShape.props), scale: lerp(startShape.props.scale, endShape.props.scale, progress), start: { x: lerp(startShape.props.start.x, endShape.props.start.x, progress), y: lerp(startShape.props.start.y, endShape.props.start.y, progress) }, end: { x: lerp(startShape.props.end.x, endShape.props.end.x, progress), y: lerp(startShape.props.end.y, endShape.props.end.y, progress) }, bend: lerp(startShape.props.bend, endShape.props.bend, progress), labelPosition: lerp(startShape.props.labelPosition, endShape.props.labelPosition, progress) }; } } function getArrowLength(editor, shape) { const info = getArrowInfo(editor, shape); return info.isStraight ? Vec.Dist(info.start.handle, info.end.handle) : Math.abs(info.handleArc.length); } const ArrowSvg = track(function ArrowSvg2({ shape, shouldDisplayHandles }) { const editor = useEditor(); const theme = useDefaultColorTheme(); const info = getArrowInfo(editor, shape); const bounds = Box.ZeroFix(editor.getShapeGeometry(shape).bounds); const bindings = getArrowBindings(editor, shape); const isForceSolid = useValue( "force solid", () => { return editor.getZoomLevel() < 0.2; }, [editor] ); const clipPathId = useSharedSafeId(shape.id + "_clip"); const arrowheadDotId = useSharedSafeId("arrowhead-dot"); const arrowheadCrossId = useSharedSafeId("arrowhead-cross"); if (!info?.isValid) return null; const strokeWidth = STROKE_SIZES[shape.props.size] * shape.props.scale; const as = info.start.arrowhead && getArrowheadPathForType(info, "start", strokeWidth); const ae = info.end.arrowhead && getArrowheadPathForType(info, "end", strokeWidth); const path = info.isStraight ? getSolidStraightArrowPath(info) : getSolidCurvedArrowPath(info); let handlePath = null; if (shouldDisplayHandles) { const sw = 2 / editor.getZoomLevel(); const { strokeDasharray: strokeDasharray2, strokeDashoffset: strokeDashoffset2 } = getPerfectDashProps( getArrowLength(editor, shape), sw, { end: "skip", start: "skip", lengthRatio: 2.5 } ); handlePath = bindings.start || bindings.end ? /* @__PURE__ */ jsx( "path", { className: "tl-arrow-hint", d: info.isStraight ? getStraightArrowHandlePath(info) : getCurvedArrowHandlePath(info), strokeDasharray: strokeDasharray2, strokeDashoffset: strokeDashoffset2, strokeWidth: sw, markerStart: bindings.start ? bindings.start.props.isExact ? "" : bindings.start.props.isPrecise ? `url(#${arrowheadCrossId})` : `url(#${arrowheadDotId})` : "", markerEnd: bindings.end ? bindings.end.props.isExact ? "" : bindings.end.props.isPrecise ? `url(#${arrowheadCrossId})` : `url(#${arrowheadDotId})` : "", opacity: 0.16 } ) : null; } const { strokeDasharray, strokeDashoffset } = getPerfectDashProps( info.isStraight ? info.length : Math.abs(info.bodyArc.length), strokeWidth, { style: shape.props.dash, forceSolid: isForceSolid } ); const labelPosition = getArrowLabelPosition(editor, shape); const clipStartArrowhead = !(info.start.arrowhead === "none" || info.start.arrowhead === "arrow"); const clipEndArrowhead = !(info.end.arrowhead === "none" || info.end.arrowhead === "arrow"); return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("clipPath", { id: clipPathId, children: /* @__PURE__ */ jsx( ArrowClipPath, { hasText: shape.props.text.trim().length > 0, bounds, labelBounds: labelPosition.box, as: clipStartArrowhead && as ? as : "", ae: clipEndArrowhead && ae ? ae : "" } ) }) }), /* @__PURE__ */ jsxs( "g", { fill: "none", stroke: theme[shape.props.color].solid, strokeWidth, strokeLinejoin: "round", strokeLinecap: "round", pointerEvents: "none", children: [ handlePath, /* @__PURE__ */ jsxs( "g", { style: { clipPath: `url(#${clipPathId})`, WebkitClipPath: `url(#${clipPathId})` }, children: [ /* @__PURE__ */ jsx( "rect", { x: toDomPrecision(bounds.minX - 100), y: toDomPrecision(bounds.minY - 100), width: toDomPrecision(bounds.width + 200), height: toDomPrecision(bounds.height + 200), opacity: 0 } ), /* @__PURE__ */ jsx("path", { d: path, strokeDasharray, strokeDashoffset }) ] } ), as && clipStartArrowhead && shape.props.fill !== "none" && /* @__PURE__ */ jsx( ShapeFill, { theme, d: as, color: shape.props.color, fill: shape.props.fill, scale: shape.props.scale } ), ae && clipEndArrowhead && shape.props.fill !== "none" && /* @__PURE__ */ jsx( ShapeFill, { theme, d: ae, color: shape.props.color, fill: shape.props.fill, scale: shape.props.scale } ), as && /* @__PURE__ */ jsx("path", { d: as }), ae && /* @__PURE__ */ jsx("path", { d: ae }) ] } ) ] }); }); function ArrowClipPath({ hasText, bounds, labelBounds, as, ae }) { const boundingBoxPath = `M${toDomPrecision(bounds.minX - 100)},${toDomPrecision(bounds.minY - 100)} h${bounds.width + 200} v${bounds.height + 200} h-${bounds.width + 200} Z`; const labelBoxPath = `M${toDomPrecision(labelBounds.minX)},${toDomPrecision(labelBounds.minY)} v${labelBounds.height} h${labelBounds.width} v-${labelBounds.height} Z`; return /* @__PURE__ */ jsx("path", { d: `${boundingBoxPath}${hasText ? labelBoxPath : ""}${as}${ae}` }); } const shapeAtTranslationStart = /* @__PURE__ */ new WeakMap(); function ArrowheadDotDef() { const id = useSharedSafeId("arrowhead-dot"); return /* @__PURE__ */ jsx("marker", { id, className: "tl-arrow-hint", refX: "3.0", refY: "3.0", orient: "0", children: /* @__PURE__ */ jsx("circle", { cx: "3", cy: "3", r: "2", strokeDasharray: "100%" }) }); } function ArrowheadCrossDef() { const id = useSharedSafeId("arrowhead-cross"); return /* @__PURE__ */ jsxs("marker", { id, className: "tl-arrow-hint", refX: "3.0", refY: "3.0", orient: "auto", children: [ /* @__PURE__ */ jsx("line", { x1: "1.5", y1: "1.5", x2: "4.5", y2: "4.5", strokeDasharray: "100%" }), /* @__PURE__ */ jsx("line", { x1: "1.5", y1: "4.5", x2: "4.5", y2: "1.5", strokeDasharray: "100%" }) ] }); } export { ArrowShapeUtil, getArrowLength }; //# sourceMappingURL=ArrowShapeUtil.mjs.map