regexper-static/src/components/SVG/VerticalLayout.js
Jeff Avallone 6ff9145603 Adding tests for SVG components
Jest/enzyme/jsdom is kicking out some nastly looking messages because it
doesn't recognize various SVG elements, but they appear to be harmless.
2018-02-17 13:04:19 -05:00

144 lines
4.4 KiB
JavaScript

import React from 'react';
import PropTypes from 'prop-types';
import { List } from 'immutable';
import Base from './Base';
import style from './style';
import Path from './path';
const connectorMargin = 20;
/** @extends React.PureComponent */
class VerticalLayout extends Base {
static defaultProps = {
withConnectors: false,
spacing: 10
}
constructor(props) {
super(props);
this.state = {
childTransforms: List()
};
}
updateChildTransforms(childBoxes) {
return childBoxes.reduce((transforms, box, i) => (
transforms.set(i, `translate(${ box.offsetX } ${ box.offsetY })`)
), this.state.childTransforms);
}
makeCurve(box) {
const thisBox = this.getBBox();
const distance = Math.abs(box.offsetY + box.axisY - thisBox.axisY);
if (distance >= 15) {
const curve = (box.axisY + box.offsetY > thisBox.axisY) ? 10 : -10;
return new Path()
// Left
.moveTo({ x: 10, y: box.axisY + box.offsetY - curve })
.quadraticCurveTo({ cx: 0, cy: curve, x: 10, y: curve, relative: true })
.lineTo({ x: box.offsetX + box.axisX1 })
// Right
.moveTo({ x: thisBox.width - 10, y: box.axisY + box.offsetY - curve })
.quadraticCurveTo({ cx: 0, cy: curve, x: -10, y: curve, relative: true })
.lineTo({ x: box.offsetX + box.axisX2 });
} else {
const anchor = box.offsetY + box.axisY - thisBox.axisY;
return new Path()
// Left
.moveTo({ x: 0, y: thisBox.axisY })
.cubicCurveTo({ cx1: 15, cy1: 0, cx2: 10, cy2: anchor, x: 20, y: anchor, relative: true })
.lineTo({ x: box.offsetX + box.axisX1 })
// Right
.moveTo({ x: thisBox.width, y: thisBox.axisY })
.cubicCurveTo({ cx1: -15, cy1: 0, cx2: -10, cy2: anchor, x: -20, y: anchor, relative: true })
.lineTo({ x: box.offsetX + box.axisX2 });
}
}
makeSide(box) {
const thisBox = this.getBBox();
const distance = Math.abs(box.offsetY + box.axisY - thisBox.axisY);
if (distance >= 15) {
const shift = (box.offsetY + box.axisY > thisBox.axisY) ? 10 : -10;
const edge = box.offsetY + box.axisY - shift;
return new Path()
// Left
.moveTo({ x: 0, y: thisBox.axisY })
.quadraticCurveTo({ cx: 10, cy: 0, x: 10, y: shift, relative: true })
.lineTo({ y: edge })
// Right
.moveTo({ x: thisBox.width, y: thisBox.axisY })
.quadraticCurveTo({ cx: -10, cy: 0, x: -10, y: shift, relative: true })
.lineTo({ y: edge });
}
}
preReflow() {
return this.children;
}
reflow() {
const { spacing, withConnectors } = this.props;
const childBoxes = this.children.map(child => child.getBBox());
const horizontalCenter = childBoxes.reduce((center, box) => Math.max(center, box.width / 2), 0);
const margin = withConnectors ? connectorMargin : 0;
const width = childBoxes.reduce((width, box) => Math.max(width, box.width), 0) + 2 * margin;
const height = childBoxes.reduce((height, box) => height + box.height, 0) + (childBoxes.length - 1) * spacing;
this.setBBox({ width, height }, { axisY: true, axisX1: true, axisX2: true });
let offset = 0;
childBoxes.forEach(box => {
box.offsetX = horizontalCenter - box.width / 2 + margin;
box.offsetY = offset;
offset += spacing + box.height;
});
return this.setStateAsync({
childTransforms: this.updateChildTransforms(childBoxes),
connectorPaths: withConnectors ? [
...childBoxes.map(box => this.makeCurve(box)),
this.makeSide(childBoxes[0]),
this.makeSide(childBoxes[childBoxes.length - 1])
].join('') : ''
});
}
childRef = i => child => this.children[i] = child
render() {
const { children } = this.props;
const { childTransforms, connectorPaths } = this.state;
this.children = [];
return <React.Fragment>
<path d={ connectorPaths } style={ style.connectors }></path>
{ React.Children.map(children, (child, i) => (
<g transform={ childTransforms.get(i) }>
{ React.cloneElement(child, {
ref: this.childRef(i)
}) }
</g>
)) }
</React.Fragment>;
}
}
VerticalLayout.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
]).isRequired,
spacing: PropTypes.number,
withConnectors: PropTypes.bool
};
export default VerticalLayout;