react-xarrows
Advanced tools
Comparing version 1.6.1 to 1.7.0
@@ -1,2 +0,26 @@ | ||
## v1.6.0: | ||
## v1.7.0: | ||
big update! | ||
**performance improvements**: | ||
- much faster and optimized render cycle. | ||
**feature update**: | ||
- creation animation added (see `animateDrawing`,`showXarrow`) | ||
- custom svg now supported,added `circle` and `heart` shapes just for examples. | ||
**new properties**: | ||
- `gridBreak` | ||
- `showXarrow` | ||
- `animateDrawing` | ||
- `headShape` | ||
- `tailShape` | ||
Docs improved! | ||
## v1.6.0: | ||
- proptypes - typescript types issues fixed. | ||
@@ -10,11 +34,15 @@ - now all pointer events enabled in the arrowhead by default ([#50](https://github.com/Eliav2/react-xarrows/issues/50)) | ||
- bugfix: in grid mode in some cases exceeded canvas. | ||
- 1.6.1 - typescript fix: optional properties were require | ||
## 1.5.0 | ||
- properties that are completely removed and no longer supported: `consoleWarning`, `monitorDOMchanges` and `registerEvents`. | ||
## 1.5.0 | ||
- properties that are completely removed and no longer supported: `consoleWarning`, `monitorDOMchanges` | ||
and `registerEvents`. | ||
- issues related to jest and other issues has fixed. | ||
- dependencies changed: prop-types added in lodash replaced with lodash.isequal and lodash.pick. | ||
- v1.5.1 - minor code and docs updates. [DEPRECATED] | ||
- v1.5.2 - debug element was published by mistake at v1.5.1. | ||
- v1.5.2 - debug element was published by mistake at v1.5.1. | ||
improved documentation: | ||
- added CHANGELOG.md file. | ||
@@ -25,2 +53,3 @@ - added TODO.md file. | ||
## 1.4.0: features update | ||
- `label` property API changed. | ||
@@ -30,27 +59,37 @@ - `path` property added | ||
## 1.3.0: bug fixes and features update | ||
- now `startAnchor` and `endAnchor` can be offset from normal position. see `anchorCustomPositionType` type in types declaration to see how to offset anchors. | ||
- a new powerful feature: `passProps` - now its possible to pass methods (such event handlers) or attributes to the inner components of Xarrow. | ||
- now `startAnchor` and `endAnchor` can be offset from normal position. see `anchorCustomPositionType` type in types | ||
declaration to see how to offset anchors. | ||
- a new powerful feature: `passProps` - now its possible to pass methods (such event handlers) or attributes to the | ||
inner components of Xarrow. | ||
- `consoleWarning` default prop changed to `false`. | ||
- bug fixes and inner optimizations(arrow head implemnted now purly using path element without marker elements).## 1.2.0 | ||
added support for javascript projects that imported the lib locally. many changes to the repo folders structure. | ||
- bug fixes and inner optimizations(arrow head implemnted now purly using path element without marker elements).## 1.2.0 | ||
added support for javascript projects that imported the lib locally. many changes to the repo folders structure. | ||
## 1.2.0 bug fixes and code structure changed | ||
The lib not worked properly on js projects(only worked well on ts). now working well on both. | ||
The lib not worked properly on js projects(only worked well on ts). now working well on both. | ||
- v1.2.1 - minor bug fix (intellij suggestinons did not apear) | ||
- v1.2.2 - bug fixes(1#changing anchors refs without remounting broke the arrow. 2#other minors) | ||
## 1.1.0 bug fixes and features update | ||
## 1.1.0 bug fixes and features update | ||
props added:`label`, `dashness` and `advanced`. | ||
`arrowStyle` removed and all his contained properties flattened to be props of xarrow directly. `strokeColor` renamed to `lineColor`. | ||
- v1.1.1 - bug fix now labels not exceed the svg canvas. the headArrow is calcualted now . this means the line ends at the start at the arrow - and this is more natural looking(especially at large headarrows). | ||
`arrowStyle` removed and all his contained properties flattened to be props of xarrow directly. `strokeColor` renamed | ||
to `lineColor`. | ||
- v1.1.1 - bug fix now labels not exceed the svg canvas. the headArrow is calcualted now . this means the line ends at | ||
the start at the arrow - and this is more natural looking(especially at large headarrows). | ||
- v1.1.2 - bug fix. (the first arrow fixed the headarrow style for all next comming arrows) | ||
- v1.1.3 - an entirely new algorithm to calculate arrow path and curveness. now the arrow acting "smarter". this include bug fixes,improvements and some adjustments. | ||
monitorDOMchanges prop default changed to true. | ||
- v1.1.3 - an entirely new algorithm to calculate arrow path and curveness. now the arrow acting "smarter". this include | ||
bug fixes,improvements and some adjustments. monitorDOMchanges prop default changed to true. | ||
- v1.1.4 - bug fixes, calculation optimizations, and smart svg canvas size adjusment. | ||
- v1.1.5 - optimazed calculations and label positioning. (Buzier curve extrema point are calculated now using derivatives and not by interpolation) other improvements as well. | ||
- v1.1.6 - errors and warnings improved. smart adjustments for diffrent positioning style(of anchors elemntes and common ancestor element) . minor bug fixes | ||
- v1.1.5 - optimazed calculations and label positioning. (Buzier curve extrema point are calculated now using | ||
derivatives and not by interpolation) other improvements as well. | ||
- v1.1.6 - errors and warnings improved. smart adjustments for diffrent positioning style(of anchors elemntes and common | ||
ancestor element) . minor bug fixes | ||
## 1.0.0 | ||
initial release. | ||
@@ -1,58 +0,35 @@ | ||
import React from "react"; | ||
export declare type xarrowPropsType = { | ||
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; | ||
import React from 'react'; | ||
import { labelsType, svgCustomEdgeType, svgEdgeShapeType, xarrowPropsType } from './types'; | ||
export type { labelsType, svgCustomEdgeType, svgEdgeShapeType, xarrowPropsType }; | ||
export declare const tAnchorEdge: readonly ["middle", "left", "right", "top", "bottom", "auto"]; | ||
export declare const tPaths: readonly ["smooth", "grid", "straight"]; | ||
export declare const tSvgElems: readonly ["circle", "ellipse", "line", "path", "polygon", "polyline", "rect"]; | ||
export declare const arrowShapes: { | ||
readonly arrow1: { | ||
readonly svgElem: "path"; | ||
readonly svgProps: { | ||
readonly d: "M 0 0 L 1 0.5 L 0 1 L 0.25 0.5 z"; | ||
}; | ||
readonly offsetForward: 0.25; | ||
}; | ||
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 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; | ||
readonly heart: { | ||
readonly svgElem: "path"; | ||
readonly svgProps: { | ||
readonly 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"; | ||
}; | ||
readonly offsetForward: 0.1; | ||
}; | ||
readonly circle: { | ||
readonly svgElem: "circle"; | ||
readonly svgProps: { | ||
readonly r: 0.5; | ||
readonly cx: 0.5; | ||
readonly cy: 0.5; | ||
}; | ||
readonly offsetForward: 0; | ||
}; | ||
}; | ||
export declare type refType = React.MutableRefObject<any> | string; | ||
export declare type labelsType = { | ||
start?: labelType; | ||
middle?: labelType; | ||
end?: labelType; | ||
}; | ||
export declare type labelType = JSX.Element | string; | ||
export declare type domEventType = keyof GlobalEventHandlersEventMap; | ||
export declare const tArrowShapes: ("circle" | "arrow1" | "heart")[]; | ||
declare const Xarrow: React.FC<xarrowPropsType>; | ||
export default Xarrow; | ||
//# sourceMappingURL=index.d.ts.map |
906
lib/index.js
"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) { | ||
@@ -32,2 +43,9 @@ if (k2 === undefined) k2 = k; | ||
}; | ||
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) { | ||
@@ -37,38 +55,90 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const react_1 = __importStar(require("react")); | ||
const lodash_isequal_1 = __importDefault(require("lodash.isequal")); | ||
const lodash_pick_1 = __importDefault(require("lodash.pick")); | ||
const utils_1 = require("./utils"); | ||
const prop_types_1 = __importDefault(require("prop-types")); | ||
const buzzier_1 = require("./utils/buzzier"); | ||
const anchors_1 = require("./utils/anchors"); | ||
const Xarrow = (props) => { | ||
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); | ||
const [anchorsRefs, setAnchorsRefs] = react_1.useState({ start: null, end: null }); | ||
const [prevPosState, setPrevPosState] = react_1.useState(null); | ||
const [prevProps, setPrevProps] = react_1.useState(null); | ||
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, 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"]); | ||
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 _11 = react_1.useState(!animateDrawing), drawAnimEnded = _11[0], setDrawAnimEnded = _11[1]; | ||
var _12 = react_1.useState({}), _ = _12[0], setRerender = _12[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). | ||
* 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 | ||
*/ | ||
const updateIfNeeded = () => { | ||
// check if anchors refs changed | ||
const start = utils_1.getElementByPropGiven(props.start); | ||
const end = utils_1.getElementByPropGiven(props.end); | ||
var updateIfNeeded = function () { | ||
// in case one of the elements does not mounted skip any update | ||
if (start == null || end == null) | ||
if (startRef == null || endRef == null || showXarrow == false) | ||
return; | ||
// if anchors changed re-set them | ||
if (!lodash_isequal_1.default(anchorsRefs, { start, end })) { | ||
initAnchorsRefs(); | ||
} | ||
else if (!lodash_isequal_1.default(props, prevProps)) { | ||
if (!lodash_isequal_1.default(props, prevProps.current)) { | ||
//first check if any properties changed | ||
if (prevProps) { | ||
if (prevProps.current) { | ||
initProps(); | ||
let posState = getAnchorsPos(); | ||
setPrevPosState(posState); | ||
updatePosition(posState); | ||
prevPosState.current = getElemsPos(); | ||
updatePosition(); | ||
} | ||
@@ -78,37 +148,41 @@ } | ||
//if the properties did not changed - update position if needed | ||
let posState = getAnchorsPos(); | ||
if (!lodash_isequal_1.default(prevPosState, posState)) { | ||
setPrevPosState(posState); | ||
updatePosition(posState); | ||
var posState = getElemsPos(); | ||
if (!lodash_isequal_1.default(prevPosState.current, posState)) { | ||
prevPosState.current = posState; | ||
updatePosition(); | ||
} | ||
} | ||
}; | ||
const initAnchorsRefs = () => { | ||
const start = utils_1.getElementByPropGiven(props.start); | ||
const end = utils_1.getElementByPropGiven(props.end); | ||
setAnchorsRefs({ start, end }); | ||
var initAnchorsRefs = function () { | ||
startRef.current = utils_1.getElementByPropGiven(props.start); | ||
endRef.current = utils_1.getElementByPropGiven(props.end); | ||
}; | ||
const initProps = () => { | ||
setPrevProps(props); | ||
var initProps = function () { | ||
prevProps.current = props; | ||
}; | ||
const monitorDOMchanges = () => { | ||
window.addEventListener("resize", updateIfNeeded); | ||
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 cleanMonitorDOMchanges = () => { | ||
window.removeEventListener("resize", updateIfNeeded); | ||
}; | ||
react_1.useEffect(() => { | ||
// console.log("xarrow mounted"); | ||
initProps(); | ||
initAnchorsRefs(); | ||
monitorDOMchanges(); | ||
return () => { | ||
cleanMonitorDOMchanges(); | ||
}; | ||
}, []); | ||
react_1.useLayoutEffect(() => { | ||
// console.log("xarrow rendered!"); | ||
updateIfNeeded(); | ||
}); | ||
const [st, setSt] = react_1.useState({ | ||
// const headBox = headRef.current?.getBBox({ stroke: true }) ?? { x: 0, y: 0, width: 1, height: 1 }; | ||
var _13 = react_1.useState({ | ||
//initial state | ||
@@ -144,3 +218,12 @@ cx0: 0, | ||
excDown: 0, | ||
}); | ||
startPoints: [], | ||
endPoints: [], | ||
mainDivPos: { x: 0, y: 0 }, | ||
xSign: 1, | ||
ySign: 1, | ||
lineLength: 0, | ||
fHeadSize: 1, | ||
fTailSize: 1, | ||
arrowPath: "", | ||
}), st = _13[0], setSt = _13[1]; | ||
headSize = Number(headSize); | ||
@@ -151,38 +234,50 @@ strokeWidth = Number(strokeWidth); | ||
lineColor = lineColor ? lineColor : color; | ||
let dashStroke = 0, dashNone = 0, animationSpeed, animationDirection = 1; | ||
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); | ||
animationSpeed = dashness.animation ? Number(dashness.animation) : null; | ||
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") { | ||
else if (typeof dashness === 'boolean') { | ||
dashStroke = Number(strokeWidth) * 2; | ||
dashNone = Number(strokeWidth); | ||
animationSpeed = null; | ||
animDashSpeed = null; | ||
} | ||
} | ||
let dashoffset = dashStroke + dashNone; | ||
if (animationSpeed < 0) { | ||
animationSpeed *= -1; | ||
animationDirection = -1; | ||
} | ||
let labelStart = null, labelMiddle = null, labelEnd = null; | ||
var labelStart = null, labelMiddle = null, labelEnd = null; | ||
if (label) { | ||
if (typeof label === "string" || "type" in label) | ||
if (typeof label === 'string' || 'type' in label) | ||
labelMiddle = label; | ||
else if (["start", "middle", "end"].some((key) => key in label)) { | ||
else if (['start', 'middle', 'end'].some(function (key) { return key in label; })) { | ||
label = label; | ||
({ start: labelStart, middle: labelMiddle, end: labelEnd } = label); | ||
(labelStart = label.start, labelMiddle = label.middle, labelEnd = label.end); | ||
} | ||
} | ||
const getSelfPos = () => { | ||
let { left: xarrowElemX, top: xarrowElemY, } = selfRef.current.getBoundingClientRect(); | ||
let xarrowStyle = getComputedStyle(selfRef.current); | ||
let xarrowStyleLeft = Number(xarrowStyle.left.slice(0, -2)); | ||
let xarrowStyleTop = Number(xarrowStyle.top.slice(0, -2)); | ||
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 { | ||
@@ -193,58 +288,80 @@ x: xarrowElemX - xarrowStyleLeft, | ||
}; | ||
const getAnchorsPos = () => { | ||
let s = anchorsRefs.start.getBoundingClientRect(); | ||
let e = anchorsRefs.end.getBoundingClientRect(); | ||
var getElemPos = function (elem) { | ||
var pos = elem.getBoundingClientRect(); | ||
return { | ||
start: { | ||
x: s.left, | ||
y: s.top, | ||
right: s.right, | ||
bottom: s.bottom, | ||
}, | ||
end: { | ||
x: e.left, | ||
y: e.top, | ||
right: e.right, | ||
bottom: e.bottom, | ||
}, | ||
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 and adjust canvas based on given properties. | ||
* calculate new path, adjusting canvas, and set state based on given properties. | ||
* */ | ||
const updatePosition = (positions) => { | ||
let { start: sPos } = positions; | ||
let { end: ePos } = positions; | ||
let headOrient = 0; | ||
let tailOrient = 0; | ||
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. | ||
let startPointsObj = anchors_1.prepareAnchorLines(startAnchor, sPos); | ||
let endPointsObj = anchors_1.prepareAnchorLines(endAnchor, ePos); | ||
// choose the smallest path for 2 points from these possibilities. | ||
let { startPointObj, endPointObj } = anchors_1.getShortestLine(startPointsObj, endPointsObj); | ||
let startAnchorPosition = startPointObj.anchorPosition, endAnchorPosition = endPointObj.anchorPosition; | ||
let startPoint = lodash_pick_1.default(startPointObj, ["x", "y"]), endPoint = lodash_pick_1.default(endPointObj, ["x", "y"]); | ||
let xarrowElemPos = getSelfPos(); | ||
let cx0 = Math.min(startPoint.x, endPoint.x) - xarrowElemPos.x; | ||
let cy0 = Math.min(startPoint.y, endPoint.y) - xarrowElemPos.y; | ||
let dx = endPoint.x - startPoint.x; | ||
let dy = endPoint.y - startPoint.y; | ||
let absDx = Math.abs(endPoint.x - startPoint.x); | ||
let absDy = Math.abs(endPoint.y - startPoint.y); | ||
let xSign = dx > 0 ? 1 : -1; | ||
let ySign = dy > 0 ? 1 : -1; | ||
let headOffset = ((headSize * 3) / 4) * strokeWidth; | ||
let tailOffset = ((tailSize * 3) / 4) * strokeWidth; | ||
let cu = Number(curveness); | ||
if (path === "straight") { | ||
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"; | ||
path = 'smooth'; | ||
} | ||
let biggerSide = headSize > tailSize ? headSize : tailSize; | ||
let calc = strokeWidth + (strokeWidth * biggerSide) / 2; | ||
let excRight = calc; | ||
let excLeft = calc; | ||
let excUp = calc; | ||
let excDown = calc; | ||
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); | ||
@@ -256,70 +373,66 @@ excRight += Number(_extendSVGcanvas); | ||
// arrow point to point calculations | ||
let x1 = 0, x2 = absDx, y1 = 0, y2 = absDy; | ||
var x1 = 0, x2 = absDx, y1 = 0, y2 = absDy; | ||
if (dx < 0) | ||
[x1, x2] = [x2, x1]; | ||
_a = [x2, x1], x1 = _a[0], x2 = _a[1]; | ||
if (dy < 0) | ||
[y1, y2] = [y2, y1]; | ||
_b = [y2, y1], y1 = _b[0], y2 = _b[1]; | ||
//////////////////////////////////// | ||
// arrow curviness and arrowhead placement calculations | ||
let xHeadOffset = 0; | ||
let yHeadOffset = 0; | ||
let xTailOffset = 0; | ||
let yTailOffset = 0; | ||
if (cu === 0) { | ||
// in case of straight path | ||
let headAngel = Math.atan(absDy / absDx); | ||
var headAngel = Math.atan(absDy / absDx); | ||
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); | ||
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) / 3 - | ||
(Math.sin(headAngel) * (headSize * strokeWidth)) / 2; | ||
yHeadOffset = | ||
(Math.cos(headAngel) * (headSize * strokeWidth)) / 2 + | ||
(Math.sin(headAngel) * headOffset) / 3; | ||
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; | ||
} | ||
let tailAngel = Math.atan(absDy / absDx); | ||
var 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); | ||
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) / 3 - | ||
(Math.sin(tailAngel) * (tailSize * strokeWidth)) / 2; | ||
yTailOffset = | ||
(Math.cos(tailAngel) * (tailSize * strokeWidth)) / 2 + | ||
(Math.sin(tailAngel) * tailOffset) / 3; | ||
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; | ||
} | ||
//todo: add tail support | ||
} | ||
else { | ||
// in case of smooth path | ||
if (endAnchorPosition === "middle") { | ||
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"; | ||
endAnchorPosition = xSign ? 'left' : 'right'; | ||
} | ||
else { | ||
endAnchorPosition = ySign ? "top" : "bottom"; | ||
endAnchorPosition = ySign ? 'top' : 'bottom'; | ||
} | ||
} | ||
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") { | ||
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; | ||
@@ -335,9 +448,7 @@ if (xSign < 0) | ||
} | ||
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") { | ||
else if (['top', 'bottom'].includes(endAnchorPosition)) { | ||
xHeadOffset += (fHeadSize * -ySign) / 2; | ||
yHeadOffset += _headOffset * ySign; | ||
y2 -= fHeadSize * ySign - yHeadOffset; | ||
if (endAnchorPosition === 'top') { | ||
headOrient = 270; | ||
@@ -352,2 +463,4 @@ if (ySign > 0) | ||
} | ||
// const xm = 1 - _headBox.width - _headBox.x; | ||
// yHeadOffset = yHeadOffset - xm * strokeWidth * yHeadOffset; | ||
} | ||
@@ -357,7 +470,7 @@ } | ||
if (showTail && cu !== 0) { | ||
if (["left", "right"].includes(startAnchorPosition)) { | ||
x1 += tailOffset * xSign; | ||
xTailOffset = -(tailOffset * xSign) / 3; | ||
yTailOffset = -(tailSize * strokeWidth * xSign) / 2; | ||
if (startAnchorPosition === "left") { | ||
if (['left', 'right'].includes(startAnchorPosition)) { | ||
xTailOffset += _tailOffset * -xSign; | ||
x1 += fTailSize * xSign + xTailOffset; | ||
yTailOffset += -(fTailSize * xSign) / 2; | ||
if (startAnchorPosition === 'left') { | ||
tailOrient = 180; | ||
@@ -373,9 +486,7 @@ if (xSign < 0) | ||
} | ||
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") { | ||
else if (['top', 'bottom'].includes(startAnchorPosition)) { | ||
yTailOffset += _tailOffset * -ySign; | ||
y1 += fTailSize * ySign + yTailOffset; | ||
xTailOffset += (fTailSize * ySign) / 2; | ||
if (startAnchorPosition === 'top') { | ||
tailOrient = 90; | ||
@@ -393,9 +504,9 @@ if (ySign > 0) | ||
// if (endAnchorPosition == startAnchorPosition) headOrient += 180; | ||
let arrowHeadOffset = { x: xHeadOffset, y: yHeadOffset }; | ||
let arrowTailOffset = { x: xTailOffset, y: yTailOffset }; | ||
let cpx1 = x1, cpy1 = y1, cpx2 = x2, cpy2 = y2; | ||
let curvesPossibilities = {}; | ||
if (path === "smooth") | ||
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: () => { | ||
hh: function () { | ||
//horizontal - from right to left or the opposite | ||
@@ -411,3 +522,3 @@ cpx1 += absDx * cu * xSign; | ||
}, | ||
vv: () => { | ||
vv: function () { | ||
//vertical - from top to bottom or opposite | ||
@@ -419,3 +530,3 @@ cpy1 += absDy * cu * ySign; | ||
}, | ||
hv: () => { | ||
hv: function () { | ||
// start horizontally then vertically | ||
@@ -426,3 +537,3 @@ // from v side to h side | ||
}, | ||
vh: () => { | ||
vh: function () { | ||
// start vertically then horizontally | ||
@@ -434,36 +545,34 @@ // from h side to v side | ||
}; | ||
else if (path === "grid") { | ||
else if (path === 'grid') { | ||
curvesPossibilities = { | ||
hh: () => { | ||
hh: function () { | ||
// cpx1 += (absDx * 0.5 - headOffset / 2) * xSign; | ||
// cpx2 -= (absDx * 0.5 - headOffset / 2) * xSign; | ||
cpx1 += absDx * 0.5 * xSign; | ||
cpx2 -= absDx * 0.5 * xSign; | ||
cpx1 += absDx * gridBreak * xSign; | ||
cpx2 -= absDx * (1 - gridBreak) * xSign; | ||
if (showHead) { | ||
cpx1 -= (headOffset / 2) * xSign; | ||
cpx2 += (headOffset / 2) * xSign; | ||
cpx1 -= ((fHeadSize * (1 - headOffset)) / 2) * xSign; | ||
cpx2 += ((fHeadSize * (1 - headOffset)) / 2) * xSign; | ||
} | ||
if (showTail) { | ||
cpx1 -= (tailOffset / 2) * xSign; | ||
cpx2 += (tailOffset / 2) * xSign; | ||
cpx1 -= ((fTailSize * (1 - tailOffset)) / 2) * xSign; | ||
cpx2 += ((fTailSize * (1 - tailOffset)) / 2) * xSign; | ||
} | ||
}, | ||
vv: () => { | ||
// cpy1 += (absDy * 0.5 - headOffset / 2) * ySign; | ||
// cpy2 -= (absDy * 0.5 - headOffset / 2) * ySign; | ||
cpy1 += absDy * 0.5 * ySign; | ||
cpy2 -= absDy * 0.5 * ySign; | ||
vv: function () { | ||
cpy1 += absDy * gridBreak * ySign; | ||
cpy2 -= absDy * (1 - gridBreak) * ySign; | ||
if (showHead) { | ||
cpy1 -= (headOffset / 2) * ySign; | ||
cpy2 += (headOffset / 2) * ySign; | ||
cpy1 -= ((fHeadSize * (1 - headOffset)) / 2) * ySign; | ||
cpy2 += ((fHeadSize * (1 - headOffset)) / 2) * ySign; | ||
} | ||
if (showTail) { | ||
cpy1 -= (tailOffset / 2) * ySign; | ||
cpy2 += (tailOffset / 2) * ySign; | ||
cpy1 -= ((fTailSize * (1 - tailOffset)) / 2) * ySign; | ||
cpy2 += ((fTailSize * (1 - tailOffset)) / 2) * ySign; | ||
} | ||
}, | ||
hv: () => { | ||
hv: function () { | ||
cpx1 = x2; | ||
}, | ||
vh: () => { | ||
vh: function () { | ||
cpy1 = y2; | ||
@@ -474,19 +583,19 @@ }, | ||
// smart select best curve for the current anchors | ||
let 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"; | ||
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"); | ||
selectedCurviness = selectedCurviness.replace(/m/g, 'h'); | ||
else | ||
selectedCurviness = selectedCurviness.replace(/m/g, "v"); | ||
selectedCurviness = selectedCurviness.replace(/m/g, 'v'); | ||
curvesPossibilities[selectedCurviness](); | ||
@@ -499,4 +608,5 @@ cpx1 += _cpx1Offset; | ||
// canvas smart size adjustments | ||
const [xSol1, xSol2] = buzzier_1.buzzierMinSols(x1, cpx1, cpx2, x2); | ||
const [ySol1, ySol2] = buzzier_1.buzzierMinSols(y1, cpy1, cpy2, y2); | ||
// 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) | ||
@@ -510,7 +620,7 @@ excLeft += -xSol1; | ||
excDown += ySol2 - absDy; | ||
if (path === "grid") { | ||
excLeft += calc; | ||
excRight += calc; | ||
excUp += calc; | ||
excDown += calc; | ||
if (path === 'grid') { | ||
excLeft += _calc; | ||
excRight += _calc; | ||
excUp += _calc; | ||
excDown += _calc; | ||
} | ||
@@ -525,69 +635,167 @@ x1 += excLeft; | ||
cpy2 += excUp; | ||
const cw = absDx + excLeft + excRight, ch = absDy + excUp + excDown; | ||
var cw = absDx + excLeft + excRight, ch = absDy + excUp + excDown; | ||
cx0 -= excLeft; | ||
cy0 -= excUp; | ||
//labels | ||
const bzx = buzzier_1.bzFunction(x1, cpx1, cpx2, x2); | ||
const bzy = buzzier_1.bzFunction(y1, cpy1, cpy2, y2); | ||
const labelStartPos = { x: bzx(0.01), y: bzy(0.01) }; | ||
const labelMiddlePos = { x: bzx(0.5), y: bzy(0.5) }; | ||
const labelEndPos = { x: bzx(0.99), y: bzy(0.99) }; | ||
const arrowEnd = { x: bzx(1), y: bzy(1) }; | ||
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, | ||
cy0, | ||
x1, | ||
x2, | ||
y1, | ||
y2, | ||
cw, | ||
ch, | ||
cpx1, | ||
cpy1, | ||
cpx2, | ||
cpy2, | ||
dx, | ||
dy, | ||
absDx, | ||
absDy, | ||
headOrient, | ||
tailOrient, | ||
labelStartPos, | ||
labelMiddlePos, | ||
labelEndPos, | ||
arrowEnd, | ||
excLeft, | ||
excRight, | ||
excUp, | ||
excDown, | ||
headOffset, | ||
arrowHeadOffset, | ||
arrowTailOffset, | ||
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, | ||
}); | ||
}; | ||
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} `; | ||
if (path === "straight") | ||
arrowPath = `M ${st.x1} ${st.y1} ${st.x2} ${st.y2}`; | ||
if (path === "grid") | ||
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({}, 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), | ||
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, | ||
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), | ||
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", | ||
transform: st.dx < 0 ? 'translate(-100% , -50%)' : 'translate(-0% , -50%)', | ||
width: 'max-content', | ||
position: 'absolute', | ||
left: st.cx0 + st.labelStartPos.x, | ||
@@ -597,6 +805,6 @@ top: st.cy0 + st.labelStartPos.y - strokeWidth - 5, | ||
labelMiddle ? (react_1.default.createElement("div", { style: { | ||
display: "table", | ||
width: "max-content", | ||
transform: "translate(-50% , -50%)", | ||
position: "absolute", | ||
display: 'table', | ||
width: 'max-content', | ||
transform: 'translate(-50% , -50%)', | ||
position: 'absolute', | ||
left: st.cx0 + st.labelMiddlePos.x, | ||
@@ -606,22 +814,28 @@ top: st.cy0 + st.labelMiddlePos.y, | ||
labelEnd ? (react_1.default.createElement("div", { style: { | ||
transform: st.dx > 0 ? "translate(-100% , -50%)" : "translate(-0% , -50%)", | ||
width: "max-content", | ||
position: "absolute", | ||
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, | ||
} }, labelEnd)) : null)); | ||
} }, 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 | ||
const pAnchorPositionType = prop_types_1.default.oneOf([ | ||
"middle", | ||
"left", | ||
"right", | ||
"top", | ||
"bottom", | ||
"auto", | ||
]); | ||
const pAnchorCustomPositionType = prop_types_1.default.exact({ | ||
var pAnchorPositionType = prop_types_1.default.oneOf(exports.tAnchorEdge); | ||
var pAnchorCustomPositionType = prop_types_1.default.exact({ | ||
position: pAnchorPositionType.isRequired, | ||
offset: prop_types_1.default.shape({ | ||
offset: prop_types_1.default.exact({ | ||
rightness: prop_types_1.default.number, | ||
@@ -631,13 +845,7 @@ bottomness: prop_types_1.default.number, | ||
}); | ||
const _pAnchorType = prop_types_1.default.oneOfType([ | ||
pAnchorPositionType, | ||
pAnchorCustomPositionType, | ||
]); | ||
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({ | ||
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, | ||
@@ -647,2 +855,12 @@ middle: _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 = { | ||
@@ -663,5 +881,10 @@ start: pRefType.isRequired, | ||
showTail: prop_types_1.default.bool, | ||
path: prop_types_1.default.oneOf(["smooth", "grid", "straight"]), | ||
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, | ||
@@ -680,32 +903,3 @@ arrowBodyProps: prop_types_1.default.object, | ||
}; | ||
Xarrow.defaultProps = { | ||
startAnchor: "auto", | ||
endAnchor: "auto", | ||
label: null, | ||
color: "CornflowerBlue", | ||
lineColor: null, | ||
headColor: null, | ||
tailColor: null, | ||
strokeWidth: 4, | ||
showHead: true, | ||
headSize: 6, | ||
showTail: false, | ||
tailSize: 6, | ||
path: "smooth", | ||
curveness: 0.8, | ||
dashness: false, | ||
passProps: {}, | ||
arrowBodyProps: {}, | ||
arrowHeadProps: {}, | ||
arrowTailProps: {}, | ||
SVGcanvasProps: {}, | ||
divContainerProps: {}, | ||
_extendSVGcanvas: 0, | ||
_debug: false, | ||
_cpx1Offset: 0, | ||
_cpy1Offset: 0, | ||
_cpx2Offset: 0, | ||
_cpy2Offset: 0, | ||
}; | ||
exports.default = Xarrow; | ||
//# sourceMappingURL=index.js.map |
/** | ||
* utility functions for preparing `startAnchor` and `endAnchor` to accept the diffrent types that can be passed. | ||
*/ | ||
import { anchorPositionType } from "../index"; | ||
import { anchorPositionType } from '../types'; | ||
export declare const prepareAnchorLines: (anchor: any, anchorPos: any) => { | ||
x: any; | ||
y: any; | ||
anchorPosition: anchorPositionType; | ||
anchorPosition: "middle" | "left" | "right" | "top" | "bottom" | "auto"; | ||
}[]; | ||
@@ -10,0 +10,0 @@ declare type t1 = { |
@@ -7,4 +7,5 @@ "use strict"; | ||
exports.getShortestLine = exports.prepareAnchorLines = void 0; | ||
const index_1 = require("./index"); | ||
const getAnchorsDefaultOffsets = (width, height) => { | ||
var index_1 = require("./index"); | ||
var index_2 = require("../index"); | ||
var getAnchorsDefaultOffsets = function (width, height) { | ||
return { | ||
@@ -18,11 +19,13 @@ middle: { rightness: width * 0.5, bottomness: height * 0.5 }, | ||
}; | ||
exports.prepareAnchorLines = (anchor, anchorPos) => { | ||
let defsOffsets = getAnchorsDefaultOffsets(anchorPos.right - anchorPos.x, anchorPos.bottom - anchorPos.y); | ||
exports.prepareAnchorLines = function (anchor, anchorPos) { | ||
var defsOffsets = getAnchorsDefaultOffsets(anchorPos.right - anchorPos.x, anchorPos.bottom - anchorPos.y); | ||
// convert given anchors to array if not array already | ||
let anchorChoice = Array.isArray(anchor) ? anchor : [anchor]; | ||
var anchorChoice = Array.isArray(anchor) ? anchor : [anchor]; | ||
if (anchorChoice.length == 0) | ||
anchorChoice = ["auto"]; | ||
anchorChoice = ['auto']; | ||
//now map each item in the array to relevant object | ||
let anchorChoiceMapped = anchorChoice.map((anchorChoice) => { | ||
if (index_1.typeOf(anchorChoice) === "string") { | ||
var anchorChoiceMapped = anchorChoice.map(function (anchorChoice) { | ||
if (index_1.typeOf(anchorChoice) === 'string') { | ||
if (!index_2.tAnchorEdge.includes(anchorChoice)) | ||
anchorChoice = 'auto'; | ||
anchorChoice = anchorChoice; | ||
@@ -34,3 +37,3 @@ return { | ||
} | ||
else if (index_1.typeOf(anchorChoice) === "object") { | ||
else if (index_1.typeOf(anchorChoice) === 'object') { | ||
if (!anchorChoice.offset) | ||
@@ -47,37 +50,37 @@ anchorChoice.offset = { rightness: 0, bottomness: 0 }; | ||
//now build the object that represents the users possibilities for different anchors | ||
let anchorPossibilities = []; | ||
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; | ||
anchorPossibilities.push({ position: anchor, offset }); | ||
var anchorPossibilities = []; | ||
if (anchorChoiceMapped.map(function (a) { return a.position; }).includes('auto')) { | ||
var autoAnchor_1 = anchorChoiceMapped.find(function (a) { return a.position === 'auto'; }); | ||
['left', 'right', 'top', 'bottom'].forEach(function (anchor) { | ||
var offset = defsOffsets[anchor]; | ||
offset.rightness += autoAnchor_1.offset.rightness; | ||
offset.bottomness += autoAnchor_1.offset.bottomness; | ||
anchorPossibilities.push({ position: anchor, offset: offset }); | ||
}); | ||
} | ||
else { | ||
anchorChoiceMapped.forEach((customAnchor) => { | ||
let offset = defsOffsets[customAnchor.position]; | ||
anchorChoiceMapped.forEach(function (customAnchor) { | ||
var offset = defsOffsets[customAnchor.position]; | ||
offset.rightness += customAnchor.offset.rightness; | ||
offset.bottomness += customAnchor.offset.bottomness; | ||
anchorPossibilities.push({ position: customAnchor.position, offset }); | ||
anchorPossibilities.push({ position: customAnchor.position, offset: offset }); | ||
}); | ||
} | ||
// now prepare this list of anchors to object expected by the `getShortestLine` function | ||
return anchorPossibilities.map((pos) => ({ | ||
return anchorPossibilities.map(function (pos) { return ({ | ||
x: anchorPos.x + pos.offset.rightness, | ||
y: anchorPos.y + pos.offset.bottomness, | ||
anchorPosition: pos.position, | ||
})); | ||
}); }); | ||
}; | ||
const dist = (p1, p2) => { | ||
var dist = function (p1, p2) { | ||
//length of line | ||
return Math.sqrt(Math.pow((p1.x - p2.x), 2) + Math.pow((p1.y - p2.y), 2)); | ||
}; | ||
exports.getShortestLine = (sPoints, ePoints) => { | ||
exports.getShortestLine = function (sPoints, ePoints) { | ||
// closes tPair Of Points which feet to the specified anchors | ||
let minDist = Infinity, d = Infinity; | ||
let closestPair; | ||
sPoints.forEach((sp) => { | ||
ePoints.forEach((ep) => { | ||
var minDist = Infinity, d = Infinity; | ||
var closestPair; | ||
sPoints.forEach(function (sp) { | ||
ePoints.forEach(function (ep) { | ||
d = dist(sp, ep); | ||
@@ -84,0 +87,0 @@ if (d < minDist) { |
@@ -9,11 +9,10 @@ "use strict"; | ||
*/ | ||
exports.bzFunction = (p1, p2, p3, p4) => (t) => Math.pow((1 - t), 3) * p1 + | ||
3 * Math.pow((1 - t), 2) * t * p2 + | ||
3 * (1 - t) * Math.pow(t, 2) * p3 + | ||
Math.pow(t, 3) * p4; | ||
exports.bzFunction = function (p1, p2, p3, p4) { return function (t) { | ||
return Math.pow((1 - t), 3) * p1 + 3 * Math.pow((1 - t), 2) * t * p2 + 3 * (1 - t) * Math.pow(t, 2) * p3 + Math.pow(t, 3) * p4; | ||
}; }; | ||
/** | ||
* returns 2 solutions from extram points for buzzier curve with 2 controls points | ||
*/ | ||
exports.buzzierMinSols = (p1, p2, p3, p4) => { | ||
const bz = exports.bzFunction(p1, p2, p3, p4); | ||
exports.buzzierMinSols = function (p1, p2, p3, p4) { | ||
var bz = exports.bzFunction(p1, p2, p3, p4); | ||
// dt(bz) = -3 p1 (1 - t)^2 + 3 p2 (1 - t)^2 - 6 p2 (1 - t) t + 6 p3 (1 - t) t - 3 p3 t^2 + 3 p4 t^2 | ||
@@ -26,10 +25,9 @@ // when p1=(x1,y1),p2=(cpx1,cpy1),p3=(cpx2,cpy2),p4=(x2,y2) | ||
// then solution we want is: bz(tSol) | ||
const A = -6 * p1 + 12 * p2 - 6 * p3; | ||
const B = Math.pow((-6 * p1 + 12 * p2 - 6 * p3), 2) - | ||
4 * (3 * p2 - 3 * p1) * (-3 * p1 + 9 * p2 - 9 * p3 + 3 * p4); | ||
const C = 2 * (-3 * p1 + 9 * p2 - 9 * p3 + 3 * p4); | ||
const sol1 = bz((A + Math.sqrt(B)) / C); | ||
const sol2 = bz((A - Math.sqrt(B)) / C); | ||
var A = -6 * p1 + 12 * p2 - 6 * p3; | ||
var B = Math.pow((-6 * p1 + 12 * p2 - 6 * p3), 2) - 4 * (3 * p2 - 3 * p1) * (-3 * p1 + 9 * p2 - 9 * p3 + 3 * p4); | ||
var C = 2 * (-3 * p1 + 9 * p2 - 9 * p3 + 3 * p4); | ||
var sol1 = bz((A + Math.sqrt(B)) / C); | ||
var sol2 = bz((A - Math.sqrt(B)) / C); | ||
return [sol1, sol2]; | ||
}; | ||
//# sourceMappingURL=buzzier.js.map |
@@ -1,6 +0,8 @@ | ||
import { refType } from "../index"; | ||
declare type extendedJtypes = "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "null" | "array"; | ||
import { refType } from '../types'; | ||
declare type extendedJtypes = 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | 'null' | 'array'; | ||
export declare const getElementByPropGiven: (ref: refType) => HTMLElement; | ||
export declare const typeOf: (arg: any) => extendedJtypes; | ||
export declare const factorDpathStr: (d: string, factor: any) => string; | ||
export declare const measureFunc: (callbackFunc: Function, name?: string) => any; | ||
export {}; | ||
//# sourceMappingURL=index.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.typeOf = exports.getElementByPropGiven = void 0; | ||
exports.getElementByPropGiven = (ref) => { | ||
let myRef; | ||
if (typeof ref === "string") { | ||
exports.measureFunc = exports.factorDpathStr = exports.typeOf = exports.getElementByPropGiven = void 0; | ||
exports.getElementByPropGiven = function (ref) { | ||
var myRef; | ||
if (typeof ref === 'string') { | ||
// myRef = document.getElementById(ref); | ||
myRef = document.getElementById(ref); | ||
@@ -13,12 +14,42 @@ } | ||
}; | ||
exports.typeOf = (arg) => { | ||
let type = typeof arg; | ||
if (type === "object") { | ||
exports.typeOf = function (arg) { | ||
var type = typeof arg; | ||
if (type === 'object') { | ||
if (arg === null) | ||
type = "null"; | ||
type = 'null'; | ||
else if (Array.isArray(arg)) | ||
type = "array"; | ||
type = 'array'; | ||
} | ||
return type; | ||
}; | ||
// receives string representing a d path and factoring only the numbers | ||
exports.factorDpathStr = function (d, factor) { | ||
var l = d.split(/(\d+(?:\.\d+)?)/); | ||
l = l.map(function (s) { | ||
if (Number(s)) | ||
return (Number(s) * factor).toString(); | ||
else | ||
return s; | ||
}); | ||
return l.join(''); | ||
}; | ||
// debug | ||
exports.measureFunc = function (callbackFunc, name) { | ||
if (name === void 0) { name = ''; } | ||
var t = performance.now(); | ||
var returnVal = callbackFunc(); | ||
console.log('time ', name, ':', performance.now() - t); | ||
return returnVal; | ||
}; | ||
// export const measureFunc = (func: Function, name = '') => (...args) => { | ||
// const t = performance.now(); | ||
// | ||
// console.log(this, func.name, ...args); | ||
// const returnVal = func(...args); | ||
// console.log('time ', func.name || name, ':', performance.now() - t); | ||
// return returnVal; | ||
// }; | ||
//// example | ||
// instead: myFunc(arg1,arg2) | ||
// call: measureFunc(myFunc)(arg1,arg2) | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "react-xarrows", | ||
"version": "1.6.1", | ||
"version": "1.7.0", | ||
"author": "Eliav Louski", | ||
@@ -36,5 +36,7 @@ "description": "Draw arrows (or lines) between components in React!", | ||
"peerDependencies": { | ||
"react": ">=16.0.0" | ||
"react": ">=16.8.0" | ||
}, | ||
"devDependencies": { | ||
"@types/lodash": "^4.14.168", | ||
"@types/node": "^15.0.1", | ||
"@types/react": "^16.9.19", | ||
@@ -44,4 +46,3 @@ "@typescript-eslint/parser": "^4.15.2", | ||
"eslint-plugin-import": "^2.22.1", | ||
"react": "^16.12.0", | ||
"typescript": "^3.7.5" | ||
"typescript": "3.9" | ||
}, | ||
@@ -63,3 +64,9 @@ "scripts": { | ||
"README.md" | ||
] | ||
], | ||
"prettier": { | ||
"printWidth": 120, | ||
"jsxBracketSameLine": true, | ||
"singleQuote": true, | ||
"tabWidth": 2 | ||
} | ||
} |
493
README.md
@@ -16,6 +16,4 @@ # react-xarrows | ||
- Automatic anchoring based on smallest path. | ||
- can add customizable labels | ||
- relatively fast algorithm to find path and to adjust canvas. | ||
- Easily customize the look and behavior of the arrow. | ||
- Written in typescript so you get nice props suggestions(but support js also of course). | ||
- Fast algorithm to find path and to adjust canvas. | ||
- Customization is easy but flexible | ||
@@ -26,4 +24,2 @@ found a problem? not a problem! post a new issue([here](https://github.com/Eliav2/react-xarrows/issues)). | ||
[see and fork easily on codesandbox](https://codesandbox.io/s/github/Eliav2/react-xarrows). | ||
## installation | ||
@@ -39,9 +35,12 @@ | ||
[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). | ||
codebox of few examples(in this repo at [/examples](./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) | ||
see this interactive example: <https://lwwwp.csb.app/CustomizeArrow> | ||
[comment]: <> (![react-xarrows-v1 6](https://user-images.githubusercontent.com/47307889/113949468-070f1c80-9818-11eb-90e6-ddc6d814b912.gif)) | ||
<img src="https://user-images.githubusercontent.com/47307889/113949468-070f1c80-9818-11eb-90e6-ddc6d814b912.gif" width="650px"/> | ||
### simple example: | ||
@@ -88,67 +87,85 @@ | ||
### Contributing | ||
Want a feature that is not supported? found a bug?\ | ||
no need to clone the repo and set up the dev environment anymore!\ | ||
here's a ready to use development environment with a click of a button(patience, it takes about a minute to setup): | ||
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Eliav2/react-xarrows/blob/master/src/index.tsx) | ||
this will set up environment that will clone react-xarrow master,and will link the code from the src to the examples, | ||
and will start examples,with typescript watch process that will recompile when any change is made.\ | ||
this means that any code changes in src/index.tsx will immediately be reflected to the running example at port 3000! | ||
(add console.log("test") line and see!)\ | ||
to reproduce this dev env on your local machine git clone and follow same commands as in [gitpod.yml](./.gitpod.yml). | ||
if you made an improvement that is relevant for most users, you can quickly submit a pull request. | ||
Please always pull request from and into dev branch - | ||
here's [Gitpod](https://gitpod.io/#https://github.com/Eliav2/react-xarrows/blob/dev/src/index.tsx) | ||
### react-xarrows v2 | ||
v2 is on its way. want to contribute and participate in plannig the next react architecture for react-xarrows? | ||
see discussion [here](https://github.com/Eliav2/react-xarrows/discussions/53)! | ||
v2 is on its way. want to contribute and participate in plannig the next react architecture for react-xarrows? see | ||
discussion [here](https://github.com/Eliav2/react-xarrows/discussions/53)! | ||
### types definitions | ||
### API | ||
the properties the xarrow component receives is as follows(don't panic,the important ones exmplained next): | ||
to see full typescript definition see [types.ts](./src/types.ts) file. | ||
```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; | ||
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; | ||
}; | ||
here's a summary of the all the available props: | ||
export type anchorType = anchorPositionType | anchorCustomPositionType; | ||
export type anchorPositionType = | "middle" | "left" | "right" | "top" | "bottom" | "auto"; | ||
**Properties**|**Description**|**default value**|**type** | ||
:-----:|:-----:|:-----:|:-----: | ||
[start](#refs)|ref to start element|none(Required!)|string/ReactRef | ||
[end](#refs)|ref to end element|none(Required!)|string/ReactRef | ||
[startAnchor](#anchors)|from which side the arrow should start from start element| 'auto'|string/object/array | ||
[endAnchor](#anchors)|at which side the arrow should end at end element| 'auto'|string/object/array | ||
[label](#label)|optional labels| null|string/array | ||
[color](#colors)|color of Xarrow(all parts)| 'CornflowerBlue'|string | ||
[lineColor](#colors)|color of the line| null|string | ||
[headColor](#colors)|color of the head| null|string | ||
[tailColor](#colors)|color of the tail| null|string | ||
[strokeWidth](#widths)|thickness of Xarrow(all parts)|4|number | ||
[headSize](#widths)|thickness of head(relative to strokeWidth)|6|number | ||
[tailSize](#widths)|thickness of tail(relative to strokeWidth)|6|number | ||
[path](#path)|path drawing style| 'smooth'|string | ||
[curveness](#curveness)|how much the line curveness when path='smooth'| 0.8|number | ||
[gridBreak](#gridBreak)|where the line breaks in path='grid'| 0.5|number | ||
[dashness](#dashness)|should the line be dashed| false|boolean/object | ||
[showHead](#shows)|show the arrow head?| true|boolean | ||
[showTail](#shows)|show the arrow tail?| false|boolean | ||
[showXarrow](#shows)|show Xarrow?| true|boolean | ||
[animateDrawing](#animateDrawing)|animate drawing when arrow mounts?| false|boolean/object | ||
[headShape](#customsvgs)|shape of the arrow head| 'arrow1'|string/object | ||
[tailShape](#customsvgs)|shape of the arrow tail|'arrow1'|string/object | ||
export type anchorCustomPositionType = { | ||
position: anchorPositionType; | ||
offset: { rightness?: number; bottomness?: number }; | ||
}; | ||
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; | ||
``` | ||
<details> | ||
<summary>Advanced Props</summary> | ||
[see details](#advancedCustom) | ||
**Properties**|**Description**|**default value**|**type** | ||
:-----:|:-----:|:-----:|:-----: | ||
passProps|properties which will be pased to arrowBody,arrowHead,arrowTail| {}|object | ||
SVGcanvasProps|properties which will be passed to svgCanvas| {}|object | ||
arrowBodyProps|properties which will be passed to arrowBody| {}|object | ||
arrowHeadProps|properties which will be passed to arrowHead| {}|object | ||
arrowTailProps|properties which will be passed to arrowTail| {}|object | ||
divContainerProps|properties which will be passed to divContainer| {}|object | ||
SVGcanvasStyle|style properties which will be passed svgCanvas|0|object | ||
divContainerStyle|style properties which will be passed divContainer| false|object | ||
_extendSVGcanvas|extend svgCanas at all sides|0|number | ||
_debug|show debug elements|0|boolean | ||
_cpx1Offset|offset control point 1 x|0|number | ||
_cpy1Offset|offset control point 1 y|0|number | ||
_cpx2Offset|offset control point 2 x|0|number | ||
_cpy2Offset|offset control point 2 x|0|number | ||
</details> | ||
##### 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` | ||
custom props for more custom behavior - the API except both(see [`startAnchor`](#anchors) or `label` | ||
properties for good examples) | ||
@@ -160,6 +177,11 @@ .<br/> | ||
this documentation is examples driven. | ||
This documentation is examples driven.\ | ||
The examples is sorted from the most common use case to the most custom one. | ||
#### 'start' and 'end' | ||
<a name="refs"></a> | ||
<details> | ||
<summary> 'start' and 'end' </summary> | ||
_required_\ | ||
@@ -173,29 +195,82 @@ can be a reference to a react ref to html element or string - an id of a DOM element. | ||
#### 'startAnchor' and 'endAnchor' | ||
</details> | ||
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`. | ||
examples: | ||
<a name="anchors"></a> | ||
<details> | ||
<summary> 'startAnchor' and 'endAnchor' </summary> | ||
specify what anchors are allowed. can be a string/object/array. | ||
type: | ||
```typescript | ||
export type anchorType = anchorPositionType | anchorCustomPositionType; | ||
``` | ||
#### simple usage: | ||
type: | ||
```typescript | ||
export const tAnchorEdge = ['middle', 'left', 'right', 'top', 'bottom', 'auto'] as const; | ||
export type anchorPositionType = typeof tAnchorEdge[number]; | ||
``` | ||
one of `"auto" | "middle" | "left" | "right" | "top" | "bottom"` | ||
`auto` will choose automatically the path with the smallest length. | ||
example: | ||
- `endAnchor="middle"` will set the anchor of the end of the line to the middle of the end element. | ||
#### custom usage: | ||
type: | ||
```typescript | ||
export type anchorCustomPositionType = { | ||
position: anchorPositionType; | ||
offset: { rightness?: number; bottomness?: number }; | ||
}; | ||
``` | ||
you can offset the anchor from normal positioning. NOTE: breaking changes in naming in v2. | ||
example: | ||
- `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. | ||
if list is provided - the minimal length anchors will be chosen from the list. example: | ||
- `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 | ||
</details> | ||
_optional, default: null_ \ | ||
<a name="label"></a> | ||
<details> | ||
<summary> label </summary> | ||
you can place up to 3 labels. see examples | ||
- `label="middleLabel"` - middle label | ||
- `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 | ||
- ```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 and tailColor | ||
</details> | ||
_optional, default: "CornflowerBlue"_ \ | ||
<a name="colors"></a> | ||
<details> | ||
<summary> color,lineColor and headColor and tailColor </summary> | ||
`color` defines color to the entire arrow. lineColor,headColor and tailColor will override color specifically for | ||
@@ -209,5 +284,10 @@ line,tail or head. examples: | ||
#### strokeWidth and headSize and tailSize | ||
</details> | ||
_optional, default: 6_ \ | ||
<a name="widths"></a> | ||
<details> | ||
<summary>strokeWidth and headSize and tailSize</summary> | ||
strokeWidth defines the thickness of the entire arrow. headSize and tailSize defines how big will be the head or tail | ||
@@ -220,5 +300,10 @@ relative to the strokeWidth. examples: | ||
#### path | ||
</details> | ||
_optional, default: "smooth"_ \ | ||
<a name="path"></a> | ||
<details> | ||
<summary>path</summary> | ||
`path` can be one of: `"smooth" | "grid" | "straight"`, and it controls the path arrow is drawn, exactly how their name | ||
@@ -229,7 +314,12 @@ suggest. examples: | ||
#### curveness | ||
</details> | ||
_optional, default: 0.8_ \ | ||
defines how much the lines curve. examples: | ||
<a name="curveness"></a> | ||
<details> | ||
<summary>curveness</summary> | ||
defines how much the lines curve. makes a difference only in `path='smooth'`. examples: | ||
- `curveness={false}` will make the line straight without curves(exactly like path='straight'). | ||
@@ -239,5 +329,25 @@ - `curveness={true}` will choose default values of curveness. | ||
#### dashness | ||
</details> | ||
_optional, default: false_ \ | ||
<a name="gridBreak"></a> | ||
<details> | ||
<summary>gridBreak</summary> | ||
defines where the line will break when `path='grid'`. value should be a number from 0 to 1. | ||
examples: | ||
- `gridBreak={0.2}` will make the line straight without curves(exactly like path='straight'). | ||
</details> | ||
<a name="dashness"></a> | ||
<details> | ||
<summary>dashness</summary> | ||
can make the arrow dashed and can even animate. if true default values(for dashness) are chosen. if object is passed | ||
@@ -249,10 +359,159 @@ then default values are chosen except what passed. examples: | ||
</details> | ||
<a name="shows"></a> | ||
<details> | ||
<summary>showHead, showTail and showXarrow</summary> | ||
`showXarrow`: show or not show Xarrow? (can be used to restart the drawing animation) | ||
`showHead`: show or not the arrow head? | ||
`showTail`: show or not the arrow tail? | ||
- `showXarrow={false}` - will hide (unmount) Xarrow and his labels. | ||
- `showHead={false}` - will hide the arrow head. | ||
</details> | ||
<a name="animateDrawing"></a> | ||
<details> | ||
<summary>animateDrawing</summary> | ||
can animate the drawing of the arrow using svg animation. type: boolean|number. if true animation duration is 1s. if | ||
number is passed then animation duration is number's value in seconds. examples: | ||
- `animateDrawing` will animate the drawing of the arrow in 1 second. | ||
- `animateDrawing={5}` will animate the drawing of the arrow in 5 seconds. | ||
- `animateDrawing={0.1}` will animate the drawing of the arrow in 100 milliseconds. | ||
</details> | ||
<a name="customsvgs"></a> | ||
<details> | ||
<summary> custom svg arrows - headShape and tailShape</summary> | ||
[//]: # (todo: add svg custom shapes docs!) | ||
new feature! you can customize the svg edges (head or tail) of the arrow. you can use predefined svg by passing | ||
string,one of `"arrow1" | "circle" | "arrow1"` | ||
#### simple usage: | ||
_headShape type:string_ | ||
<table> | ||
<tr> | ||
<th>Code</th> | ||
<th>Result</th> | ||
</tr> | ||
<tr> | ||
<td> | ||
```jsx | ||
<xarrow headShape='circle'/> | ||
``` | ||
</td> | ||
<td> | ||
![img_1.png](images/fillCircle.png) | ||
</td> | ||
</tr> | ||
<tr> | ||
<td> | ||
```jsx | ||
<xarrow headShape='circle' | ||
arrowHeadProps={"fill": "transparent", | ||
"strokeWidth": "0.1", | ||
"stroke": "CornflowerBlue"} | ||
/> | ||
``` | ||
</td> | ||
<td> | ||
![img_1.png](images/emptyCircle.png) | ||
</td> | ||
</tr> | ||
<tr> | ||
<td> | ||
```jsx | ||
<xarrow headShape='heart'/> | ||
``` | ||
</td> | ||
<td> | ||
![img.png](images/heart.png) | ||
</td> | ||
</tr> | ||
</table> | ||
you can import `arrowShapes` which is object contains all predefined svg shapes. | ||
<details> | ||
<summary>custom usage</summary> | ||
you can also pass _your own_ svg shapes: | ||
```typescript | ||
headShapeType = { | ||
svgElem: T | ||
: | ||
'circle' | 'ellipse' | 'line' | 'path' | 'polygon' | 'polyline' | 'rect'; | ||
svgProps ? : JSX.IntrinsicElements[T]; | ||
offsetForward ? : number; | ||
} | ||
; | ||
``` | ||
for example, you can pass the following object, and it will be exactly equivalent to passing `'arrow1'`: | ||
```js | ||
headShape = { | ||
svgElem: 'path', | ||
svgProps: {d: `M 0 0 L 1 0.5 L 0 1 L 0.25 0.5 z`}, | ||
offsetForward: 0.25 | ||
} | ||
``` | ||
`svgElem` - an svg element like `path` or `circle`. | ||
`svgProps` - props that will be passed to the svg element. | ||
`offsetForward` - how much to offset tht line into the svg element(from 0 to 1). normally the line will connect to the | ||
start of the svgElem. for example in case of the default arrow you want the line to enter 25% into the svgElem. | ||
don't forget about `arrowHeadProps` and `arrowTailProps` in case you want to use default shape but custom svg props. | ||
**in case you pass a custom svg element:** currently you have to adjust the path to start from 0,0 and to be at size box | ||
1x1 in order to make the custom shape look like the default shapes in size, in later versions it is planned to support | ||
automatic adjustment using getBBox() function. | ||
</details> | ||
</details> | ||
### advanced customization | ||
<a name="advancedCustom"></a> | ||
<details> | ||
### passing props | ||
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`. | ||
passed to xarrow so by default it'll be passed down to `divContainer`. | ||
#### passProps | ||
_optional, default: {}_ \ | ||
you can pass properties to visible parts(body and head) of the arrow (such event handlers and much more). this supposed | ||
@@ -272,4 +531,2 @@ to be enough for most cases. examples: | ||
_optional, default: {}_ \ | ||
![image](https://user-images.githubusercontent.com/47307889/95031511-09ed5100-06bf-11eb-95a3-4cdc8d0be0ad.png) | ||
@@ -294,3 +551,3 @@ | ||
##### SVGcanvasStyle divContainerStyle | ||
##### SVGcanvasStyle, divContainerStyle | ||
@@ -300,48 +557,30 @@ if you wish to pass style to divContainer or SVGcanvas use `SVGcanvasStyle`,`divContainerStyle` and not `SVGcanvasProps` | ||
[//]: # (todo: check if width and height can't be automatically determined) | ||
##### _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 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. | ||
##### _cpx1Offset,_cpy1Offset,_cpx2Offset,_cpy2Offset | ||
_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 | ||
</details> | ||
default props is as follows: | ||
## Versions | ||
```jsx | ||
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, | ||
_debug: false, | ||
_cpx1Offset: 0, | ||
_cpy1Offset: 0, | ||
_cpx2Offset: 0, | ||
_cpy2Offset: 0, | ||
}; | ||
``` | ||
See [CHANGELOG.md](./CHANGELOG.md) in this repo. | ||
## Versions | ||
See [CHANGELOG.md](https://github.com/Eliav2/react-xarrows/blob/master/CHANGELOG.md) in this repo. | ||
<style> | ||
details { | ||
border: 1px solid #aaa; | ||
border-radius: 4px; | ||
padding: .5em .5em 0; | ||
margin: 1em 0; | ||
} | ||
</style> |
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
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
130716
28
1276
573
7
2
1