react-xarrows
Advanced tools
Comparing version 1.5.2 to 1.6.0
@@ -0,1 +1,11 @@ | ||
## v1.6.0: | ||
- proptypes - typescript types issues fixed. | ||
- now all pointer events enabled in the arrowhead by default ([#50](https://github.com/Eliav2/react-xarrows/issues/50)) | ||
- added properties: `SVGcanvasStyle` and `divContainerStyle` ([#42])(https://github.com/Eliav2/react-xarrows/issues/42) | ||
- examples: bugfixes(playground) and examples update. | ||
- a new feature! tail support! new properties: `showHead`,`showTail`,`tailSize`,`tailColor`,`arrowTailProps` . | ||
- renamed `extendSVGcanvas` to `_extendSVGcanvas` | ||
- advanced customization support: newProperties: `_cpx1Offset`,`_cpy1Offset`,`_cpx2Offset`,`_cpy2Offset`. | ||
- bugfix: in grid mode in some cases exceeded canvas. | ||
## 1.5.0 | ||
@@ -5,3 +15,4 @@ - properties that are completely removed and no longer supported: `consoleWarning`, `monitorDOMchanges` and `registerEvents`. | ||
- dependencies changed: prop-types added in lodash replaced with lodash.isequal and lodash.pick. | ||
- v1.5.1 - minor code and docs updates. | ||
- v1.5.1 - minor code and docs updates. [DEPRECATED] | ||
- v1.5.2 - debug element was published by mistake at v1.5.1. | ||
@@ -8,0 +19,0 @@ improved documentation: |
@@ -11,4 +11,8 @@ import React from "react"; | ||
headColor?: string | null; | ||
tailColor?: string | null; | ||
strokeWidth?: number; | ||
showHead?: boolean; | ||
headSize?: number; | ||
showTail?: boolean; | ||
tailSize?: number; | ||
path?: "smooth" | "grid" | "straight"; | ||
@@ -22,7 +26,15 @@ curveness?: number; | ||
passProps?: React.SVGProps<SVGPathElement>; | ||
extendSVGcanvas?: number; | ||
SVGcanvasProps?: React.SVGAttributes<SVGSVGElement>; | ||
arrowBodyProps?: React.SVGProps<SVGPathElement>; | ||
arrowHeadProps?: React.SVGProps<SVGPathElement>; | ||
arrowTailProps?: React.SVGProps<SVGPathElement>; | ||
divContainerProps?: React.HTMLProps<HTMLDivElement>; | ||
SVGcanvasStyle?: React.CSSProperties; | ||
divContainerStyle?: React.CSSProperties; | ||
_extendSVGcanvas?: number; | ||
_debug: boolean; | ||
_cpx1Offset: number; | ||
_cpy1Offset: number; | ||
_cpx2Offset: number; | ||
_cpy2Offset: number; | ||
}; | ||
@@ -34,10 +46,7 @@ export declare type anchorType = anchorPositionType | anchorCustomPositionType; | ||
offset: { | ||
rightness: number; | ||
bottomness: number; | ||
rightness?: number; | ||
bottomness?: number; | ||
}; | ||
}; | ||
export declare type reactRefType = { | ||
current: null | HTMLElement; | ||
}; | ||
export declare type refType = reactRefType | string; | ||
export declare type refType = React.MutableRefObject<any> | string; | ||
export declare type labelsType = { | ||
@@ -48,11 +57,6 @@ start?: labelType; | ||
}; | ||
export declare type labelType = JSX.Element; | ||
export declare type labelType = JSX.Element | string; | ||
export declare type domEventType = keyof GlobalEventHandlersEventMap; | ||
export declare type registerEventsType = { | ||
ref: refType; | ||
eventName: domEventType; | ||
callback?: CallableFunction; | ||
}; | ||
declare const Xarrow: React.FC<xarrowPropsType>; | ||
export default Xarrow; | ||
//# sourceMappingURL=index.d.ts.map |
287
lib/index.js
"use strict"; | ||
//// @ts-nocheck | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
@@ -45,3 +44,3 @@ if (k2 === undefined) k2 = k; | ||
const Xarrow = (props) => { | ||
let { startAnchor, endAnchor, label, color, lineColor, headColor, strokeWidth, headSize, path, curveness, dashness, passProps, SVGcanvasProps, arrowBodyProps, arrowHeadProps, divContainerProps, extendSVGcanvas } = props, extraProps = __rest(props, ["startAnchor", "endAnchor", "label", "color", "lineColor", "headColor", "strokeWidth", "headSize", "path", "curveness", "dashness", "passProps", "SVGcanvasProps", "arrowBodyProps", "arrowHeadProps", "divContainerProps", "extendSVGcanvas"]); | ||
let { startAnchor, endAnchor, label, color, lineColor, headColor, strokeWidth, showHead, headSize, showTail, tailColor, tailSize, path, curveness, dashness, passProps, SVGcanvasProps, arrowBodyProps, arrowHeadProps, arrowTailProps, divContainerProps, SVGcanvasStyle, divContainerStyle, _extendSVGcanvas, _debug, _cpx1Offset, _cpy1Offset, _cpx2Offset, _cpy2Offset } = props, extraProps = __rest(props, ["startAnchor", "endAnchor", "label", "color", "lineColor", "headColor", "strokeWidth", "showHead", "headSize", "showTail", "tailColor", "tailSize", "path", "curveness", "dashness", "passProps", "SVGcanvasProps", "arrowBodyProps", "arrowHeadProps", "arrowTailProps", "divContainerProps", "SVGcanvasStyle", "divContainerStyle", "_extendSVGcanvas", "_debug", "_cpx1Offset", "_cpy1Offset", "_cpx2Offset", "_cpy2Offset"]); | ||
const selfRef = react_1.useRef(null); | ||
@@ -52,3 +51,3 @@ const [anchorsRefs, setAnchorsRefs] = react_1.useState({ start: null, end: null }); | ||
/** | ||
* determine a an update is needed and update if so. | ||
* 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 | ||
@@ -94,2 +93,8 @@ * of the elements has changed(it points to a different element). | ||
}; | ||
const monitorDOMchanges = () => { | ||
window.addEventListener("resize", updateIfNeeded); | ||
}; | ||
const cleanMonitorDOMchanges = () => { | ||
window.removeEventListener("resize", updateIfNeeded); | ||
}; | ||
react_1.useEffect(() => { | ||
@@ -99,2 +104,6 @@ // console.log("xarrow mounted"); | ||
initAnchorsRefs(); | ||
monitorDOMchanges(); | ||
return () => { | ||
cleanMonitorDOMchanges(); | ||
}; | ||
}, []); | ||
@@ -124,2 +133,3 @@ react_1.useLayoutEffect(() => { | ||
headOrient: 0, | ||
tailOrient: 0, | ||
labelStartPos: { x: 0, y: 0 }, | ||
@@ -130,2 +140,3 @@ labelMiddlePos: { x: 0, y: 0 }, | ||
arrowHeadOffset: { x: 0, y: 0 }, | ||
arrowTailOffset: { x: 0, y: 0 }, | ||
headOffset: 0, | ||
@@ -140,2 +151,3 @@ excRight: 0, | ||
headColor = headColor ? headColor : color; | ||
tailColor = tailColor ? tailColor : color; | ||
lineColor = lineColor ? lineColor : color; | ||
@@ -209,2 +221,3 @@ let dashStroke = 0, dashNone = 0, animationSpeed, animationDirection = 1; | ||
let headOrient = 0; | ||
let tailOrient = 0; | ||
// convert startAnchor and endAnchor to list of objects represents allowed anchors. | ||
@@ -227,2 +240,3 @@ let startPointsObj = anchors_1.prepareAnchorLines(startAnchor, sPos); | ||
let headOffset = ((headSize * 3) / 4) * strokeWidth; | ||
let tailOffset = ((tailSize * 3) / 4) * strokeWidth; | ||
let cu = Number(curveness); | ||
@@ -233,10 +247,12 @@ if (path === "straight") { | ||
} | ||
let excRight = strokeWidth + (strokeWidth * headSize) / 2; | ||
let excLeft = strokeWidth + (strokeWidth * headSize) / 2; | ||
let excUp = strokeWidth + (strokeWidth * headSize) / 2; | ||
let excDown = strokeWidth + (strokeWidth * headSize) / 2; | ||
excLeft += Number(extendSVGcanvas); | ||
excRight += Number(extendSVGcanvas); | ||
excUp += Number(extendSVGcanvas); | ||
excDown += Number(extendSVGcanvas); | ||
let biggerSide = headSize > tailSize ? headSize : tailSize; | ||
let calc = strokeWidth + (strokeWidth * biggerSide) / 2; | ||
let excRight = calc; | ||
let excLeft = calc; | ||
let excUp = calc; | ||
let excDown = calc; | ||
excLeft += Number(_extendSVGcanvas); | ||
excRight += Number(_extendSVGcanvas); | ||
excUp += Number(_extendSVGcanvas); | ||
excDown += Number(_extendSVGcanvas); | ||
//////////////////////////////////// | ||
@@ -253,19 +269,41 @@ // arrow point to point calculations | ||
let yHeadOffset = 0; | ||
let xTailOffset = 0; | ||
let yTailOffset = 0; | ||
if (cu === 0) { | ||
// in case of straight path | ||
let headAngel = Math.atan(absDy / absDx); | ||
x2 -= headOffset * xSign * Math.cos(headAngel); | ||
y2 -= headOffset * ySign * Math.sin(headAngel); | ||
// cpx2 -= headOffset * xSign * Math.cos(headAngel); | ||
// cpy2 -= headOffset * ySign * Math.sin(headAngel); | ||
headAngel *= ySign; | ||
if (xSign < 0) | ||
headAngel = (Math.PI - headAngel * xSign) * xSign; | ||
xHeadOffset = | ||
(Math.cos(headAngel) * headOffset) / 3 - | ||
(Math.sin(headAngel) * (headSize * strokeWidth)) / 2; | ||
yHeadOffset = | ||
(Math.cos(headAngel) * (headSize * strokeWidth)) / 2 + | ||
(Math.sin(headAngel) * headOffset) / 3; | ||
headOrient = (headAngel * 180) / Math.PI; | ||
if (showHead) { | ||
x2 -= headOffset * xSign * Math.cos(headAngel); | ||
y2 -= headOffset * ySign * Math.sin(headAngel); | ||
// cpx2 -= headOffset * xSign * Math.cos(headAngel); | ||
// cpy2 -= headOffset * ySign * Math.sin(headAngel); | ||
headAngel *= ySign; | ||
if (xSign < 0) | ||
headAngel = (Math.PI - headAngel * xSign) * xSign; | ||
xHeadOffset = | ||
(Math.cos(headAngel) * headOffset) / 3 - | ||
(Math.sin(headAngel) * (headSize * strokeWidth)) / 2; | ||
yHeadOffset = | ||
(Math.cos(headAngel) * (headSize * strokeWidth)) / 2 + | ||
(Math.sin(headAngel) * headOffset) / 3; | ||
headOrient = (headAngel * 180) / Math.PI; | ||
} | ||
let tailAngel = Math.atan(absDy / absDx); | ||
if (showTail) { | ||
x1 += tailOffset * xSign * Math.cos(tailAngel); | ||
y1 += tailOffset * ySign * Math.sin(tailAngel); | ||
// cpx2 -= headOffset * xSign * Math.cos(headAngel); | ||
// cpy2 -= headOffset * ySign * Math.sin(headAngel); | ||
tailAngel *= -ySign; | ||
if (xSign > 0) | ||
tailAngel = (Math.PI - tailAngel * xSign) * xSign; | ||
xTailOffset = | ||
(Math.cos(tailAngel) * tailOffset) / 3 - | ||
(Math.sin(tailAngel) * (tailSize * strokeWidth)) / 2; | ||
yTailOffset = | ||
(Math.cos(tailAngel) * (tailSize * strokeWidth)) / 2 + | ||
(Math.sin(tailAngel) * tailOffset) / 3; | ||
tailOrient = (tailAngel * 180) / Math.PI; | ||
} | ||
//todo: add tail support | ||
} | ||
@@ -283,34 +321,70 @@ else { | ||
} | ||
if (["left", "right"].includes(endAnchorPosition)) { | ||
x2 -= headOffset * xSign; | ||
// cpx2 -= headOffset * xSign * 2; | ||
// cpx1 += headOffset * xSign; | ||
xHeadOffset = (headOffset * xSign) / 3; | ||
yHeadOffset = (headSize * strokeWidth * xSign) / 2; | ||
if (endAnchorPosition === "left") { | ||
headOrient = 0; | ||
if (showHead) { | ||
if (["left", "right"].includes(endAnchorPosition)) { | ||
x2 -= headOffset * xSign; | ||
// cpx2 -= headOffset * xSign * 2; | ||
// cpx1 += headOffset * xSign; | ||
xHeadOffset = (headOffset * xSign) / 3; | ||
yHeadOffset = (headSize * strokeWidth * xSign) / 2; | ||
if (endAnchorPosition === "left") { | ||
headOrient = 0; | ||
if (xSign < 0) | ||
headOrient += 180; | ||
} | ||
else { | ||
headOrient = 180; | ||
if (xSign > 0) | ||
headOrient += 180; | ||
} | ||
} | ||
else if (["top", "bottom"].includes(endAnchorPosition)) { | ||
yHeadOffset = (headOffset * ySign) / 3; | ||
xHeadOffset = (headSize * strokeWidth * -ySign) / 2; | ||
y2 -= headOffset * ySign; | ||
// cpy1 += headOffset * ySign; | ||
// cpy2 -= headOffset * ySign; | ||
if (endAnchorPosition === "top") { | ||
headOrient = 270; | ||
if (ySign > 0) | ||
headOrient += 180; | ||
} | ||
else { | ||
headOrient = 90; | ||
if (ySign < 0) | ||
headOrient += 180; | ||
} | ||
} | ||
} | ||
} | ||
if (showTail && cu !== 0) { | ||
if (["left", "right"].includes(startAnchorPosition)) { | ||
x1 += tailOffset * xSign; | ||
xTailOffset = -(tailOffset * xSign) / 3; | ||
yTailOffset = -(tailSize * strokeWidth * xSign) / 2; | ||
if (startAnchorPosition === "left") { | ||
tailOrient = 180; | ||
if (xSign < 0) | ||
headOrient += 180; | ||
tailOrient += 180; | ||
} | ||
else { | ||
headOrient = 180; | ||
tailOrient = 0; | ||
if (xSign > 0) | ||
headOrient += 180; | ||
tailOrient += 180; | ||
} | ||
} | ||
else if (["top", "bottom"].includes(endAnchorPosition)) { | ||
yHeadOffset = (headOffset * ySign) / 3; | ||
xHeadOffset = (headSize * strokeWidth * -ySign) / 2; | ||
y2 -= headOffset * ySign; | ||
// cpy1 += headOffset * ySign; | ||
// cpy2 -= headOffset * ySign; | ||
if (endAnchorPosition === "top") { | ||
headOrient = 270; | ||
else if (["top", "bottom"].includes(startAnchorPosition)) { | ||
yTailOffset = -(tailOffset * ySign) / 3; | ||
xTailOffset = -(tailSize * strokeWidth * -ySign) / 2; | ||
y1 += tailOffset * ySign; | ||
// cpy1 += tailOffset * ySign; | ||
// cpy2 -= tailOffset * ySign; | ||
if (startAnchorPosition === "top") { | ||
tailOrient = 90; | ||
if (ySign > 0) | ||
headOrient += 180; | ||
tailOrient += 180; | ||
} | ||
else { | ||
headOrient = 90; | ||
tailOrient = 270; | ||
if (ySign < 0) | ||
headOrient += 180; | ||
tailOrient += 180; | ||
} | ||
@@ -321,2 +395,3 @@ } | ||
let arrowHeadOffset = { x: xHeadOffset, y: yHeadOffset }; | ||
let arrowTailOffset = { x: xTailOffset, y: yTailOffset }; | ||
let cpx1 = x1, cpy1 = y1, cpx2 = x2, cpy2 = y2; | ||
@@ -360,8 +435,28 @@ let curvesPossibilities = {}; | ||
hh: () => { | ||
cpx1 += (absDx * 0.5 - headOffset / 2) * xSign; | ||
cpx2 -= (absDx * 0.5 - headOffset / 2) * xSign; | ||
// cpx1 += (absDx * 0.5 - headOffset / 2) * xSign; | ||
// cpx2 -= (absDx * 0.5 - headOffset / 2) * xSign; | ||
cpx1 += absDx * 0.5 * xSign; | ||
cpx2 -= absDx * 0.5 * xSign; | ||
if (showHead) { | ||
cpx1 -= (headOffset / 2) * xSign; | ||
cpx2 += (headOffset / 2) * xSign; | ||
} | ||
if (showTail) { | ||
cpx1 -= (tailOffset / 2) * xSign; | ||
cpx2 += (tailOffset / 2) * xSign; | ||
} | ||
}, | ||
vv: () => { | ||
cpy1 += (absDy * 0.5 - headOffset / 2) * ySign; | ||
cpy2 -= (absDy * 0.5 - headOffset / 2) * ySign; | ||
// cpy1 += (absDy * 0.5 - headOffset / 2) * ySign; | ||
// cpy2 -= (absDy * 0.5 - headOffset / 2) * ySign; | ||
cpy1 += absDy * 0.5 * ySign; | ||
cpy2 -= absDy * 0.5 * ySign; | ||
if (showHead) { | ||
cpy1 -= (headOffset / 2) * ySign; | ||
cpy2 += (headOffset / 2) * ySign; | ||
} | ||
if (showTail) { | ||
cpy1 -= (tailOffset / 2) * ySign; | ||
cpy2 += (tailOffset / 2) * ySign; | ||
} | ||
}, | ||
@@ -395,2 +490,6 @@ hv: () => { | ||
curvesPossibilities[selectedCurviness](); | ||
cpx1 += _cpx1Offset; | ||
cpy1 += _cpy1Offset; | ||
cpx2 += _cpx2Offset; | ||
cpy2 += _cpy2Offset; | ||
//////////////////////////////////// | ||
@@ -408,2 +507,8 @@ // canvas smart size adjustments | ||
excDown += ySol2 - absDy; | ||
if (path === "grid") { | ||
excLeft += calc; | ||
excRight += calc; | ||
excUp += calc; | ||
excDown += calc; | ||
} | ||
x1 += excLeft; | ||
@@ -445,2 +550,3 @@ x2 += excLeft; | ||
headOrient, | ||
tailOrient, | ||
labelStartPos, | ||
@@ -456,7 +562,12 @@ labelMiddlePos, | ||
arrowHeadOffset, | ||
arrowTailOffset, | ||
}); | ||
}; | ||
const fHeadSize = headSize * strokeWidth; //factored headsize | ||
const fHeadSize = headSize * strokeWidth; //factored head size | ||
const fTailSize = tailSize * strokeWidth; //factored head size | ||
const xOffsetHead = st.x2 - st.arrowHeadOffset.x; | ||
const yOffsetHead = st.y2 - st.arrowHeadOffset.y; | ||
const xOffsetTail = st.x1 - st.arrowTailOffset.x; | ||
const yOffsetTail = st.y1 - st.arrowTailOffset.y; | ||
// console.log("x1,x2,tailOrient\n:", st.x1, st.x2, st.tailOrient); | ||
let arrowPath = `M ${st.x1} ${st.y1} C ${st.cpx1} ${st.cpy1}, ${st.cpx2} ${st.cpy2}, ${st.x2} ${st.y2} `; | ||
@@ -467,16 +578,11 @@ if (path === "straight") | ||
arrowPath = `M ${st.x1} ${st.y1} L ${st.cpx1} ${st.cpy1} L ${st.cpx2} ${st.cpy2} L ${st.x2} ${st.y2}`; | ||
return (react_1.default.createElement("div", Object.assign({ style: { position: "absolute" } }, divContainerProps, extraProps), | ||
react_1.default.createElement("svg", Object.assign({ ref: selfRef, width: st.cw, height: st.ch, style: { | ||
// border: "2px yellow dashed", | ||
position: "absolute", | ||
left: st.cx0, | ||
top: st.cy0, | ||
pointerEvents: "none", | ||
}, overflow: "auto" }, SVGcanvasProps), | ||
react_1.default.createElement("path", Object.assign({ d: arrowPath, stroke: lineColor, strokeDasharray: `${dashStroke} ${dashNone}`, strokeWidth: strokeWidth, fill: "transparent", | ||
// markerEnd={`url(#${arrowHeadId})`} | ||
pointerEvents: "visibleStroke" }, passProps, arrowBodyProps), animationSpeed ? (react_1.default.createElement("animate", { attributeName: "stroke-dashoffset", values: `${dashoffset * animationDirection};0`, dur: `${1 / animationSpeed}s`, repeatCount: "indefinite" })) : null), | ||
react_1.default.createElement("path", Object.assign({ d: `M 0 0 L ${fHeadSize} ${fHeadSize / 2} L 0 ${fHeadSize} L ${fHeadSize / 4} ${fHeadSize / 2} z`, fill: headColor, | ||
// pointerEvents="all" | ||
transform: `translate(${xOffsetHead},${yOffsetHead}) rotate(${st.headOrient})` }, passProps, arrowHeadProps))), | ||
return (react_1.default.createElement("div", Object.assign({}, divContainerProps, { style: Object.assign({ position: "absolute" }, divContainerStyle) }, extraProps), | ||
react_1.default.createElement("svg", Object.assign({ ref: selfRef, width: st.cw, height: st.ch, style: Object.assign({ position: "absolute", left: st.cx0, top: st.cy0, pointerEvents: "none", border: _debug ? "1px dashed yellow" : null }, SVGcanvasStyle), overflow: "auto" }, SVGcanvasProps), | ||
_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, | ||
showTail ? (react_1.default.createElement("path", Object.assign({ d: `M 0 0 L ${fTailSize} ${fTailSize / 2} L 0 ${fTailSize} L ${fTailSize / 4} ${fTailSize / 2} z`, fill: tailColor, pointerEvents: "all", transform: `translate(${xOffsetTail},${yOffsetTail}) rotate(${st.tailOrient})` }, passProps, arrowTailProps))) : null, | ||
react_1.default.createElement("path", Object.assign({ d: arrowPath, stroke: lineColor, strokeDasharray: `${dashStroke} ${dashNone}`, strokeWidth: strokeWidth, fill: "transparent", pointerEvents: "visibleStroke" }, passProps, arrowBodyProps), animationSpeed ? (react_1.default.createElement("animate", { attributeName: "stroke-dashoffset", values: `${dashoffset * animationDirection};0`, dur: `${1 / animationSpeed}s`, repeatCount: "indefinite" })) : null), | ||
showHead ? (react_1.default.createElement("path", Object.assign({ d: `M 0 0 L ${fHeadSize} ${fHeadSize / 2} L 0 ${fHeadSize} L ${fHeadSize / 4} ${fHeadSize / 2} z`, fill: headColor, pointerEvents: "all", transform: `translate(${xOffsetHead},${yOffsetHead}) rotate(${st.headOrient})` }, passProps, arrowHeadProps))) : null), | ||
labelStart ? (react_1.default.createElement("div", { style: { | ||
@@ -505,2 +611,4 @@ transform: st.dx < 0 ? "translate(-100% , -50%)" : "translate(-0% , -50%)", | ||
}; | ||
////////////////////////////// | ||
// propTypes | ||
const pAnchorPositionType = prop_types_1.default.oneOf([ | ||
@@ -514,3 +622,3 @@ "middle", | ||
]); | ||
const pAnchorCustomPositionType = prop_types_1.default.shape({ | ||
const pAnchorCustomPositionType = prop_types_1.default.exact({ | ||
position: pAnchorPositionType.isRequired, | ||
@@ -520,10 +628,19 @@ offset: prop_types_1.default.shape({ | ||
bottomness: prop_types_1.default.number, | ||
}), | ||
}).isRequired, | ||
}); | ||
const pAnchorType = prop_types_1.default.oneOfType([ | ||
const _pAnchorType = prop_types_1.default.oneOfType([ | ||
pAnchorPositionType, | ||
pAnchorCustomPositionType, | ||
prop_types_1.default.arrayOf(prop_types_1.default.oneOfType([pAnchorPositionType, pAnchorCustomPositionType])), | ||
]); | ||
const pRefType = prop_types_1.default.oneOfType([prop_types_1.default.string, prop_types_1.default.object]); | ||
const pAnchorType = prop_types_1.default.oneOfType([_pAnchorType, prop_types_1.default.arrayOf(_pAnchorType)]); | ||
const pRefType = prop_types_1.default.oneOfType([ | ||
prop_types_1.default.string, | ||
prop_types_1.default.exact({ current: prop_types_1.default.instanceOf(Element) }), | ||
]); | ||
const _pLabelType = prop_types_1.default.oneOfType([prop_types_1.default.element, prop_types_1.default.string]); | ||
const pLabelsType = prop_types_1.default.exact({ | ||
start: _pLabelType, | ||
middle: _pLabelType, | ||
end: _pLabelType, | ||
}); | ||
Xarrow.propTypes = { | ||
@@ -534,8 +651,12 @@ start: pRefType.isRequired, | ||
endAnchor: pAnchorType, | ||
label: prop_types_1.default.oneOfType([prop_types_1.default.elementType, prop_types_1.default.object]), | ||
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, | ||
headSize: prop_types_1.default.number, | ||
showTail: prop_types_1.default.bool, | ||
path: prop_types_1.default.oneOf(["smooth", "grid", "straight"]), | ||
@@ -547,5 +668,11 @@ curveness: prop_types_1.default.number, | ||
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, | ||
_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, | ||
}; | ||
@@ -559,4 +686,8 @@ Xarrow.defaultProps = { | ||
headColor: null, | ||
tailColor: null, | ||
strokeWidth: 4, | ||
showHead: true, | ||
headSize: 6, | ||
showTail: false, | ||
tailSize: 6, | ||
path: "smooth", | ||
@@ -568,7 +699,13 @@ curveness: 0.8, | ||
arrowHeadProps: {}, | ||
arrowTailProps: {}, | ||
SVGcanvasProps: {}, | ||
divContainerProps: {}, | ||
extendSVGcanvas: 0, | ||
_extendSVGcanvas: 0, | ||
_debug: false, | ||
_cpx1Offset: 0, | ||
_cpy1Offset: 0, | ||
_cpx2Offset: 0, | ||
_cpy2Offset: 0, | ||
}; | ||
exports.default = Xarrow; | ||
//# sourceMappingURL=index.js.map |
@@ -19,7 +19,7 @@ "use strict"; | ||
let defsOffsets = getAnchorsDefaultOffsets(anchorPos.right - anchorPos.x, anchorPos.bottom - anchorPos.y); | ||
// convert given anchors to array if array not already given | ||
// convert given anchors to array if not array already | ||
let anchorChoice = Array.isArray(anchor) ? anchor : [anchor]; | ||
if (anchorChoice.length == 0) | ||
anchorChoice = ["auto"]; | ||
//now map each item in the list to relevant object | ||
//now map each item in the array to relevant object | ||
let anchorChoiceMapped = anchorChoice.map((anchorChoice) => { | ||
@@ -26,0 +26,0 @@ if (index_1.typeOf(anchorChoice) === "string") { |
@@ -0,5 +1,6 @@ | ||
import { refType } from "../index"; | ||
declare type extendedJtypes = "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "null" | "array"; | ||
export declare const getElementByPropGiven: (ref: any) => HTMLElement; | ||
export declare const getElementByPropGiven: (ref: refType) => HTMLElement; | ||
export declare const typeOf: (arg: any) => extendedJtypes; | ||
export {}; | ||
//# sourceMappingURL=index.d.ts.map |
{ | ||
"name": "react-xarrows", | ||
"version": "1.5.2", | ||
"version": "1.6.0", | ||
"author": "Eliav Louski", | ||
@@ -30,2 +30,3 @@ "description": "Draw arrows (or lines) between components in React!", | ||
"dependencies": { | ||
"@types/prop-types": "^15.7.3", | ||
"lodash.isequal": "^4.5.0", | ||
@@ -40,2 +41,5 @@ "lodash.pick": "^4.4.0", | ||
"@types/react": "^16.9.19", | ||
"@typescript-eslint/parser": "^4.15.2", | ||
"eslint-import-resolver-typescript": "^2.4.0", | ||
"eslint-plugin-import": "^2.22.1", | ||
"react": "^16.12.0", | ||
@@ -42,0 +46,0 @@ "typescript": "^3.7.5" |
285
README.md
@@ -36,37 +36,39 @@ # react-xarrows | ||
[see here!](https://codesandbox.io/embed/github/Eliav2/react-xarrows/tree/master/examples?fontsize=14&hidenavigation=1&theme=dark) codebox of few examples(in this repo at /examples). | ||
[see here!](https://codesandbox.io/embed/github/Eliav2/react-xarrows/tree/master/examples?fontsize=14&hidenavigation=1&theme=dark) | ||
codebox of few examples(in this repo at /examples). | ||
![react-xarrow-picture-1 4 2](https://user-images.githubusercontent.com/47307889/87698325-facfc480-c79b-11ea-976a-dbad0ecd9b48.png) | ||
see this interactive example: https://lwwwp.csb.app/CustomizeArrow | ||
![react-xarrows-v1 6](https://user-images.githubusercontent.com/47307889/113949468-070f1c80-9818-11eb-90e6-ddc6d814b912.gif) | ||
### simple example: | ||
```jsx | ||
import React, { useRef } from "react"; | ||
import React, {useRef} from "react"; | ||
import Xarrow from "react-xarrows"; | ||
const boxStyle = { | ||
border: "grey solid 2px", | ||
borderRadius: "10px", | ||
padding: "5px", | ||
border: "grey solid 2px", | ||
borderRadius: "10px", | ||
padding: "5px", | ||
}; | ||
function SimpleExample() { | ||
const box1Ref = useRef(null); | ||
return ( | ||
<div | ||
style={{ display: "flex", justifyContent: "space-evenly", width: "100%" }} | ||
> | ||
<div ref={box1Ref} style={boxStyle}> | ||
hey | ||
</div> | ||
<p id="elem2" style={boxStyle}> | ||
hey2 | ||
</p> | ||
<Xarrow | ||
start={box1Ref} //can be react ref | ||
end="elem2" //or an id | ||
/> | ||
</div> | ||
); | ||
const box1Ref = useRef(null); | ||
return ( | ||
<div style={{display: "flex", justifyContent: "space-evenly", width: "100%"}}> | ||
<div ref={box1Ref} style={boxStyle}> | ||
hey | ||
</div> | ||
<p id="elem2" style={boxStyle}> | ||
hey2 | ||
</p> | ||
<Xarrow | ||
start={box1Ref} //can be react ref | ||
end="elem2" //or an id | ||
/> | ||
</div> | ||
); | ||
} | ||
@@ -77,44 +79,72 @@ | ||
## The API | ||
## Usage | ||
react-xarrows does not renders automatically if one of the connected elements is rendered. You have to manually trigger | ||
an update on the arrows whenever one of the connected elements renders(possibaly by trigger update on the parent of the | ||
arrows) ,this is because the Xarrow component does not have any control or awareness of the connected elements. in | ||
addition, make sure to render Xarrows later in the DOM then the connected elements else the app will crash. | ||
**this is planned to be changed in react-xarrows v2.** | ||
### types definitions | ||
the properties the xarrow component receives is as follow: | ||
the properties the xarrow component receives is as follows(don't panic,the important ones exmplained next): | ||
```js | ||
```ts | ||
export type xarrowPropsType = { | ||
start: refType; | ||
end: refType; | ||
startAnchor?: anchorType | anchorType[]; | ||
endAnchor?: anchorType | anchorType[]; | ||
label?: labelType | labelsType; | ||
color?: string; | ||
lineColor?: string | null; | ||
headColor?: string | null; | ||
strokeWidth?: number; | ||
headSize?: number; | ||
path: "smooth" | "grid" | "straight"; | ||
curveness?: number; | ||
dashness?: boolean | { strokeLen?: number; nonStrokeLen?: number; animation?: boolean | number }; | ||
passProps?: React.SVGProps<SVGPathElement>; | ||
extendSVGcanvas?: number; | ||
SVGcanvasProps?: React.SVGAttributes<SVGSVGElement>; | ||
arrowBodyProps?: React.SVGProps<SVGPathElement>; | ||
arrowHeadProps?: React.SVGProps<SVGPathElement>; | ||
divContainerProps?: React.HTMLProps<HTMLDivElement>; | ||
start: refType; | ||
end: refType; | ||
startAnchor?: anchorType | anchorType[]; | ||
endAnchor?: anchorType | anchorType[]; | ||
label?: labelType | labelsType; | ||
color?: string; | ||
lineColor?: string | null; | ||
headColor?: string | null; | ||
tailColor?: string | null; | ||
strokeWidth?: number; | ||
showHead?: boolean; | ||
headSize?: number; | ||
showTail?: boolean; | ||
tailSize?: number; | ||
path?: "smooth" | "grid" | "straight"; | ||
curveness?: number; | ||
dashness?: | boolean | { strokeLen?: number; nonStrokeLen?: number; animation?: boolean | number; }; | ||
passProps?: React.SVGProps<SVGPathElement>; | ||
SVGcanvasProps?: React.SVGAttributes<SVGSVGElement>; | ||
arrowBodyProps?: React.SVGProps<SVGPathElement>; | ||
arrowHeadProps?: React.SVGProps<SVGPathElement>; | ||
arrowTailProps?: React.SVGProps<SVGPathElement>; | ||
divContainerProps?: React.HTMLProps<HTMLDivElement>; | ||
SVGcanvasStyle?: React.CSSProperties; | ||
divContainerStyle?: React.CSSProperties; | ||
_extendSVGcanvas?: number; | ||
_debug: boolean; | ||
_cpx1Offset: number; | ||
_cpy1Offset: number; | ||
_cpx2Offset: number; | ||
_cpy2Offset: number; | ||
}; | ||
export type anchorType = anchorPositionType | anchorCustomPositionType; | ||
export type anchorPositionType = "middle" | "left" | "right" | "top" | "bottom" | "auto"; | ||
export type anchorPositionType = | "middle" | "left" | "right" | "top" | "bottom" | "auto"; | ||
export type anchorCustomPositionType = { | ||
position: anchorPositionType; | ||
offset: { rightness: number; bottomness: number }; | ||
position: anchorPositionType; | ||
offset: { rightness?: number; bottomness?: number }; | ||
}; | ||
export type reactRefType = { current: null | HTMLElement }; | ||
export type refType = reactRefType | string; | ||
export type labelsType = { start?: labelType; middle?: labelType; end?: labelType }; | ||
export type labelType = JSX.Element; | ||
export type refType = React.MutableRefObject<any> | string; | ||
export type labelsType = { | ||
start?: labelType; | ||
middle?: labelType; | ||
end?: labelType; | ||
}; | ||
export type labelType = JSX.Element | string; | ||
export type domEventType = keyof GlobalEventHandlersEventMap; | ||
``` | ||
##### API flexibility | ||
This API has built in such way that most props can accept different types. you can keep things simple or provide more detailed props for more custom behavior - the API except both(see `startAnchor` or `label` properties for good examples).<br/> | ||
##### API flexibility | ||
This API is built in such way that most props can accept different types. you can keep things simple or provide more | ||
detailed props for more custom behavior - the API except both(see [`startAnchor`](#startAnchor-and-endAnchor) or `label` | ||
properties for good examples) | ||
.<br/> | ||
see typescript types above for detailed descriptions of what type excepts every prop. | ||
@@ -124,50 +154,65 @@ | ||
this documentation is examples driven. | ||
#### 'start' and 'end' | ||
_required_\ | ||
can be a reference to a react ref to html element or string - an id of a DOM element. | ||
examples: | ||
- `start="myid"` - `myid` is id of a dom element. | ||
- `start={myRef}` - `myRef` is a react ref. | ||
#### 'startAnchor' and 'endAnchor' | ||
_required_\ | ||
each anchor can be: `"auto" | "middle" | "left" | "right" | "top" | "bottom"`. | ||
`auto` will choose automatically the path with the smallest length. | ||
can also be a list of possible anchors. if list is provided - the minimal length anchors will be choose from the list. | ||
you can also offset each anchor passing `offset`. | ||
`auto` will choose automatically the path with the smallest length. can also be a list of possible anchors. if list is | ||
provided - the minimal length anchors will be choose from the list. you can also offset each anchor passing `offset`. | ||
examples: | ||
- `endAnchor="middle"` will set the anchor of the end of the line to the middle of the end element. | ||
- `endAnchor= { position: "auto", offset: { rightness: 20 } }` will choose automatic anchoring for end anchor but will offset it 20 pixels to the right after normal positioning. | ||
- `endAnchor= ["right", {position: "left", offset: {bottomness: -10}}]` only right and left anchors will be allowed for endAnchor, and if the left side connected then it will be offset 10 pixels up. | ||
- `endAnchor= { position: "auto", offset: { rightness: 20 } }` will choose automatic anchoring for end anchor but will | ||
offset it 20 pixels to the right after normal positioning. | ||
- `endAnchor= ["right", {position: "left", offset: {bottomness: -10}}]` only right and left anchors will be allowed for | ||
endAnchor, and if the left side connected then it will be offset 10 pixels up. | ||
#### label | ||
can be a string that will default to be at the middle or an object that describes where to place label and how to customize it. see `label` at `xarrowPropsType` above. | ||
examples: | ||
_optional, default: null_ \ | ||
you can place up to 3 labels. see examples | ||
- `label="middleLabel"` | ||
- `label=<div style={{ fontSize: "1.3em", fontFamily: "fantasy", fontStyle: "italic" }}>styled middle label</div>` | ||
- `label="middleLabel"` - middle label | ||
- `label=<div style={{ fontSize: "1.3em", fontFamily: "fantasy", fontStyle: "italic" }}>styled middle label</div>` - | ||
custom middle label | ||
- `label={{ start:"I'm start label",middle: "middleLabel",end:<div style={{ fontSize: "1.3em", fontFamily: "fantasy", fontStyle: "italic" }}>big end label</div> }}` | ||
- start and middle label and custom end label | ||
#### color,lineColor and headColor | ||
#### color,lineColor and headColor and tailColor | ||
color defines color for all the arrow include head. if lineColor or headColor is given so it overrides color specifically for line or head. | ||
examples: | ||
_optional, default: "CornflowerBlue"_ \ | ||
`color` defines color to the entire arrow. lineColor,headColor and tailColor will override color specifically for | ||
line,tail or head. examples: | ||
- `color="red"` will change the color of the arrow to red(body and head). | ||
- `headColor="red"` will change the color of the head of the arrow to red. | ||
- `lineColor="red"` will change the color of the body of the arrow to red. | ||
- `headColor="red"` will change only the color of the head to red. | ||
- `tailColor="red"` will change only the color of the tail to red. | ||
- `lineColor="red"` will change only the color of the body to red. | ||
(NOTE - maybe `headColor` and `lineColor` will be combined to color) | ||
#### strokeWidth and headSize and tailSize | ||
#### strokeWidth and headSize | ||
_optional, default: 6_ \ | ||
strokeWidth defines the thickness of the entire arrow. headSize and tailSize defines how big will be the head or tail | ||
relative to the strokeWidth. examples: | ||
strokeWidth defines the thickness of the arrow(line and head). | ||
headSize defines how big will be the head relative to the line(set headSize to 0 to make the arrow like a line). | ||
examples: | ||
- `strokeWidth={15}` will make the arrow more thick(body and head). | ||
- `headSize={15}` will make the head of the arrow more thick(relative to strokeWidth as well). | ||
- `tailSize={15}` will make arrow's tail thicker. | ||
#### path | ||
`path` can be one of: `"smooth" | "grid" | "straight"`, and it controls the path arrow is drawn, exactly how their name suggest. | ||
examples: | ||
_optional, default: "smooth"_ \ | ||
`path` can be one of: `"smooth" | "grid" | "straight"`, and it controls the path arrow is drawn, exactly how their name | ||
suggest. examples: | ||
@@ -178,4 +223,4 @@ - `path={"grid"}` will draw the line in sharp curves(90 degrees) like grid. | ||
defines how much the lines curve. | ||
examples: | ||
_optional, default: 0.8_ \ | ||
defines how much the lines curve. examples: | ||
@@ -188,5 +233,5 @@ - `curveness={false}` will make the line straight without curves(exactly like path='straight'). | ||
can make the arrow dashed and can even animate. | ||
if true default values(for dashness) are choose. if object is passed then default values are choose except what passed. | ||
examples: | ||
_optional, default: false_ \ | ||
can make the arrow dashed and can even animate. if true default values(for dashness) are chosen. if object is passed | ||
then default values are chosen except what passed. examples: | ||
@@ -198,12 +243,13 @@ - `dashness={true}` will make the line of the arrow to be dashed. | ||
The xarrow is fully customizable, and you can pass props to any part of the component. | ||
if unlisted(unknown) property is passed to xarrow so by default it'll be passed down to `divConatiner`. | ||
The xarrow is fully customizable, and you can pass props to any part of the component. if unlisted(unknown) property is | ||
passed to xarrow so by default it'll be passed down to `divConatiner`. | ||
#### passProps | ||
you can pass properties to visible parts(body and head) of the arrow (such event handlers and much more). | ||
this supposed to be enough for most cases. | ||
examples: | ||
_optional, default: {}_ \ | ||
you can pass properties to visible parts(body and head) of the arrow (such event handlers and much more). this supposed | ||
to be enough for most cases. examples: | ||
- `passProps= {{onClick: () => console.log("xarrow clicked!")}}` - now the arrow will console log a message when clicked. | ||
- `passProps= {{onClick: () => console.log("xarrow clicked!")}}` - now the arrow will console log a message when | ||
clicked. | ||
- `passProps= {{cursor: "pointer"}}` - now the cursor will change to pointer style when hovering over Xarrow. | ||
@@ -218,6 +264,11 @@ - `passProps= {{pointerEvents: "none"}}` - now the user cannot interact with Xarrow via mouse events. | ||
_optional, default: {}_ \ | ||
![image](https://user-images.githubusercontent.com/47307889/95031511-09ed5100-06bf-11eb-95a3-4cdc8d0be0ad.png) | ||
if you wish you can pass props specific part of the component. | ||
- `divContainerProps` - the container which contains the SVG canvas, and the optional labels elements. It takes no place, and located where you normaly placed him in the elements tree(no offset). The SVGcanvas and the labels will be placed in a offset from this div. | ||
- `divContainerProps` - the container which contains the SVG canvas, and the optional labels elements. It takes no | ||
place, and located where you normaly placed him in the elements tree(no offset). The SVGcanvas and the labels will be | ||
placed in a offset from this div. | ||
- `SVGcanvasProps` - the svg canvas which contains arrow head and body. | ||
@@ -227,17 +278,23 @@ - `arrowBodyProps` - the body of the arrow | ||
Note that `arrowBody` and `arrowHead` receives props of svg path element, `SVGcanvas` receives props of svg element, and `divContainerProps` of a div element. | ||
Note that `arrowBody` and `arrowHead` receives props of svg path element, `SVGcanvas` receives props of svg element, | ||
and `divContainerProps` of a div element. | ||
examples: | ||
- `arrowHead = {onClick: () => console.log("head clicked!")}` - now only the head will console log a message when clicked. | ||
- `arrowHead = {onClick: () => console.log("head clicked!")}` - now only the head will console log a message when | ||
clicked. | ||
##### _extendSVGcanvas | ||
##### extendSVGcanvas | ||
_optional, default: 0_ \ | ||
will extend the svg canvas at all sides. can be useful if for some reason the arrow(or labels) is cut though to small | ||
svg canvas(should be used in advanced custom arrows). example: `_extendSVGcanvas = {30}` - will extend svg canvas in all | ||
sides by 30 pixels. | ||
will extend the svg canvas at all sides. can be useful if for some reason the arrow(or labels) is cut though to small svg canvas(should be used in advanced custom arrows, for example if you used `dx` to move one of the labels and at exceeded the canvas). | ||
example: `advanced= {{extendSVGcanvas: 30 }}` - will extended svg canvas in all sides by 30 pixels. | ||
##### _cpx1Offset,_cpy1Offset,_cpx2Offset,_cpy2Offset | ||
##### passProps | ||
_optional, default: 0_ \ | ||
now you can manipulate and offset the control points of the arrow. this way you can control how the line curves. check | ||
out the interactive codesandbox, set _debug to true and play with these properties. | ||
### default props | ||
@@ -249,19 +306,24 @@ | ||
Xarrow.defaultProps = { | ||
startAnchor: "auto", | ||
endAnchor: "auto", | ||
label: null, | ||
color: "CornflowerBlue", | ||
lineColor: null, | ||
headColor: null, | ||
strokeWidth: 4, | ||
headSize: 6, | ||
path: "smooth", | ||
curveness: 0.8, | ||
dashness: false, | ||
passProps: {}, | ||
arrowBodyProps: {}, | ||
arrowHeadProps: {}, | ||
SVGcanvasProps: {}, | ||
divContainerProps: {}, | ||
extendSVGcanvas: 0, | ||
startAnchor: "auto", | ||
endAnchor: "auto", | ||
label: null, | ||
color: "CornflowerBlue", | ||
lineColor: null, | ||
headColor: null, | ||
strokeWidth: 4, | ||
headSize: 6, | ||
path: "smooth", | ||
curveness: 0.8, | ||
dashness: false, | ||
passProps: {}, | ||
arrowBodyProps: {}, | ||
arrowHeadProps: {}, | ||
SVGcanvasProps: {}, | ||
divContainerProps: {}, | ||
_extendSVGcanvas: 0, | ||
_debug: false, | ||
_cpx1Offset: 0, | ||
_cpy1Offset: 0, | ||
_cpx2Offset: 0, | ||
_cpy2Offset: 0, | ||
}; | ||
@@ -271,2 +333,3 @@ ``` | ||
## Versions | ||
See [CHANGELOG.md](https://github.com/Eliav2/react-xarrows/blob/master/CHANGELOG.md) in this repo. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
90031
914
326
5
6
20
+ Added@types/prop-types@^15.7.3
+ Added@types/prop-types@15.7.14(transitive)