arrowjoin
Version:
ArrowJoin is a creative and functional React library that effortlessly connects two React components with a sleek arrow.
349 lines • 14.3 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = __importStar(require("react"));
var utils_1 = require("./utils");
var lodash_1 = __importDefault(require("lodash"));
var constants_1 = require("../constants");
var parseLabels = function (label) {
var parsedLabel = { start: null, middle: null, end: null };
if (label) {
if (typeof label === 'string' || react_1.default.isValidElement(label))
parsedLabel.middle = label;
else {
for (var key in label) {
parsedLabel[key] = label[key];
}
}
}
return parsedLabel;
};
var parseAnchor = function (anchor) {
// convert to array
var anchorChoice = Array.isArray(anchor) ? anchor : [anchor];
//convert to array of objects
var anchorChoice2 = anchorChoice.map(function (anchorChoice) {
if (typeof anchorChoice === 'string') {
return { position: anchorChoice };
}
else
return anchorChoice;
});
//remove any invalid anchor names
anchorChoice2 = anchorChoice2.filter(function (an) {
return constants_1.cAnchorEdge.includes(an.position);
});
if (anchorChoice2.length == 0)
anchorChoice2 = [{ position: 'auto' }];
//replace any 'auto' with ['left','right','bottom','top']
var autosAncs = anchorChoice2.filter(function (an) { return an.position === 'auto'; });
if (autosAncs.length > 0) {
anchorChoice2 = anchorChoice2.filter(function (an) { return an.position !== 'auto'; });
anchorChoice2.push.apply(anchorChoice2, autosAncs.flatMap(function (anchorObj) {
return ['left', 'right', 'top', 'bottom'].map(function (anchorName) {
return __assign(__assign({}, anchorObj), { position: anchorName });
});
}));
}
// default values
var anchorChoice3 = anchorChoice2.map(function (anchorChoice) {
if (typeof anchorChoice === 'object') {
var anchorChoiceCustom = anchorChoice;
if (!anchorChoiceCustom.position)
anchorChoiceCustom.position = 'auto';
if (!anchorChoiceCustom.offset)
anchorChoiceCustom.offset = { x: 0, y: 0 };
if (!anchorChoiceCustom.offset.y)
anchorChoiceCustom.offset.y = 0;
if (!anchorChoiceCustom.offset.x)
anchorChoiceCustom.offset.x = 0;
anchorChoiceCustom =
anchorChoiceCustom;
return anchorChoiceCustom;
}
else
return anchorChoice;
});
return anchorChoice3;
};
var parseDashness = function (dashness, props) {
var dashStroke = 0, dashNone = 0, animDashSpeed, animDirection = 1;
if (typeof dashness === 'object') {
dashStroke = dashness.strokeLen || props.strokeWidth * 2;
dashNone = dashness.strokeLen ? dashness.nonStrokeLen : props.strokeWidth;
animDashSpeed = dashness.animation ? dashness.animation : null;
}
else if (typeof dashness === 'boolean' && dashness) {
dashStroke = props.strokeWidth * 2;
dashNone = props.strokeWidth;
animDashSpeed = null;
}
return {
strokeLen: dashStroke,
nonStrokeLen: dashNone,
animation: animDashSpeed,
animDirection: animDirection,
};
};
var parseEdgeShape = function (svgEdge) {
if (typeof svgEdge == 'string') {
if (svgEdge in constants_1.arrowShapes)
svgEdge = constants_1.arrowShapes[svgEdge];
else {
console.warn("'".concat(svgEdge, "' is not supported arrow shape. the supported arrow shapes is one of ").concat(constants_1.cArrowShapes, ".\n reverting to default shape."));
svgEdge = constants_1.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?.svgProps === undefined) svgEdge.svgProps = arrowShapes.arrow1.svgProps;
return svgEdge;
};
var parseGridBreak = function (gridBreak) {
var resGridBreak = (0, utils_1.xStr2absRelative)(gridBreak);
if (!resGridBreak)
resGridBreak = { relative: 0.5, abs: 0 };
return resGridBreak;
};
/**
* should be wrapped with any changed prop that is affecting the points path positioning
* @param propVal
* @param updateRef
*/
var withUpdate = function (propVal, updateRef) {
if (updateRef)
updateRef.current = true;
return propVal;
};
var noParse = function (userProp) { return userProp; };
var noParseWithUpdatePos = function (userProp, _, updatePos) {
return withUpdate(userProp, updatePos);
};
var parseNumWithUpdatePos = function (userProp, _, updatePos) {
return withUpdate(Number(userProp), updatePos);
};
var parseNum = function (userProp) { return Number(userProp); };
var parsePropsFuncs = {
start: function (userProp) { return (0, utils_1.getElementByPropGiven)(userProp); },
end: function (userProp) { return (0, utils_1.getElementByPropGiven)(userProp); },
startAnchor: function (userProp, _, updatePos) {
return withUpdate(parseAnchor(userProp), updatePos);
},
endAnchor: function (userProp, _, updatePos) {
return withUpdate(parseAnchor(userProp), updatePos);
},
labels: function (userProp) { return parseLabels(userProp); },
color: noParse,
lineColor: function (userProp, propsRefs) { return userProp || propsRefs.color; },
headColor: function (userProp, propsRefs) { return userProp || propsRefs.color; },
tailColor: function (userProp, propsRefs) { return userProp || propsRefs.color; },
strokeWidth: parseNumWithUpdatePos,
showHead: noParseWithUpdatePos,
headSize: parseNumWithUpdatePos,
showTail: noParseWithUpdatePos,
tailSize: parseNumWithUpdatePos,
path: noParseWithUpdatePos,
curveness: parseNumWithUpdatePos,
gridBreak: function (userProp, _, updatePos) {
return withUpdate(parseGridBreak(userProp), updatePos);
},
// // gridRadius = strokeWidth * 2, //todo
dashness: function (userProp, propsRefs) { return parseDashness(userProp, propsRefs); },
headShape: function (userProp) { return parseEdgeShape(userProp); },
tailShape: function (userProp) { return parseEdgeShape(userProp); },
showXarrow: noParse,
animateDrawing: noParse,
zIndex: parseNum,
passProps: noParse,
className: null,
onClick: null,
arrowBodyProps: noParseWithUpdatePos,
arrowHeadProps: noParseWithUpdatePos,
arrowTailProps: noParseWithUpdatePos,
SVGcanvasProps: noParseWithUpdatePos,
divContainerProps: noParseWithUpdatePos,
divContainerStyle: noParseWithUpdatePos,
SVGcanvasStyle: noParseWithUpdatePos,
_extendSVGcanvas: noParseWithUpdatePos,
_debug: noParseWithUpdatePos,
_cpx1Offset: noParseWithUpdatePos,
_cpy1Offset: noParseWithUpdatePos,
_cpx2Offset: noParseWithUpdatePos,
_cpy2Offset: noParseWithUpdatePos,
};
//build dependencies
var propsDeps = {};
//each prop depends on himself
for (var propName in parsePropsFuncs) {
propsDeps[propName] = [propName];
}
// 'lineColor', 'headColor', 'tailColor' props also depends on 'color' prop
for (var _i = 0, _a = ['lineColor', 'headColor', 'tailColor']; _i < _a.length; _i++) {
var propName = _a[_i];
propsDeps[propName].push('color');
}
var parseGivenProps = function (props, propsRef) {
var _a;
for (var _i = 0, _b = Object.entries(props); _i < _b.length; _i++) {
var _c = _b[_i], name_1 = _c[0], val = _c[1];
propsRef[name_1] = (_a = parsePropsFuncs === null || parsePropsFuncs === void 0 ? void 0 : parsePropsFuncs[name_1]) === null || _a === void 0 ? void 0 : _a.call(parsePropsFuncs, val, propsRef);
}
return propsRef;
};
var defaultProps = {
start: null,
end: null,
startAnchor: 'auto',
endAnchor: 'auto',
labels: null,
color: 'CornflowerBlue',
lineColor: null,
headColor: null,
tailColor: null,
strokeWidth: 4,
showHead: true,
headSize: 6,
showTail: false,
tailSize: 6,
path: 'smooth',
curveness: 0.8,
gridBreak: '50%',
// gridRadius : strokeWidth * 2, //todo
dashness: false,
headShape: 'arrow1',
tailShape: 'arrow1',
showXarrow: true,
animateDrawing: false,
zIndex: 0,
passProps: {},
arrowBodyProps: {},
arrowHeadProps: {},
arrowTailProps: {},
SVGcanvasProps: {},
divContainerProps: {},
divContainerStyle: {},
SVGcanvasStyle: {},
_extendSVGcanvas: 0,
className: null,
onClick: null,
_debug: false,
_cpx1Offset: 0,
_cpy1Offset: 0,
_cpx2Offset: 0,
_cpy2Offset: 0,
};
var initialParsedProps = {};
initialParsedProps = parseGivenProps(defaultProps, initialParsedProps);
console.log('initialParsedProps', initialParsedProps);
var initialValVars = {
startPos: { x: 0, y: 0, right: 0, bottom: 0 },
endPos: { x: 0, y: 0, right: 0, bottom: 0 },
};
// const parseAllProps = () => parseGivenProps(defaultProps, initialParsedProps);
function deepCompareEquals(a, b) {
return lodash_1.default.isEqual(a, b);
}
function useDeepCompareMemoize(value) {
var ref = (0, react_1.useRef)();
// it can be done by using useMemo as well
// but useRef is rather cleaner and easier
if (!deepCompareEquals(value, ref.current)) {
ref.current = value;
}
return ref.current;
}
function useDeepCompareEffect(callback, dependencies) {
(0, react_1.useLayoutEffect)(callback, dependencies.map(useDeepCompareMemoize));
}
/**
* smart hook that provides parsed props to Xarrow and will trigger rerender whenever given prop is changed.
*/
var useXarrowProps = function (userProps) {
var _a = (0, react_1.useState)(initialParsedProps), propsRefs = _a[0], setPropsRefs = _a[1];
var shouldUpdatePosition = (0, react_1.useRef)(false);
// const _propsRefs = useRef(initialParsedProps);
// const propsRefs = _propsRefs.current;
propsRefs['shouldUpdatePosition'] = shouldUpdatePosition;
var curProps = __assign(__assign({}, defaultProps), userProps);
var _loop_1 = function (propName) {
(0, react_1.useLayoutEffect)(function () {
var _a;
propsRefs[propName] = (_a = parsePropsFuncs === null || parsePropsFuncs === void 0 ? void 0 : parsePropsFuncs[propName]) === null || _a === void 0 ? void 0 : _a.call(parsePropsFuncs, curProps[propName], propsRefs, shouldUpdatePosition);
// console.log('prop update:', propName, 'with value', propsRefs[propName]);
setPropsRefs(__assign({}, propsRefs));
}, propsDeps[propName].map(function (name) { return userProps[name]; }));
};
// react states the number of hooks per render must stay constant,
// this is ok we are using these hooks in a loop, because the number of props in defaultProps is constant,
// so the number of hook we will fire each render will always be the same.
// update the value of the ref that represents the corresponding prop
// for example: if given 'start' prop would change call getElementByPropGiven(props.start) and save value into propsRefs.start.current
// why to save refs to props parsed values? some of the props require relatively expensive computations(like 'start' and 'startAnchor').
// this will always run in the same order and THAT'S WAY ITS LEGAL
for (var propName in defaultProps) {
_loop_1(propName);
}
// rerender whenever position of start element or end element changes
var _b = (0, react_1.useState)(initialValVars), valVars = _b[0], setValVars = _b[1];
var startPos = (0, utils_1.getElemPos)(propsRefs.start);
useDeepCompareEffect(function () {
valVars.startPos = startPos;
shouldUpdatePosition.current = true;
setValVars(__assign({}, valVars));
// console.log('start update pos', startPos);
}, [startPos]);
var endPos = (0, utils_1.getElemPos)(propsRefs.end);
useDeepCompareEffect(function () {
valVars.endPos = endPos;
shouldUpdatePosition.current = true;
setValVars(__assign({}, valVars));
// console.log('end update pos', endPos);
}, [endPos]);
(0, react_1.useLayoutEffect)(function () {
// console.log('svg shape changed!');
shouldUpdatePosition.current = true;
setValVars(__assign({}, valVars));
}, [propsRefs.headShape.svgElem, propsRefs.tailShape.svgElem]);
return [propsRefs, valVars];
};
exports.default = useXarrowProps;
//# sourceMappingURL=useXarrowProps.js.map