react-xarrows
Advanced tools
Comparing version 1.2.2 to 1.3.0
import React from "react"; | ||
declare type xarrowPropsType = { | ||
export declare type xarrowPropsType = { | ||
start: refType; | ||
@@ -19,17 +19,29 @@ end: refType; | ||
}; | ||
monitorDOMchanges?: boolean; | ||
registerEvents?: registerEventsType[]; | ||
consoleWarning?: boolean; | ||
passProps?: React.SVGProps<SVGPathElement>; | ||
advanced?: { | ||
extendSVGcanvas?: number; | ||
passProps?: { | ||
SVGcanvas?: React.SVGAttributes<SVGSVGElement>; | ||
arrowBody?: React.SVGProps<SVGPathElement>; | ||
arrowHead?: React.SVGProps<SVGPathElement>; | ||
}; | ||
}; | ||
monitorDOMchanges?: boolean; | ||
registerEvents?: registerEventsType[]; | ||
}; | ||
declare type anchorType = anchorMethodType | anchorPositionType; | ||
declare type anchorMethodType = "auto"; | ||
declare type anchorPositionType = "middle" | "left" | "right" | "top" | "bottom"; | ||
declare type reactRefType = { | ||
export declare type anchorType = anchorPositionType | anchorCustomPositionType; | ||
export declare type anchorPositionType = "middle" | "left" | "right" | "top" | "bottom" | "auto"; | ||
export declare type anchorCustomPositionType = { | ||
position: anchorPositionType; | ||
offset: { | ||
rightness: number; | ||
bottomness: number; | ||
}; | ||
}; | ||
export declare type reactRefType = { | ||
current: null | HTMLElement; | ||
}; | ||
declare type refType = reactRefType | string; | ||
declare type labelsType = { | ||
export declare type refType = reactRefType | string; | ||
export declare type labelsType = { | ||
start?: labelType; | ||
@@ -39,9 +51,9 @@ middle?: labelType; | ||
}; | ||
declare type labelPropsType = { | ||
export declare type labelPropsType = { | ||
text: string; | ||
extra?: React.SVGAttributes<SVGTextElement>; | ||
}; | ||
declare type labelType = string | labelPropsType; | ||
declare type domEventType = keyof GlobalEventHandlersEventMap; | ||
declare type registerEventsType = { | ||
export declare type labelType = string | labelPropsType; | ||
export declare type domEventType = keyof GlobalEventHandlersEventMap; | ||
export declare type registerEventsType = { | ||
ref: refType; | ||
@@ -52,4 +64,3 @@ eventName: domEventType; | ||
declare const Xarrow: React.FC<xarrowPropsType>; | ||
export type { xarrowPropsType, anchorType, anchorMethodType, anchorPositionType, reactRefType, refType, labelsType, labelPropsType, labelType, domEventType, registerEventsType, }; | ||
export default Xarrow; | ||
//# sourceMappingURL=index.d.ts.map |
267
lib/index.js
@@ -0,3 +1,14 @@ | ||
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; | ||
}; | ||
import React, { useRef, useEffect, useState } from "react"; | ||
import lodash from "lodash"; | ||
import _ from "lodash"; | ||
const findCommonAncestor = (elem, elem2) => { | ||
@@ -14,2 +25,3 @@ function parents(node) { | ||
var parents2 = parents(node2); | ||
// if (parents1[0] !== parents2[0]) throw new Error("No common ancestor!"); | ||
if (parents1[0] !== parents2[0]) | ||
@@ -60,3 +72,14 @@ throw new Error("No common ancestor!"); | ||
}; | ||
const Xarrow = (props) => { | ||
const typeOf = (arg) => { | ||
let type = typeof arg; | ||
if (type === "object") { | ||
if (arg === null) | ||
type = "null"; | ||
else if (Array.isArray(arg)) | ||
type = "array"; | ||
} | ||
return type; | ||
}; | ||
const Xarrow = (_a) => { | ||
var props = __rest(_a, []); | ||
const selfRef = useRef(null); | ||
@@ -72,2 +95,3 @@ const [anchorsRefs, setAnchorsRefs] = useState({ start: null, end: null }); | ||
const updateIfNeeded = () => { | ||
// console.log("updateIfNeeded"); | ||
if (checkIfAnchorsRefsChanged()) { | ||
@@ -77,3 +101,3 @@ initAnchorsRefs(); | ||
} | ||
else if (!lodash.isEqual(props, prevProps)) { | ||
else if (!_.isEqual(props, prevProps)) { | ||
//first check if any properties changed | ||
@@ -88,3 +112,3 @@ if (prevProps) { | ||
let posState = getAnchorsPos(); | ||
if (!lodash.isEqual(prevPosState, posState)) { | ||
if (!_.isEqual(prevPosState, posState)) { | ||
setPrevPosState(posState); | ||
@@ -97,3 +121,3 @@ } | ||
var end = getElementByPropGiven(props.end); | ||
return !lodash.isEqual(anchorsRefs, { start, end }); | ||
return !_.isEqual(anchorsRefs, { start, end }); | ||
}; | ||
@@ -134,3 +158,3 @@ const monitorDOMchanges = () => { | ||
console.warn(`Xarrow warning: you placed Xarrow not as son of the common ancestor of 'start' component and 'end' component. | ||
the suggested element to put Xarrow inside of to prevent redundant rerenders is `, anchorsCommonAncestor, `if this was your intention set monitorDOMchanges to true so Xarrow will render whenever relevant DOM events are triggerd. | ||
the suggested element to put Xarrow inside of to prevent redundant rerenders iss `, anchorsCommonAncestor, " and not ", selfRef.current.parentElement, `if this was your intention set monitorDOMchanges to true so Xarrow will render whenever relevant DOM events are triggerd. | ||
to disable this warnings set consoleWarning property to false`); | ||
@@ -140,16 +164,6 @@ if ((allAncestorChildrensStart.length > 0 || allAncestorChildrensEnd.length > 0) && | ||
console.warn(`Xarrow warning: set monitorDOMchanges to true - its possible that the positioning will get out of sync on DOM events(like scroll), | ||
on these elements`, lodash.uniqWith([...allAncestorChildrensStart, ...allAncestorChildrensEnd], lodash.isEqual), `\nto disable this warnings set consoleWarning property to false`); | ||
on these elements`, _.uniqWith([...allAncestorChildrensStart, ...allAncestorChildrensEnd], _.isEqual), `\nto disable this warnings set consoleWarning property to false`); | ||
} | ||
}; | ||
const testUserGivenProperties = () => { | ||
const typeOf = (arg) => { | ||
let type = typeof arg; | ||
if (type === "object") { | ||
if (arg === null) | ||
type = "null"; | ||
else if (Array.isArray(arg)) | ||
type = "array"; | ||
} | ||
return type; | ||
}; | ||
const throwError = (errorMsg, consoleMsg) => { | ||
@@ -196,3 +210,3 @@ let err = Error("Xarrows: " + errorMsg); | ||
const checkAnchor = (anchor, name) => { | ||
typeCheck(anchor, ["string", "array"], name); | ||
typeCheck(anchor, ["string", "array", "object"], name); | ||
if (typeOf(anchor) === "string") | ||
@@ -203,2 +217,6 @@ valueCheck(anchor, ["auto", "left", "right", "top", "bottom", "middle"], name); | ||
}; | ||
if (getElementByPropGiven(props.start) === getElementByPropGiven(props.end)) | ||
throwError(`'start' and 'end' props cannot point to the same element`, [ | ||
`'start' and 'end' props cannot point to the same element`, | ||
]); | ||
checkRef(props.start, "start"); | ||
@@ -290,6 +308,9 @@ checkRef(props.end, "end"); | ||
cpy2: 0, | ||
headOrient: "auto", | ||
headOrient: 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 }, | ||
headOffset: 0, | ||
excRight: 0, | ||
@@ -359,2 +380,4 @@ excLeft: 0, | ||
let labalCanvExtraX = Math.max(labelStart ? labelStart.length : 0, labelMiddle ? labelMiddle.length : 0, labelEnd ? labelEnd.length : 0); | ||
let { passProps: adPassProps = { SVGcanvas: {}, arrowHead: {}, arrowBody: {} }, extendSVGcanvas: extendSVGcanvas = 0, } = props.advanced; | ||
let { SVGcanvas = {}, arrowBody = {}, arrowHead = {} } = adPassProps; | ||
const getSelfPos = () => { | ||
@@ -393,6 +416,8 @@ let { x: xarrowElemX, y: xarrowElemY } = selfRef.current.getBoundingClientRect(); | ||
// this function in the useEffect hook. | ||
let { start: s } = positions; | ||
let { end: e } = positions; | ||
let headOrient = "auto"; | ||
const getAnchorOffset = (width, height) => { | ||
let { start: sPos } = positions; | ||
let { end: ePos } = positions; | ||
let headOrient = 0; | ||
////////////////////////////////////////////////////////////////////// | ||
// declare relevant functions for later | ||
const getAnchorsDefaultOffsets = (width, height) => { | ||
return { | ||
@@ -406,18 +431,54 @@ middle: { rightness: width * 0.5, bottomness: height * 0.5 }, | ||
}; | ||
let startAnchorOffsets = getAnchorOffset(s.right - s.x, s.bottom - s.y); | ||
let endAnchorOffsets = getAnchorOffset(e.right - e.x, e.bottom - e.y); | ||
let startAnchorChoice = Array.isArray(props.startAnchor) ? props.startAnchor : [props.startAnchor]; | ||
let endAnchorChoice = Array.isArray(props.endAnchor) ? props.endAnchor : [props.endAnchor]; | ||
let startAnchorPossabilities = {}; | ||
let endAnchorPossabilities = {}; | ||
if (startAnchorChoice.includes("auto")) | ||
["left", "right", "top", "bottom"].forEach((anchor) => (startAnchorPossabilities[anchor] = startAnchorOffsets[anchor])); | ||
else { | ||
startAnchorChoice.forEach((anchor) => (startAnchorPossabilities[anchor] = startAnchorOffsets[anchor])); | ||
} | ||
if (endAnchorChoice.includes("auto")) | ||
["left", "right", "top", "bottom"].forEach((anchor) => (endAnchorPossabilities[anchor] = endAnchorOffsets[anchor])); | ||
else { | ||
endAnchorChoice.forEach((anchor) => (endAnchorPossabilities[anchor] = endAnchorOffsets[anchor])); | ||
} | ||
const prepareAnchorLines = (anchor, anchorPos) => { | ||
let defsOffsets = getAnchorsDefaultOffsets(anchorPos.right - anchorPos.x, anchorPos.bottom - anchorPos.y); | ||
// convert given anchors to array if array not already given | ||
let anchorChoice = Array.isArray(anchor) ? anchor : [anchor]; | ||
//now map each item in the list to relevent object | ||
let anchorChoiceMapped = anchorChoice.map((anchorChoice) => { | ||
if (typeOf(anchorChoice) === "string") { | ||
anchorChoice = anchorChoice; | ||
return { position: anchorChoice, offset: { rightness: 0, bottomness: 0 } }; | ||
} | ||
else if (typeOf(anchorChoice) === "object") { | ||
if (!anchorChoice.offset) | ||
anchorChoice.offset = { rightness: 0, bottomness: 0 }; | ||
if (!anchorChoice.offset.bottomness) | ||
anchorChoice.offset.bottomness = 0; | ||
if (!anchorChoice.offset.rightness) | ||
anchorChoice.offset.rightness = 0; | ||
anchorChoice = anchorChoice; | ||
return anchorChoice; | ||
} | ||
}); | ||
//now build the object that represents the users possablities for diffrent anchors | ||
let anchorPossabilities = []; | ||
if (anchorChoiceMapped.map((a) => a.position).includes("auto")) { | ||
let autoAnchor = anchorChoiceMapped.find((a) => a.position === "auto"); | ||
["left", "right", "top", "bottom"].forEach((anchor) => { | ||
let offset = defsOffsets[anchor]; | ||
offset.rightness += autoAnchor.offset.rightness; | ||
offset.bottomness += autoAnchor.offset.bottomness; | ||
anchorPossabilities.push({ position: anchor, offset }); | ||
}); | ||
} | ||
else { | ||
anchorChoiceMapped.forEach((customAnchor) => { | ||
let offset = defsOffsets[customAnchor.position]; | ||
offset.rightness += customAnchor.offset.rightness; | ||
offset.bottomness += customAnchor.offset.bottomness; | ||
anchorPossabilities.push({ position: customAnchor.position, offset }); | ||
}); | ||
} | ||
// now preper this list of anchors to object expected by the `getShortestLine` function | ||
let points = anchorPossabilities.map((pos) => ({ | ||
x: anchorPos.x + pos.offset.rightness, | ||
y: anchorPos.y + pos.offset.bottomness, | ||
anchorPosition: pos.position, | ||
})); | ||
return points; | ||
}; | ||
//end declare functions | ||
///////////////////////////////////////////////////////////////////////////////////////// | ||
let startPointsObj = prepareAnchorLines(props.startAnchor, sPos); | ||
let endPointsObj = prepareAnchorLines(props.endAnchor, ePos); | ||
const dist = (p1, p2) => { | ||
@@ -429,30 +490,18 @@ //length of line | ||
// closes tPair Of Points which feet to the specifed anchors | ||
let minDist = Infinity; | ||
let minDist = Infinity, d = Infinity; | ||
let closestPair; | ||
for (let startAnchor in sPoints) { | ||
for (let endAnchor in ePoints) { | ||
let d = dist(sPoints[startAnchor], ePoints[endAnchor]); | ||
sPoints.forEach((sp) => { | ||
ePoints.forEach((ep) => { | ||
d = dist(sp, ep); | ||
if (d < minDist) { | ||
minDist = d; | ||
closestPair = [{ [startAnchor]: sPoints[startAnchor] }, { [endAnchor]: ePoints[endAnchor] }]; | ||
closestPair = { startPointObj: sp, endPointObj: ep }; | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
return closestPair; | ||
}; | ||
let startPoints = {}; | ||
for (let key in startAnchorPossabilities) { | ||
startPoints[key] = {}; | ||
startPoints[key]["x"] = startAnchorPossabilities[key].rightness + s.x; | ||
startPoints[key]["y"] = startAnchorPossabilities[key].bottomness + s.y; | ||
} | ||
let endPoints = {}; | ||
for (let key in endAnchorPossabilities) { | ||
endPoints[key] = {}; | ||
endPoints[key]["x"] = endAnchorPossabilities[key].rightness + e.x; | ||
endPoints[key]["y"] = endAnchorPossabilities[key].bottomness + e.y; | ||
} | ||
let [startPointObj, endPointObj] = getShortestLine(startPoints, endPoints); | ||
let startPoint = Object.values(startPointObj)[0], endPoint = Object.values(endPointObj)[0]; | ||
let startAnchor = Object.keys(startPointObj)[0], endAnchor = Object.keys(endPointObj)[0]; | ||
let { startPointObj, endPointObj } = getShortestLine(startPointsObj, endPointsObj); | ||
let startAnchor = startPointObj.anchorPosition, endAnchor = endPointObj.anchorPosition; | ||
let startPoint = _.pick(startPointObj, ["x", "y"]), endPoint = _.pick(endPointObj, ["x", "y"]); | ||
let xarrowElemPos = getSelfPos(); | ||
@@ -473,6 +522,6 @@ let cx0 = Math.min(startPoint.x, endPoint.x) - xarrowElemPos.x; | ||
let excDown = strokeWidth + labalCanvExtraY; | ||
excLeft += Number(props.advanced.extendSVGcanvas); | ||
excRight += Number(props.advanced.extendSVGcanvas); | ||
excUp += Number(props.advanced.extendSVGcanvas); | ||
excDown += Number(props.advanced.extendSVGcanvas); | ||
excLeft += Number(extendSVGcanvas); | ||
excRight += Number(extendSVGcanvas); | ||
excUp += Number(extendSVGcanvas); | ||
excDown += Number(extendSVGcanvas); | ||
//////////////////////////////////// | ||
@@ -486,24 +535,57 @@ // arrow point to point calculations | ||
//////////////////////////////////// | ||
// arrow curveness calculations | ||
// arrow curveness and arrowhead placement calculations | ||
let xHeadOffset = 0; | ||
let yHeadOffset = 0; | ||
if (cu === 0) { | ||
let angel = Math.atan(absDy / absDx); | ||
x2 -= headOffset * xSign * Math.cos(angel); | ||
y2 -= headOffset * ySign * Math.sin(angel); | ||
let headAngel = Math.atan(absDy / absDx); | ||
x2 -= headOffset * xSign * Math.cos(headAngel); | ||
y2 -= 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) * 1; | ||
yHeadOffset = ((Math.cos(headAngel) * (headSize * strokeWidth)) / 2 + (Math.sin(headAngel) * headOffset) / 3) * 1; | ||
headOrient = (headAngel * 180) / Math.PI; | ||
} | ||
else { | ||
if (endAnchor === "middle") { | ||
if (absDx > absDy) | ||
x2 -= headOffset * xSign; | ||
else | ||
y2 -= headOffset * ySign; | ||
if (absDx > absDy) { | ||
endAnchor = xSign ? "left" : "right"; | ||
} | ||
else { | ||
endAnchor = ySign ? "top" : "bottom"; | ||
} | ||
} | ||
else { | ||
if (["left", "right"].includes(endAnchor)) { | ||
x2 -= headOffset * xSign; | ||
if (["left", "right"].includes(endAnchor)) { | ||
x2 -= headOffset * xSign; | ||
xHeadOffset = (headOffset * xSign) / 3; | ||
yHeadOffset = (headSize * strokeWidth * xSign) / 2; | ||
if (endAnchor === "left") { | ||
headOrient = 0; | ||
if (xSign < 0) | ||
headOrient += 180; | ||
} | ||
else if (["top", "bottom"].includes(endAnchor)) { | ||
y2 -= headOffset * ySign; | ||
else { | ||
headOrient = 180; | ||
if (xSign > 0) | ||
headOrient += 180; | ||
} | ||
} | ||
else if (["top", "bottom"].includes(endAnchor)) { | ||
yHeadOffset = (headOffset * ySign) / 3; | ||
xHeadOffset = (headSize * strokeWidth * -ySign) / 2; | ||
y2 -= headOffset * ySign; | ||
if (endAnchor === "top") { | ||
headOrient = 270; | ||
if (ySign > 0) | ||
headOrient += 180; | ||
} | ||
else { | ||
headOrient = 90; | ||
if (ySign < 0) | ||
headOrient += 180; | ||
} | ||
} | ||
} | ||
let arrowHeadOffset = { x: xHeadOffset, y: yHeadOffset }; | ||
excRight += (strokeWidth * headSize) / 2; | ||
@@ -619,2 +701,3 @@ excLeft += (strokeWidth * headSize) / 2; | ||
let labelEndPos = { x: bzx(0.99), y: bzy(0.99) }; | ||
let arrowEnd = { x: bzx(1), y: bzy(1) }; | ||
setSt({ | ||
@@ -641,2 +724,3 @@ cx0, | ||
labelEndPos, | ||
arrowEnd, | ||
excLeft, | ||
@@ -646,10 +730,13 @@ excRight, | ||
excDown, | ||
headOffset, | ||
arrowHeadOffset, | ||
}); | ||
}; | ||
let fHeadSize = headSize * strokeWidth; //factored headsize | ||
let xOffsetHead = st.x2 - st.arrowHeadOffset.x; | ||
let yOffsetHead = st.y2 - st.arrowHeadOffset.y; | ||
let arrowPath = `M ${st.x1} ${st.y1} C ${st.cpx1} ${st.cpy1}, ${st.cpx2} ${st.cpy2}, ${st.x2} ${st.y2}`; | ||
// arrowPath = `M ${st.x1} ${st.y1} ${st.x2} ${st.y2}`; | ||
let arrowHeadId = "arrowHeadMarker" + arrowPath.replace(/ /g, ""); | ||
return (React.createElement("svg", { ref: selfRef, width: st.cw, height: st.ch, | ||
// viewBox={`${-excx / 2} ${-excy / 2} ${st.cw} ${st.ch}`} | ||
style: { | ||
// let arrowHeadId = "arrowHeadMarker" + arrowPath.replace(/ /g, ""); | ||
return (React.createElement("svg", Object.assign({ ref: selfRef, width: st.cw, height: st.ch, style: { | ||
// border: "2px yellow dashed", | ||
@@ -660,8 +747,7 @@ position: "absolute", | ||
pointerEvents: "none", | ||
} }, | ||
React.createElement("marker", { id: arrowHeadId, viewBox: "0 0 12 12", refX: "3", refY: "6", | ||
// re | ||
markerUnits: "strokeWidth", markerWidth: headSize, markerHeight: headSize, orient: st.headOrient }, | ||
React.createElement("path", { d: "M 0 0 L 12 6 L 0 12 L 3 6 z", fill: headColor })), | ||
React.createElement("path", { d: arrowPath, stroke: lineColor, strokeDasharray: `${dashStroke} ${dashNone}`, strokeWidth: strokeWidth, fill: "transparent", markerEnd: `url(#${arrowHeadId})` }, animationSpeed ? (React.createElement("animate", { attributeName: "stroke-dashoffset", values: `${dashoffset * animationDirection};0`, dur: `${1 / animationSpeed}s`, repeatCount: "indefinite" })) : null), | ||
} }, SVGcanvas), | ||
React.createElement("path", Object.assign({ d: arrowPath, stroke: lineColor, strokeDasharray: `${dashStroke} ${dashNone}`, strokeWidth: strokeWidth, fill: "transparent", | ||
// markerEnd={`url(#${arrowHeadId})`} | ||
pointerEvents: "visibleStroke" }, props.passProps, arrowBody), animationSpeed ? (React.createElement("animate", { attributeName: "stroke-dashoffset", values: `${dashoffset * animationDirection};0`, dur: `${1 / animationSpeed}s`, repeatCount: "indefinite" })) : null), | ||
React.createElement("path", Object.assign({ d: `M 0 0 L ${fHeadSize} ${fHeadSize / 2} L 0 ${fHeadSize} L ${fHeadSize / 4} ${fHeadSize / 2} z`, fill: headColor, style: { pointerEvents: "all" }, transform: `translate(${xOffsetHead},${yOffsetHead}) rotate(${st.headOrient})` }, props.passProps, arrowHead)), | ||
labelStart ? (React.createElement("text", Object.assign({}, labelStartExtra, { textAnchor: st.dx > 0 ? "start" : "end", x: st.labelStartPos.x, y: st.labelStartPos.y }), labelStart)) : null, | ||
@@ -682,8 +768,9 @@ labelMiddle ? (React.createElement("text", Object.assign({}, labelMiddleExtra, { textAnchor: "middle", x: st.labelMiddlePos.x, y: st.labelMiddlePos.y }), labelMiddle)) : null, | ||
dashness: false, | ||
consoleWarning: false, | ||
passProps: {}, | ||
advanced: { extendSVGcanvas: 0, passProps: { arrowBody: {}, arrowHead: {}, SVGcanvas: {} } }, | ||
monitorDOMchanges: true, | ||
registerEvents: [], | ||
consoleWarning: true, | ||
advanced: { extendSVGcanvas: 0 }, | ||
}; | ||
export default Xarrow; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "react-xarrows", | ||
"version": "1.2.2", | ||
"version": "1.3.0", | ||
"author": "Eliav Louski", | ||
@@ -5,0 +5,0 @@ "description": "Draw arrows (or lines) between components in React!", |
108
README.md
@@ -15,5 +15,5 @@ # react-xarrows | ||
liked my work? please star this repo. | ||
liked my work? please star [this repo](https://github.com/Eliav2/react-xarrows). | ||
this project developed [using codesandbox](https://codesandbox.io/s/github/Eliav2/react-xarrows). | ||
this project developed with the help of using codesandbox. [see and fork easily here](https://codesandbox.io/s/github/Eliav2/react-xarrows). | ||
@@ -33,5 +33,9 @@ #### what to expect | ||
- keep in mind that this is React component. you should adopt React best practices and place the Xarrow under the relevent ancestor(of 'start' and 'end' element), so when the anchors elements rerenders - so do Xarrow(in simple cases at will work anyway because of DOM listerns i've added,but keep in mind). | ||
- if your component uses 3rd components that uses animations and transformations that changes the anchors DOM positions - the xarrow will not rerender to the latest animated points, but to the firstest. you need to trigger update after all animations ended. | ||
- keep in mind that this is React component ,so you should adopt React best practices. | ||
1. place the Xarrow under the relevent ancestor(of 'start' and 'end' element), so when the anchors elements rerenders so do Xarrow(in simple cases the component will rerender anyway because of DOM listerns i've added,but keep in mind),however, in most cases it will work normally anyway. | ||
2. it is recommended to provide react refs over Id's. it is more consitent, reliable, and this is the recommended way providing refs to DOM elements in React(over Id's which uses getElementById under the hood),by if you choose to stick with id's make sure you rendering xarrow after you render his anchors. | ||
- if your component uses 3rd components that uses animations and transformations that changes the anchors DOM positions - the xarrow will not rerender to the latest animated points, but to the firstest. you need to trigger update after all animations ended(this is not something i can monitor - so its up to you). | ||
## installation | ||
@@ -83,5 +87,5 @@ | ||
see 'Example2' at the examples codesandbox to play around. | ||
### types defenitions | ||
the properties the xarrow component recieves is as follow(as listed in `xarrowPropsType` in /lib/xarrow.d.ts): | ||
the properties the xarrow component recieves is as follow: | ||
@@ -94,3 +98,3 @@ ```jsx | ||
endAnchor?: anchorType | anchorType[]; | ||
label?: labelType | { start?: labelType; middle?: labelType; end?: labelType }; | ||
label?: labelType | labelsType; | ||
color?: string; | ||
@@ -103,16 +107,27 @@ lineColor?: string | null; | ||
dashness?: boolean | { strokeLen?: number; nonStrokeLen?: number; animation?: boolean | number }; | ||
monitorDOMchanges?: boolean; | ||
registerEvents?: registerEventsType[]; | ||
consoleWarning?: boolean; | ||
passProps?: React.SVGProps<SVGPathElement>; | ||
advanced?: { | ||
extendSVGcanvas?: number; | ||
passProps?: { | ||
SVGcanvas?: React.SVGAttributes<SVGSVGElement>; | ||
arrowBody?: React.SVGProps<SVGPathElement>; | ||
arrowHead?: React.SVGProps<SVGPathElement>; | ||
}; | ||
}; | ||
monitorDOMchanges?: boolean; | ||
registerEvents?: registerEventsType[]; | ||
}; | ||
export type anchorType = anchorMethodType | anchorPositionType; | ||
export type anchorMethodType = "auto"; | ||
export type anchorPositionType = "middle" | "left" | "right" | "top" | "bottom"; | ||
export type anchorType = anchorPositionType | anchorCustomPositionType; | ||
export type anchorPositionType = "middle" | "left" | "right" | "top" | "bottom" | "auto"; | ||
export type anchorCustomPositionType = { | ||
position: anchorPositionType; | ||
offset: { rightness: number; bottomness: number }; | ||
}; | ||
export type reactRefType = { current: null | HTMLElement }; | ||
export type refType = reactRefType | string; | ||
export type labelType = string | { text: string; extra?: SVGProps<SVGElement> }; | ||
export type labelsType = { start?: labelType; middle?: labelType; end?: labelType }; | ||
export type labelPropsType = { text: string; extra?: React.SVGAttributes<SVGTextElement> }; | ||
export type labelType = string | labelPropsType; | ||
export type domEventType = keyof GlobalEventHandlersEventMap; | ||
@@ -126,3 +141,3 @@ export type registerEventsType = { | ||
you can keep things simple or provide more detailed props - the API except both. | ||
you can keep things simple or provide more detailed props for more custom behavior - the API except both. | ||
for example - you can provide `label:"middleLable"` and the string will apear as middle label or customize the labels as you please: `label:{end:{text:"end",extra:{fill:"red",dx:-10}}}`. | ||
@@ -140,3 +155,8 @@ see typescript types above for detailed descriptions of what type excepts every prop. | ||
can also be a list of possible anchors. if list is provided - the minimal length anchors will be choosed from the list. | ||
you can also offset each anchor passing `offset`. | ||
examples: | ||
- `endAnchor="middle"` will choose anchor or the end of the line to in the middle of the 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. | ||
#### label | ||
@@ -158,3 +178,5 @@ | ||
- `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. | ||
@@ -165,8 +187,16 @@ #### strokeWidth and headSize | ||
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). | ||
#### curvness | ||
defines how much the lines curve. | ||
0 mean stright line and 1 'perfect' curve. larger then 1 will be over curved. | ||
examples: | ||
- `curvness={false}` will make the line stright without curves. | ||
- `curvness={true}` will choose defualt values of curvness | ||
- `curvness={2}` will make Xarrow extra curved. | ||
#### dashness | ||
@@ -179,12 +209,13 @@ | ||
- `dashness={true}` will make the line of the arrow to be dashed. | ||
- `dashness={{ strokeLen: 10, nonStrokeLen: 15, animation: -2 }}` will make a custom looking dashness | ||
- `dashness={{ strokeLen: 10, nonStrokeLen: 15, animation: -2 }}` will make a custom looking dashness. | ||
#### monitorDOMchanges | ||
#### passProps | ||
A boolean. set this property to true to add relevant eventListeners to the DOM so the xarrow component will update anchors position whenever needed(scroll and resize and so on). | ||
new and powerful feature! | ||
you can pass properties to visible parts of the arrow (such event handlers and much more). | ||
examples: | ||
#### registerEvents | ||
- `passProps: {onClick: () => console.log("xarrow clicked!")}` - now the arrow will console log a message when clicked. | ||
- `passProps: {onClick: () => console.log("xarrow clicked!")}` - now the cursor will change to pointer style when hovering over Xarrow. | ||
you can register the xarrow to DOM event as you please. each time a event that his registed will fire the xarrow component will update his position and will call `callback` (if provided). | ||
#### consoleWarning | ||
@@ -197,4 +228,27 @@ | ||
here i will provide some flexibility to the API for some cases that i may not thought of. | ||
extendSVGcanvas will extend the svg canvas at all sides. can be usefull if for some reason the arrow(or labels) is cutted 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). | ||
##### extendSVGcanvas | ||
will extend the svg canvas at all sides. can be usefull if for some reason the arrow(or labels) is cutted 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. | ||
##### passProps | ||
if you wish you can pass props specipically to either the body of the arrow,or his head,or even the svg canvas which contains both of them. | ||
note that `arrowBody` and `arrowHead` recives props of svg path element and `SVGcanvas` recives props of svg element. | ||
examples: | ||
- `advanced: {passProps: {arrowHead:{onClick: () => console.log("head clicked!")}}}` - now only the head will console log a message when clicked. | ||
#### monitorDOMchanges | ||
A boolean. set this property to true to add relevant eventListeners to the DOM so the xarrow component will update anchors position whenever needed(scroll and resize and so on). (NOTE - maybe will removed in future updates ) | ||
examples: | ||
- `monitorDOMchanges={false}` will disable any DOM monitoring. | ||
#### registerEvents | ||
you can register the xarrow to DOM event as you please. each time a event that his registed will fire the xarrow component will update his position and will call `callback` (if provided). (NOTE - planned to be removed) | ||
### default props | ||
@@ -216,6 +270,7 @@ | ||
dashness: false, | ||
passProps: {}, | ||
consoleWarning: false, | ||
advanced: { extendSVGcanvas: 0, passProps: { arrowBody: {}, arrowHead: {}, SVGcanvas: {} } }, | ||
monitorDOMchanges: true, | ||
registerEvents: [], | ||
consoleWarning: true, | ||
advanced: { extendSVGcanvas: 0 }, | ||
}; | ||
@@ -240,1 +295,6 @@ ``` | ||
- 1.2.2 bug fixes(1#changing anchors refs without remounting broke the arrow. 2#other minors) | ||
- 1.3.0: bug fixes and features update. | ||
1. now `startAnchor` and `endAnchor` can be offset from normal position. see `anchorCustomPositionType` type in types declaration to see how to offset anchors. | ||
2. a new powerful feature: `passProps` - now its possible to pass methods (such event handlers) or attributes to the inner components of Xarrow. | ||
3. `consoleWarning` default prop changed to `false`. | ||
4. bug fixes and inner optimizations(arrow head implemnted now purly using path element without marker elements). |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
81705
816
288