繁体   English   中英

如何让我的嵌套导航菜单在移动设备上工作?

[英]How can I make my nested navigation menu work on mobile?

我正在使用Base Web 我有一个带有子菜单的菜单。 它基于这个例子。 在桌面上它工作正常。 我打开菜单,在一个选项上输入 hover,然后出现子菜单。 但是,当我在 iPhone 上尝试时,我无法访问子菜单。 点击目标打开主菜单,但点击菜单项关闭主菜单。

我想,要解决这个问题,我必须将显示子菜单的目标事件从 hover 更改为单击。 那是对的吗? 如果是这样,有谁知道我如何使用 Base Web 菜单来做到这一点? 我查看了文档,但没有看到相应的选项。

这里有一个码笔: https://codesandbox.io/s/base-web-menu-focus-issue-forked-czz9kp (注意:要查看问题,请在您的手机上访问 URL。不要使用 codepen 响应视图)。

这是菜单的源代码文件:

//HamburgerMenu.js

import React from 'react'
import { themedUseStyletron as useStyletron } from 'Shared/Theme'
import { StatefulMenu, NestedMenus } from 'baseui/menu'
import { StatefulPopover, PLACEMENT } from 'baseui/popover'
import { HamburgerMenu as MenuIcon } from 'Components/Icons'
import InvisibleButtonWrapper from 'Components/Shared/InvisibleButtonWrapper/InvisibleButtonWrapper'

type MenuItemT =  {
  label: string
  onClick?: () => void
}

type HamburgerMenuProps = {
  items:MenuItemT[]
  placement: 'bottom' | 'auto' | 'topLeft' | 'top' | 'topRight' | 'rightTop' | 'right' | 'rightBottom' | 'bottomRight' | 'bottomLeft' | 'leftBottom' | 'left' | 'leftTop' | undefined
  ariaLabel: string
  id: string
  subItems?: {[key: string]: MenuItemT[]}
  fill: string
}

const HamburgerMenu = ({ items, placement = PLACEMENT.bottom, ariaLabel, id, subItems, fill }: HamburgerMenuProps) => {
  const [, theme] = useStyletron()
  return (
    <StatefulPopover
      autoFocus={false}
      dismissOnClickOutside={false}
      content={({ close }) => (
        <NestedMenus>
          <StatefulMenu
            items={items}
            onItemSelect={({ item }) => {
              typeof item?.onClick === 'function' && item.onClick()
              close()
            }}
            overrides={{
              List: {
                style: ({ $theme }) => ({
                  borderTopLeftRadius: '6px',
                  borderTopRightRadius: '6px',
                  borderBottomLeftRadius: '6px',
                  borderBottomRightRadius: '6px',
                  border: `1px solid ${$theme.colors.backgroundQuaternary}`
                })
              },
              Option: {
                props: {
                  getChildMenu: (item: MenuItemT) => {
                    if (!subItems?.[item.label]) return null
                    return (
                      <StatefulMenu
                        items={subItems[item.label] as MenuItemT[]}
                        overrides={{
                          List: {
                            style: ({ $theme }) => ({
                              borderTopLeftRadius: '6px',
                              borderTopRightRadius: '6px',
                              borderBottomLeftRadius: '6px',
                              borderBottomRightRadius: '6px',
                              border: `1px solid ${$theme.colors.backgroundQuaternary}`,
                              whiteSpace: 'nowrap'
                            })
                          },
                          Option: {
                            style: ({ $theme }) => ({
                              fontFamily: 'Roboto',
                              color: $theme.colors.primary,
                              fontWeight: 'bold'
                            })
                          }
                        }}
                        onItemSelect={({ item }) => {
                          typeof item?.onClick === 'function' && item.onClick()
                          close()
                        }}
                      />
                    )
                  }
                },
                style: ({ $theme }) => ({
                  fontFamily: 'Roboto',
                  color: $theme.colors.primary,
                  fontWeight: 'bold'
                })
              }
            }}
          />
        </NestedMenus>
      )}
      accessibilityType={'tooltip'}
      placement={placement}
    >
      <InvisibleButtonWrapper placement={'left'} >
        <MenuIcon size={24} aria-label={ariaLabel} fill={fill}/>
      </InvisibleButtonWrapper>
    </StatefulPopover>
  )
}

