Reputation: 93
guys. I'm learning reactJS and I'm trying to make a nested accordion from scratch, but i can't figure out how to handle the sub-levels.
This is what I've built so far. I have a few questions.
Thanks!
My data:
export default [
{
id: 1,
title: 'Smokes',
children: [
{
id: '1A',
title: 'CT',
children: [
{
id: '',
title: 'From half wall',
// children: [],
},
{
id: '',
title: 'From car',
// children: [],
},
{
id: '',
title: 'From ct Base',
// children: [],
},
],
},
{
id: '1B',
title: 'A long',
children: [
{
id: '',
title: 'From top mid',
// children: [],
},
{
id: '',
title: 'From 2nd mid',
// children: [],
},
],
},
],
},
{
id: 2,
title: 'Flashes',
children: [
{
id: '2A',
title: 'Pit',
children: [
{
id: '',
title: 'From t top mid',
// children: [],
},
{
id: '',
title: 'From 2nd mid',
// children: [],
},
{
id: '',
title: 'From apps',
// children: [],
},
],
},
{
id: '2B',
title: 'Bomb A',
children: [
{
id: '',
title: 'From banana',
// children: [],
},
],
},
],
},
{
id: 3,
title: 'Molotovs',
children: [
{
id: '3A',
title: 'Patio',
children: [
{
id: '',
title: 'From t top mid',
// children: [],
},
{
id: '',
title: 'From boiler',
// children: [],
},
],
},
{
id: '3B',
title: 'Back site A',
children: [
{
id: '',
title: 'From A long',
// children: [],
},
],
},
],
},
];
Accordion.js:
import React from 'react';
import { useState } from 'react';
import accordionData from '../../data/accordion';
function Accordion() {
const [selected, setselected] = useState('');
const toggle = () => {
setselected(selected === '' ? 'active' : '');
};
return (
<section className="accordion-section">
<div className=" flex-container accordion-wrapper">
<div className="accordion">
{accordionData.map((item) => (
<div className="accordion-items">
<div className="accordion-title" onClick={() => toggle()}>
<h2>{item.title}</h2>
{/* If seleceted render "-", otherwise "+" */}
<span>{selected === 'active' ? '-' : '+'}</span>
</div>
<div>
{item.children.map((stratItem) => (
<div>
<div
className={`accordion-item destination ${selected}`}
onClick={() => toggle()}
>
{stratItem.title}
</div>
<div>
{stratItem.children.map((originItem) => (
<div
className={`accordion-item origin ${selected}`}
onClick={() => toggle()}
>
{originItem.title}
</div>
))}
</div>
</div>
))}
</div>
</div>
))}
</div>
</div>
</section>
);
}
export default Accordion;
app.css:
.accordion-section {
background-color: #687980;
}
.accordion-wrapper{
background-color: #687980;
color: #F5F2E7;
height: 100%;
width: auto;
}
.accordion {
width: 25%;
height: auto;
padding: 15px;
}
.accordion-title {
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 10px;
cursor: pointer;
background-color: #2C3333;
}
.accordion-title span {
font-size: 30px;
padding-right: 5px;
}
.accordion-items {
padding: 15px;
margin: 5px 0;
font-weight: lighter;
}
.accordion-item {
height: 30px;
display: flex;
align-items: center;
/* border: 1px solid #2C3333; */
margin: 2px;
cursor: pointer;
transition: 0.20s ease;
background-color: #4f4f53;
}
.accordion-item:hover{
transform: translate(5px, 5px);
transform: scale(5px);
background-color: #cfe3f3;
font-weight: bold;
color:#2C3333
}
.destination {
padding-left: 15px;
font-weight: 500;
font-size: 18px;
max-height: 0;
overflow: hidden;
transition: all 0.5s cubic-bezier(0,1,0,1);
}
.destination.active {
height: auto;
max-height: 999px;
transition: all 0.5s cubic-bezier(0,1,0,1);
}
.origin {
padding-left: 30px;
font-weight: 300;
max-height: 0;
overflow: hidden;
transition: all 0.5s cubic-bezier(0,1,0,1);
}
.origin.active {
height: auto;
max-height: 999;
transition: all 0.5s cubic-bezier(0,1,0,1);
}
Upvotes: 1
Views: 4463
Reputation: 13265
You can achieve it with a simple recursive function that will work for any depth of your accordion. Change the styles as you want. I have created SubLevelComp
as you tried to handle the visibility of each sub-level according to a component level state.
Try like this:
const accordionData = [ { id: 1, title: "Smokes", children: [ { id: "1A", title: "CT", children: [ { id: "", title: "From half wall" }, { id: "", title: "From car" }, { id: "", title: "From ct Base" } ] }, { id: "1B", title: "A long", children: [ { id: "", title: "From top mid" }, { id: "", title: "From 2nd mid" } ] } ] }, { id: 2, title: "Flashes", children: [ { id: "2A", title: "Pit", children: [ { id: "", title: "From t top mid" }, { id: "", title: "From 2nd mid" }, { id: "", title: "From apps" } ] }, { id: "2B", title: "Bomb A", children: [ { id: "", title: "From banana" } ] } ] }, { id: 3, title: "Molotovs", children: [ { id: "3A", title: "Patio", children: [ { id: "", title: "From t top mid" }, { id: "", title: "From boiler" } ] }, { id: "3B", title: "Back site A", children: [ { id: "", title: "From A long" } ] } ] } ];
const SubLevelComp = ({ item, renderNestedLevels }) => {
const [selected, setselected] = React.useState("");
const toggle = () => {
setselected(selected === "" ? "active" : "");
};
const hasChidlren = (item) => {
return Array.isArray(item.children) && item.children.length > 0;
};
return (
<div>
<p
onClick={() => toggle()}
style={{ cursor: hasChidlren(item) ? "pointer" : "" }}
>
{item.title}{" "}
{hasChidlren(item) && <span>{selected === "active" ? "-" : "+"}</span>}
</p>
{selected && (
<div style={{ marginLeft: "20px" }}>
{hasChidlren(item) && renderNestedLevels(item.children)}
</div>
)}
</div>
);
};
function Accordion() {
const renderNestedLevels = (data) => {
return data.map((item, itemIndex) => (
<SubLevelComp item={item} renderNestedLevels={renderNestedLevels} key={itemIndex}/>
));
};
return renderNestedLevels(accordionData);
}
ReactDOM.render(<Accordion />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>
Upvotes: 3