Material UI in React [ Day 16 ] Navigation Menu (下拉框)

Menu

这个套件应用的范围很广,之前讲解过的 Select 也是用这里的 MenuItem 来替换原本的 option,官方文件

Simple Menu

预设的情况下是从点击位子的 element 上方开启选单,可以透过 anchorOrigin 属性修改开启选单的位置,当靠近萤幕边缘时,选单会垂直重新对齐以确保所有选项都完全可见。
理想情况下,选择一个选项应该立即提交该选项并关闭选单。
因为不用像对话框那样,影响整个页面来显示资讯,所以交互性较佳,用途也比较广泛:

// in export function
const [anchorEl, setAnchorEl] = React.useState(null);

const handleClick = (event) => {
  setAnchorEl(event.currentTarget);
};

const handleClose = () => {
  setAnchorEl(null);
};
// in return
<Button aria-controls="simple-menu" aria-haspopup="true" onClick={handleClick}>
  Open Menu
</Button>
<Menu
  id="simple-menu"
  anchorEl={anchorEl}
  keepMounted
  open={Boolean(anchorEl)}
  onClose={handleClose}
>
  <MenuItem onClick={handleClose}>Profile</MenuItem>
  <MenuItem onClick={handleClose}>My account</MenuItem>
  <MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu>

Selected Menus

如果用於项目选择,打开时,一般选单会尝试将当前选中的选项与锚点垂直对齐,预设焦点将放在选中的选项上。
范例中选择的选项是使用 selected 属性(ListItem)设置的。
要使用选定的选项而不影响选单的预设焦点或垂直定位,请将 variant 属性设置为 menu。

// 先定义选项
const options = [
  '去冰',
  '微冰',
  '少冰',
  '正常',
];
// in export function
const [anchorEl, setAnchorEl] = React.useState(null);
// 设定options的对应位置
const [selectedIndex, setSelectedIndex] = React.useState(1);

const handleClickListItem = (event) => {
  setAnchorEl(event.currentTarget);
};

const handleMenuItemClick = (event, index) => {
  setSelectedIndex(index);
  setAnchorEl(null);
};

const handleClose = () => {
  setAnchorEl(null);
};
<div className={classes.root}>
  <List component="nav" aria-label="Device settings">
    <ListItem
      button
      aria-haspopup="true"
      aria-controls="lock-menu"
      aria-label="手摇饮料冰度"
      onClick={handleClickListItem}
    >
      <ListItemText
        primary="手摇饮料冰度"
        secondary={options[selectedIndex]}
      />
    </ListItem>
  </List>
  <Menu
    id="lock-menu"
    anchorEl={anchorEl}
    keepMounted
    open={Boolean(anchorEl)}
    onClose={handleClose}
  >
    {options.map((option, index) => (
      <MenuItem
        key={option}
        disabled={index === 0}
        selected={index === selectedIndex}
        onClick={(event) => handleMenuItemClick(event, index)}
      >
        {option}
      </MenuItem>
    ))}
  </Menu>
</div>

MenuList Composition

Menu 组件在内部使用 Popover 组件,但是,您可能希望使用不同的定位策略,或者不阻止滚动,为了满足这些需求,官网公开了一个您可以组合的 MenuList 组件,在此示例中使用 Popper。

// in export function
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);

const handleToggle = () => {
  setOpen((prevOpen) => !prevOpen);
};

const handleClose = (event) => {
  if (anchorRef.current && anchorRef.current.contains(event.target)) {
    return;
  }
   setOpen(false);
};

function handleListKeyDown(event) {
  if (event.key === 'Tab') {
    event.preventDefault();
    setOpen(false);
  }
};