export default HamburgerMenu

在Header.js文件中使用:

// Header.js

import { themedUseStyletron as useStyletron } from 'Shared/Theme'
import { useDispatch, useSelector } from 'react-redux'
import { toggleDisplayCreateBoard } from 'Redux/Reducers/ModalDisplaySlice'
import { switchTheme } from 'Redux/Reducers/ThemeSlice'
import { THEME } from 'Shared/Constants'
import Link from 'next/link'
import { signOut } from 'next-auth/react'
import { useSession } from 'next-auth/react'

import HamburgerMenu from './HamburgerMenu/HamburgerMenu'

export default function Header (props) {
  const [css, theme] = useStyletron()
  const dispatch = useDispatch()
  const boards = useSelector(state => state?.board)
  const { data: session } = useSession()

  const items = [
    {
      label: 'Boards'
    },
    {
      label: 'Create'
    },
    {
      label: 'Theme'
    }
  ]

  items.push(
    {
      label: 'Sign out',
      onClick: async () => {
        await signOut({ callbackUrl: 'http://localhost:8080/signin' })
      }
    }
  )

  const subItems = {
    Boards: boards.map(board => ({
      label: <Link href={{pathname: '/board/[boardId]'}} as={`/board/${board.id}`}><span
        className={css({ color: theme.colors.primary, width: '100%', display: 'block' })}>{board.name}</span></Link>
    }))
    ,
    Create: [{
      label: 'Board',
      onClick: () => {
        dispatch(toggleDisplayCreateBoard())
      }
    }
    ],
    Theme: [
      {
        label: 'Light',
        onClick: () => {
          dispatch(switchTheme({ theme: THEME.light }))
        }
      },
      {
        label: 'Dark',
        onClick: () => {
          dispatch(switchTheme({ theme: THEME.dark }))
        }
      }
    ]
  }

  return (
    <div className={css({
      position: 'relative',
      display: 'flex',
      alignItems: 'center',
      width: '100%',
      height: '50px',
      padding: '0',
      fontFamily: 'bebas neue',
      boxShadow: 'rgba(0, 0, 0, 0.2) 0px 12px 28px 0px, rgba(0, 0, 0, 0.1) 0px 2px 4px 0px, rgba(255, 255, 255, 0.05) 0px 0px 0px 1px inset'
    })}>
      <Link href="/" style={{textDecoration: 'none'}}>
        <h1 className={css({
          cursor: 'pointer',
          fontSize: '32px',
          lineHeight: '32px',
          margin: '0 10px 0 20px',
          padding: 0,
          color: theme.colors.accent,
          borderBottom: '2px solid theme.colors.black',
          ':only-child': {
            margin: '0 0 0 20px'
          }
        })}>Get It</h1>
      </Link>
      {session &&
        <HamburgerMenu
          tabindex={0}
          ariaLabel={'main-menu'}
          items={items}
          subItems={subItems}
          fill={theme.colors.primary}
        />
      }
    </div>
  )
}

提前致谢!

好吧,我终于出来了。 问题是我在onItemSelect处理程序中调用了close()实用程序 function。 当时的想法大致是这样的:

  1. 单击外部菜单项
  2. 如果所述项目有一个onClick处理程序调用它
  3. 现在关闭外部菜单。

这在非触摸屏设备上有意义,因为您将使用 hover 打开嵌套的子菜单,而不是单击。 这是差异的屏幕截图:

在此处输入图像描述

这是用于检测触摸屏设备的助手 function:

export const isTouchScreenDevice = () => {
  try{
    document.createEvent('TouchEvent');
    return true
  }catch(e){
    return false
  }
}

现在我已经做了这个更改,我可以单击主菜单项并打开嵌套菜单。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM