diff --git a/src/rendering/HorizontalLayout/index.js b/src/rendering/HorizontalLayout/index.js new file mode 100644 index 0000000..9336c22 --- /dev/null +++ b/src/rendering/HorizontalLayout/index.js @@ -0,0 +1,90 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import * as style from 'rendering/style'; + +const HorizontalLayout = ({ connectorPath, transforms, children }) => { + return <> + + { React.Children.map(children, (child, i) => ( + { child } + )) } + ; +}; + +HorizontalLayout.defaultProps = { + withConnectors: false, + spacing: 10 +}; + +HorizontalLayout.propTypes = { + spacing: PropTypes.number, + withConnectors: PropTypes.bool, + connectorPath: PropTypes.string, + transforms: PropTypes.arrayOf(PropTypes.string), + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node + ]).isRequired +}; + +const generateOffsetBoxes = (boxes, axisY, spacing) => { + let offset = 0; + return boxes.map(box => { + try { + return { + ...box, + offsetX: offset, + offsetY: axisY - box.axisY + }; + } + finally { + offset += box.width + spacing; + } + }); +}; +const calculateChildTransforms = boxes => + boxes.map(box => `translate(${ box.offsetX } ${ box.offsetY })`); +const calculateConnectorPath = (boxes, axisY) => { + let last = boxes[0]; + return boxes.slice(1).map(box => { + try { + return ` + M${ last.offsetX + last.axisX2 },${ axisY } + H${ box.offsetX + box.axisX1 } + `; + } + finally { + last = box; + } + }).join(''); +}; +const layout = data => { + const { withConnectors, spacing } = { + ...HorizontalLayout.defaultProps, + ...data.props + }; + const childBoxes = data.children.map(child => child.box); + + data.box = { + width: childBoxes.reduce((width, box) => width + box.width, 0) + + (childBoxes.length - 1) * spacing, + height: Math.max(...(childBoxes.map(box => box.axisY))) + + Math.max(...(childBoxes.map(box => box.height - box.axisY))), + axisY: Math.max(...(childBoxes.map(box => box.axisY))) + }; + + const offsetBoxes = generateOffsetBoxes(childBoxes, data.box.axisY, spacing); + data.props = { + ...data.props, + transforms: calculateChildTransforms(offsetBoxes), + connectorPath: withConnectors + ? calculateConnectorPath(offsetBoxes, data.box.axisY) + : undefined + }; + + return data; +}; + +export default HorizontalLayout; +export { layout }; diff --git a/src/rendering/style.js b/src/rendering/style.js index 2f896a0..4b5e474 100644 --- a/src/rendering/style.js +++ b/src/rendering/style.js @@ -18,3 +18,8 @@ export const strokeBase = { strokeWidth: '2px', stroke: black }; + +export const connectors = { + fillOpacity: 0, + ...strokeBase +}; diff --git a/src/rendering/types.js b/src/rendering/types.js index da7e963..763d888 100644 --- a/src/rendering/types.js +++ b/src/rendering/types.js @@ -1,9 +1,11 @@ import * as SVG from 'rendering/SVG'; import * as Text from 'rendering/Text'; import * as Box from 'rendering/Box'; +import * as HorizontalLayout from 'rendering/HorizontalLayout'; export default { SVG, Text, - Box + Box, + HorizontalLayout }; diff --git a/src/syntax/js.js b/src/syntax/js.js index 02e0478..83a7eb4 100644 --- a/src/syntax/js.js +++ b/src/syntax/js.js @@ -6,18 +6,37 @@ const parse = expr => { type: 'SVG', children: [ { - type: 'Box', + type: 'HorizontalLayout', props: { - theme: 'literal' + withConnectors: true }, children: [ { - type: 'Text', + type: 'Box', props: { - quoted: true + theme: 'literal' }, children: [ - `JS => ${ expr }` + { + type: 'Text', + children: [ + 'JS' + ] + } + ] + }, + { + type: 'Box', + props: { + theme: 'literal' + }, + children: [ + { + type: 'Text', + children: [ + expr + ] + } ] } ] diff --git a/src/syntax/pcre.js b/src/syntax/pcre.js index 12b2fa4..6877c50 100644 --- a/src/syntax/pcre.js +++ b/src/syntax/pcre.js @@ -6,18 +6,37 @@ const parse = expr => { type: 'SVG', children: [ { - type: 'Box', + type: 'HorizontalLayout', props: { - theme: 'literal' + withConnectors: true }, children: [ { - type: 'Text', + type: 'Box', props: { - quoted: true + theme: 'literal' }, children: [ - `PCRE => ${ expr }` + { + type: 'Text', + children: [ + 'PCRE' + ] + } + ] + }, + { + type: 'Box', + props: { + theme: 'literal' + }, + children: [ + { + type: 'Text', + children: [ + expr + ] + } ] } ]