[英]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。 当时的想法大致是这样的:
onClick
处理程序调用它这在非触摸屏设备上有意义,因为您将使用 hover 打开嵌套的子菜单,而不是单击。 这是差异的屏幕截图:
这是用于检测触摸屏设备的助手 function:
export const isTouchScreenDevice = () => {
try{
document.createEvent('TouchEvent');
return true
}catch(e){
return false
}
}
现在我已经做了这个更改,我可以单击主菜单项并打开嵌套菜单。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.