Парень, я столкнулся с проблемой. У меня есть двумерные данные. Данные имеют вложенную структуру, которая содержит ссылки.
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"
}
]
];
Моя задача состоит в том, чтобы использовать эти двухмерные данные и создать мобильное меню, react
которое имеет структуру, похожую на панель.
Тем не менее, я стараюсь творить так. То, что я сделал, я обработал каждый суб-массив как отдельную панель div
. Во-первых, элементы подмассива будут рассматриваться на корневой панели, которая по умолчанию видима. Если у элемента есть children
свойство, это означает, next
что для этого элемента списка генерируется динамически кнопка. Когда мы нажимаем на эту кнопку, она добавляет is-visible
класс на панель. Но вопрос в том, как он будет отслеживать, какая панель связана с этим нажатием кнопки ? Я пытаюсь использовать состояние с activeId
и prevId
Но мое индексирование работает неправильно и не открывается правильная панель. Вы можете проверить мое решение на панели Chrome Inspector. Я ценю, если вы скажете мне, что я делаю не так?
Код:
// 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>
index
а затем создать это меню. Кстати, данные поступают из внешнего API. Но использование вашего подхода действительно полезно для решения проблемы.Я думаю тебе просто нужно
handlePanelBtn(item.title, childItemIndex, prevId)
вместо
handlePanelBtn(item.title, index, prevId)
https://codesandbox.io/s/panel-menu-2uwxo
источник
products
списка элементовnext
. Это не открытие дочерних элементовproducts
списка.Я сделал это с контекстным API, чтобы упростить вашу логику отображения панели.
Создайте контекст с именем PanelContext, который имеет массив панелей, который можно использовать для отображения текущей панели и возврата в меню.
и создал компонент панели, который будет рекурсивно создавать всю панель и показывать панель, которая активна на основе значения контекста.
Я сломал ваш CSS.
и использовал только один объект с глубоко вложенными детьми.
Вот рабочие коды andbox: https://codesandbox.io/s/panel-menu-c871j
источник