Как создать сворачиваемый список с помощью компонента Reactjs с переключателем плюс минус вместе с текстом заголовка

Я создал компонент React для меню боковой панели. Я не хочу использовать для этой цели самонастраивающийся аккордеон. Я хочу создать список меню с переключателем «плюс-минус». В приведенном ниже коде, если открыты элементы списка в разделе «Технический анализ», он должен отображать «Технический анализ» -. Как я могу получить список, похожий на прилагаемый рисунок?  введите описание изображения здесь

import React, {Fragment, useState} from "react";
import {Col, Collapse, Container, ListGroup, ListGroupItem, Row} from "react-bootstrap";
import {Link, useRouteMatch} from "react-router-dom";
import ListItem from "@material-ui/core/ListItem";
import List from "@material-ui/core/List";

const SidebarL = () => {

const [open, setOpen] = useState(false);
const [open1, setOpen1] = useState(false);
const [open2, setOpen2] = useState(false);
let {url} = useRouteMatch();


return (
    <Container>
      <Row>
        <Col>
          <div onClick={() => setOpen(!open)}
                 aria-controls="collapse-text"
                 aria-expanded={open}
            >
              <Link style={{textDecoration: "none", color: "black"}}>
                <div>
                  Technical Analysis
                </div>
              </Link>
            </div>
            <Collapse in={open}>
                <div id="collapse-text">
                    <ul className="list-unstyled border-left p-2 ml-2">
                        <li>
                          <Link to={`${url}/thedowtheory`}
                                style={{textDecoration: "none"}}
                          >
                            The Dow Theory
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/charts`}
                                style={{textDecoration: "none"}}>
                            Chart & Chart Patterns
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/trendlines`}
                                style={{textDecoration: "none"}}>
                            Trend & Trend Lines
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}>
                            Support & Resistance
                          </Link>
                        </li>
                    </ul>
                </div>
            </Collapse>
            <hr className="mt-2 mb-2" />
            <div onClick={() => setOpen1(!open1)}
                 aria-controls="collapse-text1"
                 aria-expanded={open1}
            >
                <Link style={{textDecoration: "none", color: "black"}}>
                  <div>Fundamental Analysis </div>
                </Link>
            </div>
            <Collapse in={open1}>
                <div id="collapse-text1" >
                    <ul className="list-unstyled border-left p-2 ml-2"
                    >
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                    </ul>
                </div>
            </Collapse>
            <hr className="mt-2 mb-2" />
            <div onClick={() => setOpen2(!open2)}
                 aria-controls="collapse-text2"
                 aria-expanded={open2}
            >
              <Link style={{textDecoration: "none", color: "black"}}>
                  Elliot Wave Analysis
              </Link>
            </div>
            <Collapse in={open2}>
                <div id="collapse-text2" >
                    <ul className="list-unstyled border-left p-2 ml-2"
                    >
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                        <li>
                          <Link to={`${url}/supportresistance`}
                                style={{textDecoration: "none"}}
                          >
                            Link
                          </Link>
                        </li>
                    </ul>
                </div>
            </Collapse>
        </Col>
        <Col xs={7} className="border-left">
          Content
        </Col>
        <Col className="border-left">
          SidebarR
        </Col>
      </Row>
    </Container>
)


}

   export default SidebarL

person Yogendra Kumar    schedule 27.07.2020    source источник


Ответы (1)


Этот ответ предполагает, что вы спрашиваете о механизме такого меню, а не о специфике стиля.

Для аккордеона создайте объект конфигурации, представляющий пункты меню. Есть много способов сделать это, но вот пример древовидной структуры, в которой можно разместить подменю любой глубины:

const menu = [
  {
    title: 'Getting Started with Technical Analysis',
    items: [
      {
        title: 'Best ways to learn technical analysis',
        href: `${url}/best-ways-to-learn`,
      },
      {
        title: 'Top 7 Books',
        href: `${url}/top-7-books`,
      },
    ],
  },
  {
    title: 'Essential Technical Analysis Strategies',
    items: [
      {
        title: 'Intro to Strategies',
        href: `${url}/intro-to-strategies`,
      }
    ],
  }
];

Имея эту структуру, вы можете создать компонент рекурсивного пункта меню, который отображает заголовок записи и ее дочерние элементы:

const MenuItem = ({ item: { title, items = [], href } }) => (
  <li>
    <div className='menu-item-title'>{title}</div>
    
    { items.length && (
      <ul className='submenu'>
        { items.map( item => <MenuItem key={item.title} item={item} /> ) }
      </ul>
    )}
  </li>
)

Имея это на месте, вы можете добавить немного состояния компонента и CSS, чтобы развернуть / свернуть подменю:

// adding expanded/collapsed state to MenuItem component from before
const MenuItem = ({ item: { title, items = [], href } }) => {
  const [expanded, setExpanded] = React.useState(false);
  const clickHandler = React.useCallback(() => setExpanded(!expanded), [expanded]);

  return (
    <li onClick={clickHandler} className={expanded ? 'expanded' : 'collapsed'}>
      <div className='menu-item-title'>{title}</div>
        { items.length && (
          <ul className='submenu'>
            { items.map( item => <MenuItem key={item.title} item={item} /> }
          </ul>
        )}
      </div>
    </li>
  );
}

Все остальное вы можете контролировать с помощью CSS.

.collapsed .submenu {
  height: 0;
  overflow: hidden;
}

Если вы хотите, чтобы за раз разворачивался только один элемент, вы можете передать обработчик onChange сверху, который мог бы сворачивать другие элементы при расширении чего-либо.

person ray hatfield    schedule 27.07.2020
comment
Привет, Рэй, спасибо за это очень полезное руководство. У меня уже есть складное меню. Я хочу добавить знак плюс минус к заголовку меню, который изменяется при изменении состояния меню. - person Yogendra Kumar; 28.07.2020
comment
с вашим кодом я получаю сообщение об ошибке TypeError: Cannot read property 'title' of undefined - person Yogendra Kumar; 28.07.2020
comment
Вы должны передать действительное свойство item в MenuItem. - person ray hatfield; 28.07.2020
comment
Плюс / минус можно повесить на состояние expanded. - person ray hatfield; 28.07.2020
comment
Привет, Рэй, я не могу избавиться от вышеуказанной ошибки, не могли бы вы мне помочь? - person Yogendra Kumar; 30.07.2020
comment
Конечно. У вас есть конкретный вопрос? Я могу попробовать превратить приведенный выше пример в рабочую демонстрацию, но сейчас не могу. Может, сегодня позже. - person ray hatfield; 30.07.2020
comment
В приведенных выше примерах было несколько опечаток. Обновлено, чтобы исправить. - person ray hatfield; 30.07.2020
comment
Я собрал рабочую демонстрацию на codeandbox. - person ray hatfield; 30.07.2020
comment
Большое спасибо :) - person Yogendra Kumar; 30.07.2020
comment
См. stackoverflow.com/q/63193406/13558699, все элементы расширяются или сворачиваются вместе - person Yogendra Kumar; 01.08.2020