iT邦幫忙

2021 iThome 鐵人賽

DAY 16
0
自我挑戰組

Material UI in React系列 第 16

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 組件。


上一篇
Material UI in React [ Day15 ] Navigation Stepper 步驟卡
下一篇
Material UI in React [ Day 17 ] Surfaces App Bar (應用欄)
系列文
Material UI in React30

尚未有邦友留言

立即登入留言