I'm faced with a challenge involving a reusable React list component that supports nested children. My goal is to visually connect the parent div to its direct children using arrows, similar to the image linked below.
Below is an illustration of a nested list component in action:
import React from 'react';
import ListElement from './ListElement.js';
const List = () => (
<>
<ListElement>
<ListElement>
<ListElement>
<ListElement />
<ListElement>
<ListElement />
</ListElement>
</ListElement>
<ListElement />
</ListElement>
<ListElement />
</ListElement>
<ListElement />
</>
);
export default List;
The structure of the ListElement component is as follows:
import React from 'react';
const ListElement = props => {
const indentationStyle = { paddingLeft: `${3 * props.indent}rem`,
position: 'relative'};
const lineStyle = {
left: `${2 + 3 * (props.indent - 1.2)}rem`,
};
const tile = (
<div style={indentationStyle}>
{props.indent > 0 ? (
<div className={'arrow-line-container'} style={lineStyle}>
<div className={'arrow-line'}/>
<div className={'curve-arrow-line'}/>
</div>
) : null}
<div
style={{
border: '1px solid black',
padding: '1rem',
marginBottom: '1rem',
}}
>
I am a ListElement
</div>
</div>
);
const getChildren = () => {
let elements = React.Children.toArray(props.children);
// increase indent prop of each child and assign what number child it is in the list
elements = elements.map((element, index) => {
return React.cloneElement(element, {
...element.props,
indent: props.indent + 1,
childNumber: index,
});
});
return elements;
};
const childTiles = <div className={'child-tile'}>{getChildren()}</div>;
const arrowStyle = {
backgroundPosition: `${1.3 + 3 * (props.indent - 1)}rem`,
};
return (
<>
<ul className={'no-bullet'}>
<li
className={props.indent === 0 ? 'no-arrow' : 'arrow'}
style={arrowStyle}
>
{tile}
</li>
{props.children ? childTiles : null}
</ul>
</>
);
};
ListElement.defaultProps = {
childNumber: 0,
indent: 0,
};
export default ListElement;
Here's how the CSS styling for this functionality looks like:
ul.no-bullet {
list-style-type: none;
padding: 0;
margin: 0;
}
.arrow-line {
border-left: 2px solid #6a6969;
content: "";
position: absolute;
height: 65%;
}
li.arrow {
background: url("./arrow.png") no-repeat;
}
li.no-arrow {
display: block;
}
Currently, I have been using <li>
elements for the list and replacing the bullet points with arrow images. The main struggle lies in accurately calculating the line height and top position for better visual alignment between elements. Any recommendations or insights on this issue would be greatly appreciated.
You can also check out the Plunker demo here.