const prevOpen = React.useRef(open);
React.useEffect(() => {
  if (prevOpen.current === true && open === false) {
    anchorRef.current.focus();
  }
  prevOpen.current = open;
}, [open]);
// in return
<div className={classes.root}>
  <Paper className={classes.paper}>
    <MenuList>
      <MenuItem>Profile</MenuItem>
      <MenuItem>My account</MenuItem>
      <MenuItem>Logout</MenuItem>
    </MenuList>
  </Paper>
  <div>
    <Button
      ref={anchorRef}
      aria-controls={open ? 'menu-list-grow' : undefined}
      aria-haspopup="true"
      onClick={handleToggle}
    >
      Toggle Menu Grow
    </Button>
    <Popper
      open={open}
      anchorEl={anchorRef.current}
      role={undefined}
      transition
      disablePortal
    >
      {({ TransitionProps, placement }) => (
        <Grow
          {...TransitionProps}
          style={
            { transformOrigin: placement === 'bottom'
              ? 'center top' 
              : 'center bottom'
            }
          }
        >
          <Paper>
            <ClickAwayListener onClickAway={handleClose}>
              <MenuList
                autoFocusItem={open}
                id="menu-list-grow"
                onKeyDown={handleListKeyDown}
              >
                <MenuItem onClick={handleClose}>Profile</MenuItem>
                <MenuItem onClick={handleClose}>My account</MenuItem>
                <MenuItem onClick={handleClose}>Logout</MenuItem>
              </MenuList>
            </ClickAwayListener>
          </Paper>
        </Grow>
      )}
    </Popper>
  </div>
</div>

Max Height Manus

如果选单的高度阻止显示所有选项,则选单可以在内部滚动。

// 设一组较多的选项组
const options = [
  'None',
  'Atria',
  'Callisto',
  'Dione',
  'Ganymede',
  'Hangouts Call',
  'Luna',
  'Oberon',
  'Phobos',
  'Pyxis',
  'Sedna',
  'Titania',
  'Triton',
  'Umbriel',
];
// in export function
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);

const handleClick = (event) => {
  setAnchorEl(event.currentTarget);
};

const handleClose = () => {
  setAnchorEl(null);
};
// in return
<div>
  <IconButton
    aria-label="more"
    aria-controls="long-menu"
    aria-haspopup="true"
    onClick={handleClick}
  >
    <MoreVertIcon />
  </IconButton>
  <Menu
    id="long-menu"
    anchorEl={anchorEl}
    keepMounted
    open={open}
    onClose={handleClose}
    PaperProps={{
      style: {
        maxHeight: 216,
        width: '20ch',
      },
    }}
  >
    {options.map((option) => (
      <MenuItem 
        key={option}
        selected={option === 'Pyxis'}
        onClick={handleClose}
      >
        {option}
      </MenuItem>
    ))}
  </Menu>
</div>

如果选项内容很长的话可以透过 Typography 组件去限制:

<Paper style={{ width: 230 }}>
  <MenuList>
    <MenuItem>
      <ListItemIcon>
        <SendIcon fontSize="small" />
      </ListItemIcon>
      <Typography variant="inherit">A short message</Typography>
    </MenuItem>
    <MenuItem>
      <ListItemIcon>
        <PriorityHighIcon fontSize="small" />
      </ListItemIcon>
      <Typography variant="inherit">A very long text that overflows</Typography>
    </MenuItem>
    <MenuItem>
      <ListItemIcon>
        <DraftsIcon fontSize="small" />
      </ListItemIcon>
      <Typography variant="inherit" noWrap>
        A very long text that overflows
      </Typography>
    </MenuItem>
  </MenuList>
</Paper>

那麽今天的内容就讲解到这里,比较进阶和自订话的方法各位可以去官方文件查询,明天会讲解 AppBar 组件。


<<:  【第十七天 - 动态规划 题目分析】

>>:  [DAY 12] 依选项前往区段

Day 06 : 资料处理 Pandas (2)

今天接着介绍 pandas 如何表对资料表合并、资料汇总等等进阶用法! 资料表合并 inner jo...

Day 22 资料宝石:【Lab】RDS架构 建立自己的第一台云端资料库 (中)

今天我们接续 RDS Lab 实作。 创建第一台 RDS instance 按下左边列表的 Dat...

[NestJS 带你飞!] DAY13 - Guard

什麽是 Guard? Guard 是一种检测机制,就像公司的保全系统,需要使用门禁卡才能进入,否则就...

Day-7 带着童年的好朋友任天堂红白机、重新在 HDMI 电视上发光吧!

写了好几天的事前准备、我想大家应该都腻了。终於、准备到了一定程度、可以进入本文了。这篇文章主要的目的...

Day7 风生水起,观元辰宫的木-2

关於地板 地板的部分,也关系到了墙壁以及布线难易度,往往很多公司在装潢完成之後,才发现东缺西缺,到之...