Подменю неправильно закрывается в интерфейсе React + Material - PullRequest
3 голосов
/ 24 мая 2019

У меня есть вложенная структура меню, такая как:

 menuItems = [
  {
    "key": "key1",
    "caption": "Text1"
  },
  {
    "key": "key2",
    "caption": "Text2",
    "subMenuItems": [
      {
        "key": "key3",
        "caption": "Text3",
        "subMenuItems": [
          {
            "key": "key4",
            "caption": "Text4"
          }
        ]
      },
      {
        "key": "key5",
        "caption": "Text5",
        "subMenuItems": []
      },
      {
        "key": "key6",
        "caption": "Text6"
      }
    ]
  },
  {
    "key": "key7",
    "caption": "Text7"
  }
]

И я хочу открыть элементы подменю в подменю, которое открывается справа от его родителя. Нечто похожее на Меню угловых материалов (пример вложенного меню) .

Я пытался использовать Material UI Menu, и оно открывается по необходимости ( См. Здесь ), но имеет следующие 2 проблемы:

  1. Если вы откроете родительское меню, а затем любое из дочерних меню, вам нужно будет щелкнуть снаружи столько раз, сколько меню. Вместо этого он должен закрывать все родительские + дочерние меню при щелчке снаружи.
  2. Если вы хотите переключить дочернее меню, щелкнув по другому элементу родительского меню, вам необходимо сначала щелкнуть родительское меню (или, как указано в выпуске-1, щелкнуть снаружи), чтобы закрыть открытое в данный момент дочернее меню, а затем щелкнуть по нужному родительскому меню. пункт меню, чтобы открыть соответствующее дочернее меню.

Проблема 1 может быть решена с помощью ClickAwayListener , как реализовано здесь , но затем оно закрывает все меню, даже когда пытается переключиться на другое дочернее меню, например, переключение из подменю «Кнопка 3» «Больше предметов».

Спасибо.

1 Ответ

1 голос
/ 31 мая 2019

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

const items = [
     {name: 'Shallow Item', path='/', type:'shallow'},
     {
         name: 'Nested',
         type: 'nested',
         items:[
             {name: 'Shallow again', path='/nestedpath'}
         ]
     }
]

Теперь у нас есть два элемента в вашем списке, один из которых является простым мелким элементом, который не содержит вложенных опций, а второй является вложенным элементом, который содержит список мелких элементов.

Теперь нам нужно построить или два основных компонента: <NestedItem> и <ShallowItem>. Они должны выглядеть примерно так:

const ShallowItem = ({item, onClick}) =>(
    <ListItem 
        component={Link} 
        to={item.path || #}
        onClick={onClick ? onClick : ()=>{} }
    >
        <ListItemText primary={item.name}/>
        {Boolean(nested) &&
            <Icon>
                {collapsed ? 'arrow_drop_up' : 'arrow_drop_down'}
            </Icon>
        }
    </ListItem>
)

Обратите внимание, что свойства onClick и to (реакция-маршрутизатор) имеют свои значения, условно приписанные, потому что мы собираемся использовать один и тот же компонент для рендеринга первого слоя и списка NestedItem, как это:

const NestedItem = ({ item }) => {
    const [collapsed, toggler] = useState(false)
    const classes = useStyles()
    return (
        <div>
            <ShallowItem
                item={{ icon: item.icon, title: item.title }}
                nested
                collapsed={collapsed}
                onClick={() => toggler(!collapsed)}
            />
            <Collapse in={collapsed}>
                <List component='div'>
                    {item.items.map(item => (
                        <ShallowItem item={item} key={item.title}/>
                    ))}
                </List>
            </Collapse>

        </div>

)}

Ну вот, вы схватили идею? Единственный действительно визуализируемый компонент - это ShallowItem, потому что NestedItem - это просто набор мелких предметов. Поэтому мы сначала визуализируем неглубокий элемент, который открывает контейнер свертывания, содержащий подсписок более мелких элементов. Единственное, что нам нужно сделать сейчас, это проверить, есть ли у элемента подэлементы, регистр положительный, NestedItem, в противном случае - ShallowItem. Эта функция:

const renderItems = items =>{
    for(let item of items){
        switch(item.type){
            case 'nested': return <NestedItem item={item} />

            case 'shallow' : return <ShallowItem item={item} />
        }
    }
}

Теперь у вас есть рекурсивный способ рендеринга вложенных элементов, вам нужно только стилизовать один компонент, и сделать глубину теперь довольно легко. Мы используем это решение в нашей компании, и оно работает как шарм.

...