Chicos, estoy enfrentando un problema. Tengo un dato bidimensional. Los datos tienen una estructura anidada que contiene enlaces.
const data = [
// First Div Panel
[
{
id: 1,
url: "/services",
title: "Services"
},
{
id: 2,
title: "Products",
children: [
{
id: 3,
url: "/themes-templates",
title: "Themes & Templates"
},
{
id: 4,
url: "/open-source",
title: "Open Source"
},
{
id: 5,
url: "/solutions",
title: "Solutions"
}
]
},
{
id: 6,
url: "/work",
title: "Work",
children: [
{
id: 7,
url: "/methodology",
title: "Methodology",
children: [
{
id: 8,
url: "/agile",
title: "Agile",
children: [
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
}
]
}
]
},
{
id: 10,
url: "/contact-us",
title: "Contact Us"
}
],
// Second Div Panel which contains children of second list item
[
{
id: 3,
url: "/themes-templates",
title: "Themes & Templates"
},
{
id: 4,
url: "/open-source",
title: "Open Source"
},
{
id: 5,
url: "/solutions",
title: "Solutions"
}
],
// Third Div Panel which contains children of third list item
[
{
id: 7,
url: "/methodology",
title: "Methodology",
children: [
{
id: 8,
url: "/agile",
title: "Agile",
children: [
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
}
]
}
],
// Fourth Div Panel contains the children of the 3rd sub list item
[
{
id: 8,
url: "/agile",
title: "Agile",
children: [
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
}
],
// Fourth Div Panel contains the children of the 3rd sub sub list item
[
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
];
Mi tarea es utilizar esos datos bidimensionales y crear un menú móvil react
que tenga una estructura tipo panel de inserción.
Sin embargo, trato de crear así. Lo que hice fue tratar cada subconjunto como un panel separado div
. Primero, los elementos de la submatriz se considerarán en el panel raíz, que por defecto es visible. Si un elemento tiene una children
propiedad, significa que next
se genera un botón dinámico en ese elemento de la lista. Cuando hacemos clic en este botón, agregará una is-visible
clase en el panel. Pero, la pregunta es cómo rastreará qué panel está asociado con ese clic de botón . Intento usar un estado con activeId
y prevId
Pero mi indexación no funciona correctamente y no abre un panel correcto. Puede inspeccionar mi solución en el panel del inspector de cromo. Te agradezco si me dices lo que estoy haciendo mal.
Código:
// Get a hook function
const {useState} = React;
//#region Data
const data = [
// First Div Panel
[
{
id: 1,
url: "/services",
title: "Services"
},
{
id: 2,
title: "Products",
children: [
{
id: 3,
url: "/themes-templates",
title: "Themes & Templates"
},
{
id: 4,
url: "/open-source",
title: "Open Source"
},
{
id: 5,
url: "/solutions",
title: "Solutions"
}
]
},
{
id: 6,
url: "/work",
title: "Work",
children: [
{
id: 7,
url: "/methodology",
title: "Methodology",
children: [
{
id: 8,
url: "/agile",
title: "Agile",
children: [
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
}
]
}
]
},
{
id: 10,
url: "/contact-us",
title: "Contact Us"
}
],
// Second Div Panel
[
{
id: 3,
url: "/themes-templates",
title: "Themes & Templates"
},
{
id: 4,
url: "/open-source",
title: "Open Source"
},
{
id: 5,
url: "/solutions",
title: "Solutions"
}
],
// Third Div Panel
[
{
id: 7,
url: "/methodology",
title: "Methodology",
children: [
{
id: 8,
url: "/agile",
title: "Agile",
children: [
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
}
]
}
],
// Fourth Div Panel
[
{
id: 8,
url: "/agile",
title: "Agile",
children: [
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
}
],
// Fifth Div Panel
[
{
id: 9,
url: "/scrum",
title: "Scrum"
}
]
];
//#endregion Data
//#region Component
const PanelMenu = props => {
const { title } = props;
const [items, setItems] = useState(data);
// Title Header of the Panel
const [headerTitle, setHeaderTitle] = useState(title ? title : "");
// Previous Title Header of the Panel
const [prevHeaderTitle, setPrevHeaderTitle] = useState(title ? title : "");
// ActiveIndex => 0 means by default master-panel is active
const [activeId, setActiveId] = useState(0);
// PreviousIndex
const [prevId, setPrevId] = useState(0);
const handlePanelBtn = (newTitle, index, prevIndex) => {
// Title Checking
const titleProp = title ? title : "";
const prevTitle = index === 0 ? titleProp : headerTitle;
// SetStates
setPrevHeaderTitle(prevTitle);
setHeaderTitle(newTitle);
setActiveId(index);
setPrevId(prevIndex);
};
const panelRenderer = () => {
const panelsJSX = [];
for (let i = 0; i < items.length; i++) {
let childItemIndex = i;
const panels = (
<div
key={i}
id={i === 0 ? "p__master" : `p__student-${i}`}
className={
childItemIndex === activeId
? "p__panel is-visible"
: "p__panel is-hide"
}
>
<ul>
{items[i].map((item, index) => {
// It means it have children
if (item.children && item.children.length > 0) {
childItemIndex++;
return (
<li key={item.id} className="p-next">
{item.url ? (
<a href={item.url} className="p-link">
{item.title}
</a>
) : (
<div className="p-link">{item.title}</div>
)}
<button
type="button"
className="p-next__btn"
data-id={`#p__student-${childItemIndex}`}
onClick={() => handlePanelBtn(item.title, index, prevId)}
>
<span>></span>
</button>
</li>
);
} else {
return (
<li key={item.id}>
<a href={item.url} className="p-link">
{item.title}
</a>
</li>
);
}
})}
</ul>
</div>
);
panelsJSX.push(panels);
}
return panelsJSX;
};
const renderer = () => {
if (items && items.length > 0) {
return (
<div className="p">
<div className="p__wrap">
{/* Panel Actions => Header */}
<div className="p__actions">
{/* Previous Button */}
{activeId !== 0 && (
<button
type="button"
className="p-action__btn left"
onClick={() =>
handlePanelBtn(prevHeaderTitle, prevId, prevId)
}
>
<span><</span>
</button>
)}
{/* Title */}
{headerTitle && (
<div className="p-action__title">{headerTitle}</div>
)}
{/* Close Button */}
<button type="button" className="p-action__btn right">
<span>×</span>
</button>
</div>
{/* Panel children Wrapper */}
<div className="p__children">{panelRenderer()}</div>
</div>
</div>
);
}
};
return <React.Fragment>{renderer()}</React.Fragment>;
};
//#endregion Component
// Render it
ReactDOM.render(
<PanelMenu title="Menu" />,
document.getElementById("root")
)
<style>
*,:before,:after {
box-sizing: border-box;
}
.p__wrap {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 320px;
background-color: #fff;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
z-index: 1;
color: #333;
overflow-x: hidden;
}
.p__actions {
position: relative;
padding: 14px;
min-height: 54px;
border-bottom: 1px solid #dcdcdc;
}
.p-action__title {
text-align: center;
color: #333;
text-transform: uppercase;
font-weight: 700;
}
.p-action__btn {
position: absolute;
width: 54px;
height: 54px;
top: 0;
right: 0;
font-size: 16px;
color: #333;
border: none;
cursor: pointer;
}
.left {
left: 0;
}
.right {
right: 0;
}
.p__children {
position: relative;
background-color: #fff;
overflow: hidden;
height: calc(100% - 54px);
}
.p__panel {
overflow-x: hidden;
overflow-y: auto;
position: absolute;
transform: translateX(100%);
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 0;
transition: transform 0.2s ease 0s;
}
.p__panel.is-visible {
transform: translateX(0);
z-index: 1;
}
.p__panel.is-hide {
opacity: 0;
visibility: hidden;
}
.p__panel > ul {
margin: 0;
padding: 0;
}
.p__panel > ul > li {
list-style: none;
border-bottom: 1px solid #dcdcdc;
}
.p__panel > ul > li > .p-link {
color: #333;
display: block;
line-height: 22px;
font-size: 14px;
padding: 14px 24px;
background-color: transparent;
cursor: pointer;
}
.p__panel > ul > li > .p-link:hover {
background-color: #dcdcdc;
}
.p-next {
position: relative;
}
.p-next__btn {
position: absolute;
padding: 14px 16px;
font-size: 16px;
line-height: 22px;
top: 0;
right: 0;
background-color: rgb(240,240,240);
color: #333;
border: none;
border-left: 1px solid #dcdcdc;
cursor: pointer;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>