ta-react-xarrows
Version:
Draw arrows (or lines) between components in React!
897 lines • 43.7 kB
JavaScript
"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