UNPKG

ta-react-xarrows

Version:

Draw arrows (or lines) between components in React!

897 lines 43.7 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __spreadArrays = (this && this.__spreadArrays) || function () { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.tArrowShapes = exports.arrowShapes = exports.tSvgElems = exports.tPaths = exports.tAnchorEdge = void 0; var react_1 = __importStar(require("react")); var lodash_isequal_1 = __importDefault(require("lodash.isequal")); var lodash_pick_1 = __importDefault(require("lodash.pick")); var utils_1 = require("./utils"); var prop_types_1 = __importDefault(require("prop-types")); var buzzier_1 = require("./utils/buzzier"); var anchors_1 = require("./utils/anchors"); // constants used for typescript and proptypes definitions exports.tAnchorEdge = ['middle', 'left', 'right', 'top', 'bottom', 'auto']; exports.tPaths = ['smooth', 'grid', 'straight']; exports.tSvgElems = ['circle', 'ellipse', 'line', 'path', 'polygon', 'polyline', 'rect']; //default arrows svgs exports.arrowShapes = { arrow1: { svgElem: 'path', svgProps: { d: "M 0 0 L 1 0.5 L 0 1 L 0.25 0.5 z" }, offsetForward: 0.25 }, heart: { svgElem: 'path', svgProps: { d: "M 0,0.25 A 0.125,0.125 0,0,1 0.5,0.25 A 0.125,0.125 0,0,1 1,0.25 Q 1,0.625 0.5,1 Q 0,0.625 0,0.25 z", }, offsetForward: 0.1, }, circle: { svgElem: 'circle', svgProps: { r: 0.5, cx: 0.5, cy: 0.5, }, offsetForward: 0, }, }; exports.tArrowShapes = Object.keys(exports.arrowShapes); var Xarrow = function (props) { var _a; var _b = props.startAnchor, startAnchor = _b === void 0 ? 'auto' : _b, _c = props.endAnchor, endAnchor = _c === void 0 ? 'auto' : _c, _d = props.label, label = _d === void 0 ? null : _d, _e = props.color, color = _e === void 0 ? 'CornflowerBlue' : _e, _f = props.lineColor, lineColor = _f === void 0 ? null : _f, _g = props.headColor, headColor = _g === void 0 ? null : _g, _h = props.tailColor, tailColor = _h === void 0 ? null : _h, _j = props.strokeWidth, strokeWidth = _j === void 0 ? 4 : _j, _k = props.showHead, showHead = _k === void 0 ? true : _k, _l = props.headSize, headSize = _l === void 0 ? 6 : _l, _m = props.showTail, showTail = _m === void 0 ? false : _m, _o = props.tailSize, tailSize = _o === void 0 ? 6 : _o, _p = props.path, path = _p === void 0 ? 'smooth' : _p, _q = props.curveness, curveness = _q === void 0 ? 0.8 : _q, _r = props.gridBreak, gridBreak = _r === void 0 ? 0.5 : _r, // gridRadius = strokeWidth * 2, //todo _s = props.dashness, // gridRadius = strokeWidth * 2, //todo dashness = _s === void 0 ? false : _s, _t = props.headShape, headShape = _t === void 0 ? 'arrow1' : _t, _u = props.tailShape, tailShape = _u === void 0 ? 'arrow1' : _u, _v = props.showXarrow, showXarrow = _v === void 0 ? true : _v, _w = props.animateDrawing, animateDrawing = _w === void 0 ? false : _w, _x = props.passProps, passProps = _x === void 0 ? {} : _x, _y = props.arrowBodyProps, arrowBodyProps = _y === void 0 ? {} : _y, _z = props.arrowHeadProps, arrowHeadProps = _z === void 0 ? {} : _z, _0 = props.arrowTailProps, arrowTailProps = _0 === void 0 ? {} : _0, _1 = props.SVGcanvasProps, SVGcanvasProps = _1 === void 0 ? {} : _1, _2 = props.divContainerProps, divContainerProps = _2 === void 0 ? {} : _2, _3 = props.divContainerStyle, divContainerStyle = _3 === void 0 ? {} : _3, _4 = props.SVGcanvasStyle, SVGcanvasStyle = _4 === void 0 ? {} : _4, _5 = props._extendSVGcanvas, _extendSVGcanvas = _5 === void 0 ? 0 : _5, _6 = props._debug, _debug = _6 === void 0 ? false : _6, _7 = props._cpx1Offset, _cpx1Offset = _7 === void 0 ? 0 : _7, _8 = props._cpy1Offset, _cpy1Offset = _8 === void 0 ? 0 : _8, _9 = props._cpx2Offset, _cpx2Offset = _9 === void 0 ? 0 : _9, _10 = props._cpy2Offset, _cpy2Offset = _10 === void 0 ? 0 : _10, _11 = props.updatePositions, updatePositions = _11 === void 0 ? {} : _11, extraProps = __rest(props, ["startAnchor", "endAnchor", "label", "color", "lineColor", "headColor", "tailColor", "strokeWidth", "showHead", "headSize", "showTail", "tailSize", "path", "curveness", "gridBreak", "dashness", "headShape", "tailShape", "showXarrow", "animateDrawing", "passProps", "arrowBodyProps", "arrowHeadProps", "arrowTailProps", "SVGcanvasProps", "divContainerProps", "divContainerStyle", "SVGcanvasStyle", "_extendSVGcanvas", "_debug", "_cpx1Offset", "_cpy1Offset", "_cpx2Offset", "_cpy2Offset", "updatePositions"]); var svgRef = react_1.useRef(null); var lineRef = react_1.useRef(null); var headRef = react_1.useRef(null); var tailRef = react_1.useRef(null); var lineDrawAnimRef = react_1.useRef(null); var lineDashAnimRef = react_1.useRef(null); var headOpacityAnimRef = react_1.useRef(null); var startRef = react_1.useRef(null); var endRef = react_1.useRef(null); var prevPosState = react_1.useRef(null); var prevProps = react_1.useRef(null); // const [headBox, setHeadBox] = useState({ x: 0, y: 0, width: 1, height: 1 }); // const [tailBox, setTailBox] = useState({ x: 0, y: 0, width: 1, height: 1 }); // const headBox = useRef({ x: 0, y: 0, width: 1, height: 1 }); // const tailBox = useRef({ x: 0, y: 0, width: 1, height: 1 }); var _12 = react_1.useState(!animateDrawing), drawAnimEnded = _12[0], setDrawAnimEnded = _12[1]; var _13 = react_1.useState({}), _ = _13[0], setRerender = _13[1]; var dumyRenderer = function () { return setRerender({}); }; if (process.env.NODE_ENV === 'development') { // debug var _render = react_1.useRef(0); var _call = react_1.useRef(-1); _call.current += 1; var consoleState = function () { return "{call:" + _call.current + ",render:" + _render.current + "}"; }; var log = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return console.log.apply(console, __spreadArrays(args, [consoleState()])); }; } /** * determine if an update is needed and update if so. * update is needed if one of the connected elements position was changed since last render, * or if the ref to one of the elements has changed(it points to a different element) * or if one of the given props has changed */ var updateIfNeeded = function () { // in case one of the elements does not mounted skip any update if (startRef == null || endRef == null || showXarrow == false) return; if (!lodash_isequal_1.default(props, prevProps.current)) { //first check if any properties changed if (prevProps.current) { initProps(); prevPosState.current = getElemsPos(); updatePosition(); } } else { //if the properties did not changed - update position if needed var posState = getElemsPos(); if (!lodash_isequal_1.default(prevPosState.current, posState)) { prevPosState.current = posState; updatePosition(); } } }; var initAnchorsRefs = function () { startRef.current = utils_1.getElementByPropGiven(props.start); endRef.current = utils_1.getElementByPropGiven(props.end); }; var initProps = function () { prevProps.current = props; }; var monitorDOMchanges = function () { window.addEventListener('resize', updateIfNeeded); var handleDrawAmimEnd = function () { var _a; setDrawAnimEnded(true); // @ts-ignore headOpacityAnimRef.current.beginElement(); (_a = lineDashAnimRef.current) === null || _a === void 0 ? void 0 : _a.beginElement(); }; var handleDrawAmimBegin = function () { return (headRef.current.style.opacity = '0'); }; if (lineDrawAnimRef.current && headRef.current) { lineDrawAnimRef.current.addEventListener('endEvent', handleDrawAmimEnd); lineDrawAnimRef.current.addEventListener('beginEvent', handleDrawAmimBegin); } return function () { window.removeEventListener('resize', updateIfNeeded); if (lineDrawAnimRef.current) { lineDrawAnimRef.current.removeEventListener('endEvent', handleDrawAmimEnd); if (headRef.current) lineDrawAnimRef.current.removeEventListener('beginEvent', handleDrawAmimBegin); } }; }; // const headBox = headRef.current?.getBBox({ stroke: true }) ?? { x: 0, y: 0, width: 1, height: 1 }; var _14 = react_1.useState({ //initial state cx0: 0, cy0: 0, cw: 0, ch: 0, x1: 0, y1: 0, x2: 0, y2: 0, dx: 0, dy: 0, absDx: 0, absDy: 0, cpx1: 0, cpy1: 0, cpx2: 0, cpy2: 0, headOrient: 0, tailOrient: 0, labelStartPos: { x: 0, y: 0 }, labelMiddlePos: { x: 0, y: 0 }, labelEndPos: { x: 0, y: 0 }, arrowEnd: { x: 0, y: 0 }, arrowHeadOffset: { x: 0, y: 0 }, arrowTailOffset: { x: 0, y: 0 }, headOffset: 0, excRight: 0, excLeft: 0, excUp: 0, excDown: 0, startPoints: [], endPoints: [], mainDivPos: { x: 0, y: 0 }, xSign: 1, ySign: 1, lineLength: 0, fHeadSize: 1, fTailSize: 1, arrowPath: "", }), st = _14[0], setSt = _14[1]; headSize = Number(headSize); strokeWidth = Number(strokeWidth); headColor = headColor ? headColor : color; tailColor = tailColor ? tailColor : color; lineColor = lineColor ? lineColor : color; var dashStroke = 0, dashNone = 0, animDashSpeed, animDirection = 1; if (dashness) { if (typeof dashness === 'object') { dashStroke = dashness.strokeLen ? Number(dashness.strokeLen) : Number(strokeWidth) * 2; dashNone = dashness.strokeLen ? Number(dashness.nonStrokeLen) : Number(strokeWidth); animDashSpeed = dashness.animation ? Number(dashness.animation) : null; } else if (typeof dashness === 'boolean') { dashStroke = Number(strokeWidth) * 2; dashNone = Number(strokeWidth); animDashSpeed = null; } } var labelStart = null, labelMiddle = null, labelEnd = null; if (label) { if (typeof label === 'string' || 'type' in label) labelMiddle = label; else if (['start', 'middle', 'end'].some(function (key) { return key in label; })) { label = label; (labelStart = label.start, labelMiddle = label.middle, labelEnd = label.end); } } var defaultEdge = function (svgEdge) { if (typeof svgEdge == 'string') { if (svgEdge in exports.arrowShapes) svgEdge = exports.arrowShapes[svgEdge]; else { console.warn("'" + svgEdge + "' is not supported arrow shape. the supported arrow shapes is one of " + exports.tArrowShapes + ".\n reverting to default shape."); svgEdge = exports.arrowShapes['arrow1']; } } svgEdge = svgEdge; if ((svgEdge === null || svgEdge === void 0 ? void 0 : svgEdge.offsetForward) === undefined) svgEdge.offsetForward = 0.25; if ((svgEdge === null || svgEdge === void 0 ? void 0 : svgEdge.svgElem) === undefined) svgEdge.svgElem = 'path'; if ((svgEdge === null || svgEdge === void 0 ? void 0 : svgEdge.svgProps) === undefined) svgEdge.svgProps = exports.arrowShapes.arrow1.svgProps; return svgEdge; }; headShape = defaultEdge(headShape); tailShape = defaultEdge(tailShape); var getMainDivPos = function () { // if (!mainDivRef.current) return { x: 0, y: 0 }; var _a = svgRef.current.getBoundingClientRect(), xarrowElemX = _a.left, xarrowElemY = _a.top; var xarrowStyle = getComputedStyle(svgRef.current); var xarrowStyleLeft = Number(xarrowStyle.left.slice(0, -2)); var xarrowStyleTop = Number(xarrowStyle.top.slice(0, -2)); return { x: xarrowElemX - xarrowStyleLeft, y: xarrowElemY - xarrowStyleTop, }; }; var getElemPos = function (elem) { var pos = elem.getBoundingClientRect(); return { x: pos.left, y: pos.top, right: pos.right, bottom: pos.bottom, }; }; var getElemsPos = function () { var start = getElemPos(startRef.current); var end = getElemPos(endRef.current); return { start: start, end: end }; }; /** * The Main logic of path calculation for the arrow. * calculate new path, adjusting canvas, and set state based on given properties. * */ var updatePosition = function (positions) { var _a, _b; if (positions === void 0) { positions = prevPosState.current; } var sPos = positions.start; var ePos = positions.end; var headOrient = 0; var tailOrient = 0; // convert startAnchor and endAnchor to list of objects represents allowed anchors. var startPoints = anchors_1.prepareAnchorLines(startAnchor, sPos); var endPoints = anchors_1.prepareAnchorLines(endAnchor, ePos); // choose the smallest path for 2 ponts from these possibilities. var _c = anchors_1.getShortestLine(startPoints, endPoints), startPointObj = _c.startPointObj, endPointObj = _c.endPointObj; var startAnchorPosition = startPointObj.anchorPosition, endAnchorPosition = endPointObj.anchorPosition; var startPoint = lodash_pick_1.default(startPointObj, ['x', 'y']), endPoint = lodash_pick_1.default(endPointObj, ['x', 'y']); headShape = headShape; tailShape = tailShape; var mainDivPos = getMainDivPos(); var cx0 = Math.min(startPoint.x, endPoint.x) - mainDivPos.x; var cy0 = Math.min(startPoint.y, endPoint.y) - mainDivPos.y; var dx = endPoint.x - startPoint.x; var dy = endPoint.y - startPoint.y; var absDx = Math.abs(endPoint.x - startPoint.x); var absDy = Math.abs(endPoint.y - startPoint.y); var xSign = dx > 0 ? 1 : -1; var ySign = dy > 0 ? 1 : -1; var _d = [headShape.offsetForward, tailShape.offsetForward], headOffset = _d[0], tailOffset = _d[1]; var fHeadSize = headSize * strokeWidth; //factored head size var fTailSize = tailSize * strokeWidth; //factored head size // const { current: _headBox } = headBox; var xHeadOffset = 0; var yHeadOffset = 0; var xTailOffset = 0; var yTailOffset = 0; // let svgFactor = Math.max(_headBox.width, _headBox.height); // headOffset *= Math.min(_headBox.width, _headBox.height); // fHeadSize /= svgFactor; var _headOffset = fHeadSize * headOffset; var _tailOffset = fTailSize * tailOffset; // const headBox = headRef.current?.getBBox({ stroke: true }) ?? { x: 0, y: 0, width: 1, height: 1 }; // const headBox = measureFunc(() => headRef.current?.getBBox({ stroke: true }), 'getBBox') ?? { // x: 0, // y: 0, // width: 0, // height: 0, // }; var cu = Number(curveness); gridBreak = Number(gridBreak); // gridRadius = Number(gridRadius); if (!exports.tPaths.includes(path)) path = 'smooth'; if (path === 'straight') { cu = 0; path = 'smooth'; } var biggerSide = headSize > tailSize ? headSize : tailSize; var _calc = strokeWidth + (strokeWidth * biggerSide) / 2; var excRight = _calc; var excLeft = _calc; var excUp = _calc; var excDown = _calc; excLeft += Number(_extendSVGcanvas); excRight += Number(_extendSVGcanvas); excUp += Number(_extendSVGcanvas); excDown += Number(_extendSVGcanvas); //////////////////////////////////// // arrow point to point calculations var x1 = 0, x2 = absDx, y1 = 0, y2 = absDy; if (dx < 0) _a = [x2, x1], x1 = _a[0], x2 = _a[1]; if (dy < 0) _b = [y2, y1], y1 = _b[0], y2 = _b[1]; //////////////////////////////////// // arrow curviness and arrowhead placement calculations if (cu === 0) { // in case of straight path var headAngel = Math.atan(absDy / absDx); if (showHead) { x2 -= fHeadSize * (1 - headOffset) * xSign * Math.cos(headAngel); y2 -= fHeadSize * (1 - headOffset) * ySign * Math.sin(headAngel); headAngel *= ySign; if (xSign < 0) headAngel = (Math.PI - headAngel * xSign) * xSign; xHeadOffset = Math.cos(headAngel) * _headOffset - (Math.sin(headAngel) * fHeadSize) / 2; yHeadOffset = (Math.cos(headAngel) * fHeadSize) / 2 + Math.sin(headAngel) * _headOffset; headOrient = (headAngel * 180) / Math.PI; } var tailAngel = Math.atan(absDy / absDx); if (showTail) { x1 += fTailSize * (1 - tailOffset) * xSign * Math.cos(tailAngel); y1 += fTailSize * (1 - tailOffset) * ySign * Math.sin(tailAngel); tailAngel *= -ySign; if (xSign > 0) tailAngel = (Math.PI - tailAngel * xSign) * xSign; xTailOffset = Math.cos(tailAngel) * _tailOffset - (Math.sin(tailAngel) * fTailSize) / 2; yTailOffset = (Math.cos(tailAngel) * fTailSize) / 2 + Math.sin(tailAngel) * _tailOffset; tailOrient = (tailAngel * 180) / Math.PI; } } else { // in case of smooth path if (endAnchorPosition === 'middle') { // in case a middle anchor is chosen for endAnchor choose from which side to attach to the middle of the element if (absDx > absDy) { endAnchorPosition = xSign ? 'left' : 'right'; } else { endAnchorPosition = ySign ? 'top' : 'bottom'; } } if (showHead) { if (['left', 'right'].includes(endAnchorPosition)) { //todo: rotate all transforms(and arrows svg) by 90 //// for 90 deg turn // xHeadOffset = -fHeadSize + _headOffset * xSign; // x2 -= fHeadSize * (1 - headOffset) * xSign; // yHeadOffset = (fHeadSize / 2) * xSign; // if (endAnchorPosition === "left") { // headOrient = 90; // ... // const headBox = headRef.current.getBBox({ stroke: true }); xHeadOffset += _headOffset * xSign; // x2 -= fHeadSize * xSign - xHeadOffset; x2 -= fHeadSize * (1 - headOffset) * xSign; //same! yHeadOffset += (fHeadSize * xSign) / 2; // const xm = 1 - (_headBox.width + _headBox.x); // xHeadOffset += _headBox.height / headSize; // xHeadOffset = xHeadOffset - xm; // xHeadOffset -= (_headBox.x / (_headBox.height + _headBox.width)) * fHeadSize; // yHeadOffset = yHeadOffset - (1 - _headBox.height - _headBox.y) * strokeWidth * yHeadOffset; if (endAnchorPosition === 'left') { headOrient = 0; if (xSign < 0) headOrient += 180; } else { headOrient = 180; if (xSign > 0) headOrient += 180; } } else if (['top', 'bottom'].includes(endAnchorPosition)) { xHeadOffset += (fHeadSize * -ySign) / 2; yHeadOffset += _headOffset * ySign; y2 -= fHeadSize * ySign - yHeadOffset; if (endAnchorPosition === 'top') { headOrient = 270; if (ySign > 0) headOrient += 180; } else { headOrient = 90; if (ySign < 0) headOrient += 180; } // const xm = 1 - _headBox.width - _headBox.x; // yHeadOffset = yHeadOffset - xm * strokeWidth * yHeadOffset; } } } if (showTail && cu !== 0) { if (['left', 'right'].includes(startAnchorPosition)) { xTailOffset += _tailOffset * -xSign; x1 += fTailSize * xSign + xTailOffset; yTailOffset += -(fTailSize * xSign) / 2; if (startAnchorPosition === 'left') { tailOrient = 180; if (xSign < 0) tailOrient += 180; } else { tailOrient = 0; if (xSign > 0) tailOrient += 180; } } else if (['top', 'bottom'].includes(startAnchorPosition)) { yTailOffset += _tailOffset * -ySign; y1 += fTailSize * ySign + yTailOffset; xTailOffset += (fTailSize * ySign) / 2; if (startAnchorPosition === 'top') { tailOrient = 90; if (ySign > 0) tailOrient += 180; } else { tailOrient = 270; if (ySign < 0) tailOrient += 180; } } } // if (endAnchorPosition == startAnchorPosition) headOrient += 180; var arrowHeadOffset = { x: xHeadOffset, y: yHeadOffset }; var arrowTailOffset = { x: xTailOffset, y: yTailOffset }; var cpx1 = x1, cpy1 = y1, cpx2 = x2, cpy2 = y2; var curvesPossibilities = {}; if (path === 'smooth') curvesPossibilities = { hh: function () { var _a; if (updatePositions.smooth && updatePositions.smooth.hh) { _a = updatePositions.smooth.hh({ x1: x1, x2: x2, y1: y1, y2: y2, cpx1: cpx1, cpx2: cpx2, cpy1: cpy1, cpy2: cpy2, }), x1 = _a[0], x2 = _a[1], y1 = _a[2], y2 = _a[3], cpx1 = _a[4], cpx2 = _a[5], cpy1 = _a[6], cpy2 = _a[7]; } else { //horizontal - from right to left or the opposite cpx1 += absDx * cu * xSign; cpx2 -= absDx * cu * xSign; // if (absDx < 2 * headOffset) { // cpx1 += headOffset * xSign - absDx / 2; // cpx2 -= headOffset * xSign * 2 - absDx; // } // cpx1 += headOffset * 2 * xSign; // cpx2 -= headOffset * 2 * xSign; } }, vv: function () { //vertical - from top to bottom or opposite cpy1 += absDy * cu * ySign; cpy2 -= absDy * cu * ySign; // cpy1 += headOffset * 2 * ySign; // cpy2 -= headOffset * 2 * ySign; }, hv: function () { // start horizontally then vertically // from v side to h side cpx1 += absDx * cu * xSign; cpy2 -= absDy * cu * ySign; }, vh: function () { // start vertically then horizontally // from h side to v side cpy1 += absDy * cu * ySign; cpx2 -= absDx * cu * xSign; }, }; else if (path === 'grid') { curvesPossibilities = { hh: function () { // cpx1 += (absDx * 0.5 - headOffset / 2) * xSign; // cpx2 -= (absDx * 0.5 - headOffset / 2) * xSign; cpx1 += absDx * gridBreak * xSign; cpx2 -= absDx * (1 - gridBreak) * xSign; if (showHead) { cpx1 -= ((fHeadSize * (1 - headOffset)) / 2) * xSign; cpx2 += ((fHeadSize * (1 - headOffset)) / 2) * xSign; } if (showTail) { cpx1 -= ((fTailSize * (1 - tailOffset)) / 2) * xSign; cpx2 += ((fTailSize * (1 - tailOffset)) / 2) * xSign; } }, vv: function () { cpy1 += absDy * gridBreak * ySign; cpy2 -= absDy * (1 - gridBreak) * ySign; if (showHead) { cpy1 -= ((fHeadSize * (1 - headOffset)) / 2) * ySign; cpy2 += ((fHeadSize * (1 - headOffset)) / 2) * ySign; } if (showTail) { cpy1 -= ((fTailSize * (1 - tailOffset)) / 2) * ySign; cpy2 += ((fTailSize * (1 - tailOffset)) / 2) * ySign; } }, hv: function () { cpx1 = x2; }, vh: function () { cpy1 = y2; }, }; } // smart select best curve for the current anchors var selectedCurviness = ''; if (['left', 'right'].includes(startAnchorPosition)) selectedCurviness += 'h'; else if (['bottom', 'top'].includes(startAnchorPosition)) selectedCurviness += 'v'; else if (startAnchorPosition === 'middle') selectedCurviness += 'm'; if (['left', 'right'].includes(endAnchorPosition)) selectedCurviness += 'h'; else if (['bottom', 'top'].includes(endAnchorPosition)) selectedCurviness += 'v'; else if (endAnchorPosition === 'middle') selectedCurviness += 'm'; if (absDx > absDy) selectedCurviness = selectedCurviness.replace(/m/g, 'h'); else selectedCurviness = selectedCurviness.replace(/m/g, 'v'); curvesPossibilities[selectedCurviness](); cpx1 += _cpx1Offset; cpy1 += _cpy1Offset; cpx2 += _cpx2Offset; cpy2 += _cpy2Offset; //////////////////////////////////// // canvas smart size adjustments // todo: fix: calc edges size and adjust canvas var _e = buzzier_1.buzzierMinSols(x1, cpx1, cpx2, x2), xSol1 = _e[0], xSol2 = _e[1]; var _f = buzzier_1.buzzierMinSols(y1, cpy1, cpy2, y2), ySol1 = _f[0], ySol2 = _f[1]; if (xSol1 < 0) excLeft += -xSol1; if (xSol2 > absDx) excRight += xSol2 - absDx; if (ySol1 < 0) excUp += -ySol1; if (ySol2 > absDy) excDown += ySol2 - absDy; if (path === 'grid') { excLeft += _calc; excRight += _calc; excUp += _calc; excDown += _calc; } x1 += excLeft; x2 += excLeft; y1 += excUp; y2 += excUp; cpx1 += excLeft; cpx2 += excLeft; cpy1 += excUp; cpy2 += excUp; var cw = absDx + excLeft + excRight, ch = absDy + excUp + excDown; cx0 -= excLeft; cy0 -= excUp; //labels var bzx = buzzier_1.bzFunction(x1, cpx1, cpx2, x2); var bzy = buzzier_1.bzFunction(y1, cpy1, cpy2, y2); var labelStartPos = { x: bzx(0.01), y: bzy(0.01) }; var labelMiddlePos = { x: bzx(0.5), y: bzy(0.5) }; var labelEndPos = { x: bzx(0.99), y: bzy(0.99) }; var arrowEnd = { x: bzx(1), y: bzy(1) }; var arrowPath; if (path === 'grid') { // todo: support gridRadius // arrowPath = `M ${x1} ${y1} L ${cpx1 - 10} ${cpy1} a10,10 0 0 1 10,10 // L ${cpx2} ${cpy2 - 10} a10,10 0 0 0 10,10 L ${x2} ${y2}`; arrowPath = "M " + x1 + " " + y1 + " L " + cpx1 + " " + cpy1 + " L " + cpx2 + " " + cpy2 + " " + x2 + " " + y2; } else if (path === 'smooth') arrowPath = "M " + x1 + " " + y1 + " C " + cpx1 + " " + cpy1 + ", " + cpx2 + " " + cpy2 + ", " + x2 + " " + y2; setSt({ cx0: cx0, cy0: cy0, x1: x1, x2: x2, y1: y1, y2: y2, cw: cw, ch: ch, cpx1: cpx1, cpy1: cpy1, cpx2: cpx2, cpy2: cpy2, dx: dx, dy: dy, absDx: absDx, absDy: absDy, headOrient: headOrient, tailOrient: tailOrient, labelStartPos: labelStartPos, labelMiddlePos: labelMiddlePos, labelEndPos: labelEndPos, arrowEnd: arrowEnd, excLeft: excLeft, excRight: excRight, excUp: excUp, excDown: excDown, headOffset: _headOffset, arrowHeadOffset: arrowHeadOffset, arrowTailOffset: arrowTailOffset, startPoints: startPoints, endPoints: endPoints, mainDivPos: mainDivPos, xSign: xSign, ySign: ySign, lineLength: lineRef.current.getTotalLength(), fHeadSize: fHeadSize, fTailSize: fTailSize, arrowPath: arrowPath, }); }; var xOffsetHead = st.x2 - st.arrowHeadOffset.x; var yOffsetHead = st.y2 - st.arrowHeadOffset.y; var xOffsetTail = st.x1 - st.arrowTailOffset.x; var yOffsetTail = st.y1 - st.arrowTailOffset.y; var dashoffset = dashStroke + dashNone; if (animDashSpeed < 0) { animDashSpeed *= -1; animDirection = -1; } var dashArray, animation, animRepeatCount, animStartValue, animEndValue = 0; if (animateDrawing && drawAnimEnded == false) { if (typeof animateDrawing === 'boolean') animateDrawing = 1; animation = animateDrawing + 's'; dashArray = st.lineLength; animStartValue = st.lineLength; animRepeatCount = 1; if (animateDrawing < 0) { _a = [animEndValue, animStartValue], animStartValue = _a[0], animEndValue = _a[1]; animation = animateDrawing * -1 + 's'; } // } } else { dashArray = dashStroke + " " + dashNone; animation = 1 / animDashSpeed + "s"; animStartValue = dashoffset * animDirection; animRepeatCount = 'indefinite'; animEndValue = 0; } var initXarrow = function () { initProps(); initAnchorsRefs(); }; react_1.useLayoutEffect(function () { updateIfNeeded(); if (process.env.NODE_ENV === 'development') { // log('xarrow has rendered!'); _render.current += 1; } }); // update refs to elements if needed react_1.useLayoutEffect(function () { startRef.current = utils_1.getElementByPropGiven(props.start); }, [props.start]); react_1.useLayoutEffect(function () { endRef.current = utils_1.getElementByPropGiven(props.end); }, [props.end]); // handle draw animation react_1.useLayoutEffect(function () { if (lineRef.current) setSt(function (prevSt) { return (__assign(__assign({}, prevSt), { lineLength: lineRef.current.getTotalLength() })); }); }, [lineRef.current]); // // for adjustments of custom svg shapes // useLayoutEffect(() => { // headBox.current = headRef.current?.getBBox({ stroke: true }) ?? { x: 0, y: 0, width: 1, height: 1 }; // }, [props.headShape]); // useLayoutEffect(() => { // tailBox.current = tailRef.current?.getBBox({ stroke: true }) ?? { x: 0, y: 0, width: 1, height: 1 }; // }, [props.tailShape]); // set all props on first render react_1.useEffect(function () { if (showXarrow) { initXarrow(); updateIfNeeded(); } var cleanMonitorDOMchanges = monitorDOMchanges(); return function () { setDrawAnimEnded(false); cleanMonitorDOMchanges(); }; }, [showXarrow]); //todo: could make some advanced generic typescript inferring. for example get type from headShape.elem:T and // tailShape.elem:K force the type for passProps,arrowHeadProps,arrowTailProps property. for now `as any` is used to // avoid typescript conflicts // so todo- fix all the `passProps as any` assertions return (react_1.default.createElement("div", __assign({}, divContainerProps, { style: __assign({ position: 'absolute' }, divContainerStyle) }, extraProps), showXarrow ? (react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement("svg", __assign({ ref: svgRef, width: st.cw, height: st.ch, // width="100%" // height="100%" // preserveAspectRatio="none" // viewBox={'auto'} style: __assign({ position: 'absolute', left: st.cx0, top: st.cy0, pointerEvents: 'none', border: _debug ? '1px dashed yellow' : null }, SVGcanvasStyle), overflow: "auto" }, SVGcanvasProps), react_1.default.createElement("path", __assign({ ref: lineRef, d: st.arrowPath, stroke: lineColor, strokeDasharray: dashArray, // strokeDasharray={'0 0'} strokeWidth: strokeWidth, fill: "transparent", pointerEvents: "visibleStroke" }, passProps, arrowBodyProps), react_1.default.createElement(react_1.default.Fragment, null, drawAnimEnded ? (react_1.default.createElement(react_1.default.Fragment, null, animDashSpeed ? (react_1.default.createElement("animate", { ref: lineDashAnimRef, attributeName: "stroke-dashoffset", values: dashoffset * animDirection + ";0", dur: 1 / animDashSpeed + "s", repeatCount: "indefinite" })) : null)) : (react_1.default.createElement(react_1.default.Fragment, null, animateDrawing ? (react_1.default.createElement("animate", { ref: lineDrawAnimRef, id: "svgEndAnimate", attributeName: "stroke-dashoffset", values: animStartValue + ";" + animEndValue, dur: animation, repeatCount: animRepeatCount })) : null)))), showTail ? (react_1.default.createElement(tailShape.svgElem // d={normalArrowShape} , __assign({ // d={normalArrowShape} fill: tailColor, pointerEvents: "auto", transform: "translate(" + xOffsetTail + "," + yOffsetTail + ") rotate(" + st.tailOrient + ") scale(" + st.fTailSize + ")" }, tailShape.svgProps, passProps, arrowTailProps))) : null, showHead ? (react_1.default.createElement(headShape.svgElem, __assign({ ref: headRef, // d={normalArrowShape} fill: headColor, pointerEvents: "auto", transform: "translate(" + xOffsetHead + "," + yOffsetHead + ") rotate(" + st.headOrient + ") scale(" + st.fHeadSize + ")", opacity: animateDrawing && !drawAnimEnded ? 0 : 1 }, headShape.svgProps, passProps, arrowHeadProps), react_1.default.createElement("animate", { ref: headOpacityAnimRef, dur: '0.4', attributeName: "opacity", from: "0", to: "1", begin: "indefinite", repeatCount: "0", fill: "freeze" }), ") : null")) : null, _debug ? (react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement("circle", { r: "5", cx: st.cpx1, cy: st.cpy1, fill: "green" }), react_1.default.createElement("circle", { r: "5", cx: st.cpx2, cy: st.cpy2, fill: "blue" }), react_1.default.createElement("rect", { x: st.excLeft, y: st.excUp, width: st.absDx, height: st.absDy, fill: "none", stroke: "pink", strokeWidth: "2px" }))) : null), labelStart ? (react_1.default.createElement("div", { style: { transform: st.dx < 0 ? 'translate(-100% , -50%)' : 'translate(-0% , -50%)', width: 'max-content', position: 'absolute', left: st.cx0 + st.labelStartPos.x, top: st.cy0 + st.labelStartPos.y - strokeWidth - 5, zIndex: 2, } }, labelStart)) : null, labelMiddle ? (react_1.default.createElement("div", { style: { display: 'table', width: 'max-content', transform: 'translate(-50% , -50%)', position: 'absolute', left: st.cx0 + st.labelMiddlePos.x, top: st.cy0 + st.labelMiddlePos.y, zIndex: 2, } }, labelMiddle)) : null, labelEnd ? (react_1.default.createElement("div", { style: { transform: st.dx > 0 ? 'translate(-100% , -50%)' : 'translate(-0% , -50%)', width: 'max-content', position: 'absolute', left: st.cx0 + st.labelEndPos.x, top: st.cy0 + st.labelEndPos.y + strokeWidth + 5, zIndex: 2, } }, labelEnd)) : null, _debug ? (react_1.default.createElement(react_1.default.Fragment, null, __spreadArrays(st.startPoints, st.endPoints).map(function (p, i) { return (react_1.default.createElement("div", { key: i, style: { background: 'gray', opacity: 0.5, borderRadius: '50%', transform: 'translate(-50%, -50%)', height: 5, width: 5, position: 'absolute', left: p.x - st.mainDivPos.x, top: p.y - st.mainDivPos.y, } })); }))) : null)) : null)); }; ////////////////////////////// // propTypes var pAnchorPositionType = prop_types_1.default.oneOf(exports.tAnchorEdge); var pAnchorCustomPositionType = prop_types_1.default.exact({ position: pAnchorPositionType.isRequired, offset: prop_types_1.default.exact({ rightness: prop_types_1.default.number, bottomness: prop_types_1.default.number, }).isRequired, }); var _pAnchorType = prop_types_1.default.oneOfType([pAnchorPositionType, pAnchorCustomPositionType]); var pAnchorType = prop_types_1.default.oneOfType([_pAnchorType, prop_types_1.default.arrayOf(_pAnchorType)]); var pRefType = prop_types_1.default.oneOfType([prop_types_1.default.string, prop_types_1.default.exact({ current: prop_types_1.default.instanceOf(Element) })]); var _pLabelType = prop_types_1.default.oneOfType([prop_types_1.default.element, prop_types_1.default.string]); var pLabelsType = prop_types_1.default.exact({ start: _pLabelType, middle: _pLabelType, end: _pLabelType, }); var pSvgEdgeShapeType = prop_types_1.default.oneOf(Object.keys(exports.arrowShapes)); var pSvgElemType = prop_types_1.default.oneOf(exports.tSvgElems); var pSvgEdgeType = prop_types_1.default.oneOfType([ pSvgEdgeShapeType, prop_types_1.default.exact({ svgElem: pSvgElemType, svgProps: prop_types_1.default.any, offsetForward: prop_types_1.default.number, }).isRequired, ]); Xarrow.propTypes = { start: pRefType.isRequired, end: pRefType.isRequired, startAnchor: pAnchorType, endAnchor: pAnchorType, label: prop_types_1.default.oneOfType([_pLabelType, pLabelsType]), color: prop_types_1.default.string, lineColor: prop_types_1.default.string, showHead: prop_types_1.default.bool, headColor: prop_types_1.default.string, headSize: prop_types_1.default.number, tailSize: prop_types_1.default.number, tailColor: prop_types_1.default.string, strokeWidth: prop_types_1.default.number, showTail: prop_types_1.default.bool, path: prop_types_1.default.oneOf(exports.tPaths), showXarrow: prop_types_1.default.bool, curveness: prop_types_1.default.number, gridBreak: prop_types_1.default.number, dashness: prop_types_1.default.oneOfType([prop_types_1.default.bool, prop_types_1.default.object]), headShape: pSvgEdgeType, tailShape: pSvgEdgeType, animateDrawing: prop_types_1.default.oneOfType([prop_types_1.default.bool, prop_types_1.default.number]), passProps: prop_types_1.default.object, arrowBodyProps: prop_types_1.default.object, arrowHeadProps: prop_types_1.default.object, arrowTailProps: prop_types_1.default.object, SVGcanvasProps: prop_types_1.default.object, divContainerProps: prop_types_1.default.object, _extendSVGcanvas: prop_types_1.default.number, _debug: prop_types_1.default.bool, _cpx1Offset: prop_types_1.default.number, _cpy1Offset: prop_types_1.default.number, _cpx2Offset: prop_types_1.default.number, _cpy2Offset: prop_types_1.default.number, }; exports.default = Xarrow; //# sourceMappingURL=index.js.map