MUI モーダル系コンポーネント忘備録

## 目次
1. 各コンポーネントの特徴と比較
2. Modal
3. Dialog
4. Menu
5. Popover
6. Drawer
7. Tooltip

## 1. 比較


| 特徴 | Modal | Dialog | Menu | Popover | Drawer | Tooltip |
|------|-------|--------|------|---------|---------|---------|
| 背景オーバーレイ | ◯ | ◯ | × | ◯ | ◯ | × |
| スクロールブロック | ◯ | ◯ | × | × | ◯ | × |
| アンカー要素 | 不要 | 不要 | 必要 | 必要 | 不要 | 必要 |
| 表示位置 | 画面中央 | 画面中央 | アンカー基準 | アンカー基準 | 画面端 | アンカー基準 |
| 主な用途 | 重要な操作 | 確認画面 | ナビゲーション | 詳細表示 | サイドバー | ヘルプテキスト |
| キーボード操作 | Escで閉じる | Tab/Esc | 矢印キー | Tab/Esc | Tab/Esc | なし |
| アニメーション | Fade | Fade/Slide | Grow | Grow | Slide | Fade |

## 2. Modal
- 複雑なフォームやコンテンツの表示
- 重要な操作や確認が必要な場合
- ユーザーの作業を意図的に中断させる場合
- 画面中央に表示

❌ Bad
- 軽微な確認での使用する
- 小さな通知やアラート
- 頻繁な表示/非表示の切り替え
- 複数のModalを重ねて表示する

✅ Good
- ユーザー登録フォーム等の複雑なフォームや大量の情報を表示する
- Modalは一度に一つだけ表示する

### 実装例
```tsx
import { useState } from 'react'
import { Modal, Box, Button, Typography } from '@mui/material'

export const SimpleModal = () => {
  const [open, setOpen] = useState(false)

  return (
    <>
      <Button variant="contained" onClick={() => setOpen(true)}>
        モーダルを開く
      </Button>

      <Modal open={open} onClose={() => setOpen(false)}>
        <Box
          sx={{
            position: 'absolute',
            top: '50%',
            left: '50%',
            transform: 'translate(-50%, -50%)',
            bgcolor: 'background.paper',
            width: 400,
            p: 3,
          }}
        >
          <Typography>モーダルのタイトル</Typography>
          <Typography sx={{ mt: 2 }}>内容</Typography>
          <Box sx={{ mt: 3, display: 'flex', justifyContent: 'flex-end' }}>
            <Button onClick={() => setOpen(false)}>閉じる</Button>
          </Box>
        </Box>
      </Modal>
    </>
  )
}
```

## 3. Dialog
- シンプルな確認の際に使用する
- 短いメッセージを表示
- Yes/Noの選択を求める場合に使う
- 画面中央に表示する

❌ Bad
- 複雑なフォームの表示
- 大量のコンテンツの表示
- 長時間の作業が必要な操作

✅ Good
- 削除の確認
- 簡単な警告メッセージ
- 単純な選択肢の提示

### 実装例
```tsx
import { useState } from 'react'
import {
  Dialog,
  DialogTitle,
  DialogContent,
  DialogContentText,
  DialogActions,
  Button,
  Box,
  Typography,
} from '@mui/material'

export const SimpleDialog = () => {
  const [open, setOpen] = useState(false)

  const handleConfirm = () => {
    // 削除するを押した時の処理
    setOpen(false)
  }

  return (
    <>
      <Button variant="contained" color="error" onClick={() => setOpen(true)}>
        アカウントを削除
      </Button>

      <Dialog open={open} onClose={() => setOpen(false)} fullWidth>
        <DialogTitle>
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
            <Typography>アカウントの削除確認</Typography>
          </Box>
        </DialogTitle>

        <DialogContent>
          <DialogContentText>
            アカウントを削除すると、すべてのデータが完全に削除され、復元することはできません。
            この操作は取り消すことができません。
          </DialogContentText>
        </DialogContent>

        <DialogActions sx={{ p: 2 }}>
          <Button onClick={() => setOpen(false)} color="inherit">
            キャンセル
          </Button>
          <Button
            onClick={handleConfirm}
            variant="contained"
            color="error"
            autoFocus
          >
            削除する
          </Button>
        </DialogActions>
      </Dialog>
    </>
  )
}
```

## 4. Menu
- シンプルなアクション一覧
- 単一選択の操作

❌ Bad
- 大量のコンテンツをMenuに詰め込む
- フォーム要素をMenuに配置
- 深いネストの階層構造

✅ Good
- シンプルな選択肢を表示する
- 大きなコンテンツはModalを使用
- フォームはPopoverやModalを使用

### 実装例
```tsx
import { useState } from 'react'
import {
  Menu,
  MenuItem,
  Button,
  ListItemIcon,
  ListItemText,
} from '@mui/material'
import {
  Settings as SettingsIcon,
  Person as PersonIcon,
  Logout as LogoutIcon,
} from '@mui/icons-material'

export const SimpleMenu = () => {
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)

  return (
    <>
      <Button variant="contained" onClick={(e) => setAnchorEl(e.currentTarget)}>
        メニューを開く
      </Button>

      <Menu
        anchorEl={anchorEl}
        open={Boolean(anchorEl)}
        onClose={() => setAnchorEl(null)}
      >
        <MenuItem onClick={() => setAnchorEl(null)}>
          <ListItemIcon>
            <PersonIcon fontSize="small" />
          </ListItemIcon>
          <ListItemText>プロフィール</ListItemText>
        </MenuItem>
        <MenuItem onClick={() => setAnchorEl(null)}>
          <ListItemIcon>
            <SettingsIcon fontSize="small" />
          </ListItemIcon>
          <ListItemText>設定</ListItemText>
        </MenuItem>
        <MenuItem onClick={() => setAnchorEl(null)}>
          <ListItemIcon>
            <LogoutIcon fontSize="small" />
          </ListItemIcon>
          <ListItemText>ログアウト</ListItemText>
        </MenuItem>
      </Menu>
    </>
  )
}

```



## 5. Popover
- 表示位置を指定する
- カスタマイズ可能なレイアウト
- Menuと比べて柔軟なコンテンツ配置ができる

❌ Bad
- 重要な操作や確認に使う
- 大量のコンテンツを詰め込む
- スクロールが必要なほどの長いコンテンツ

✅ Good
- 重要な操作はModal/Dialogを使用する
- コンテンツは必要最小限にする
- 長いコンテンツはDrawerやModalを使用する


### 実装例
```tsx
import { useState } from 'react'
import {
  Popover,
  Button,
  Typography,
  Box,
  IconButton,
  List,
  ListItem,
  ListItemText,
  Divider,
} from '@mui/material'
import { Close as CloseIcon } from '@mui/icons-material'

export const SimplePopover = () => {
  const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null)

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget)
  }

  const handleClose = () => {
    setAnchorEl(null)
  }

  const open = Boolean(anchorEl)
  const id = open ? 'simple-popover' : undefined

  return (
    <>
      <Button variant="contained" onClick={handleClick}>
        詳細を表示
      </Button>
      <Popover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'left',
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 'left',
        }}
      >
        <Box sx={{ width: 300, p: 2 }}>
          <Box
            sx={{
              display: 'flex',
              justifyContent: 'space-between',
              alignItems: 'center',
              mb: 1,
            }}
          >
            <Typography variant="h6">詳細情報</Typography>
            <IconButton size="small" onClick={handleClose}>
              <CloseIcon />
            </IconButton>
          </Box>
          <Divider sx={{ mb: 2 }} />
          <List dense>
            <ListItem>
              <ListItemText primary="作成日" secondary="YYYY/MM/DD" />
            </ListItem>
            <ListItem>
              <ListItemText primary="ステータス" secondary="処理中" />
            </ListItem>
          </List>
        </Box>
      </Popover>
    </>
  )
}
```

## 6. Drawer
- 画面端からスライドイン
- 大量のコンテンツを表示できる
- サイドバーやナビゲーションに最適
- 画面の一部を占有しながら、メインコンテンツも表示可能

❌ Bad
- 重要な確認ダイアログをDrawerで実装
- 画面全体を覆うコンテンツ
- 短いメニュー

✅ Good
- 確認にはModal/Dialogを使用
- コンテンツ幅を適切に指定する
- 設定項目やフォームを表示
- 複数の項目を持つサイドバー

### 実装例
```tsx
import { useState } from 'react'
import {
  Drawer,
  List,
  ListItem,
  ListItemButton,
  ListItemIcon,
  ListItemText,
  IconButton,
  Box,
  Typography,
  Divider,
} from '@mui/material'
import {
  Menu as MenuIcon,
  Home as HomeIcon,
  Person as PersonIcon,
  Settings as SettingsIcon,
  Mail as MailIcon,
  Help as HelpIcon,
} from '@mui/icons-material'

export const SimpleDrawer = () => {
  const [open, setOpen] = useState(false)

  const menuItems = [
    { text: 'ホーム', icon: <HomeIcon />, path: '/' },
    { text: 'プロフィール', icon: <PersonIcon />, path: '/profile' },
    { text: 'メッセージ', icon: <MailIcon />, path: '/messages' },
    { text: '設定', icon: <SettingsIcon />, path: '/settings' },
    { text: 'ヘルプ', icon: <HelpIcon />, path: '/help' },
  ]

  return (
    <>
      <IconButton edge="start" color="inherit" onClick={() => setOpen(true)}>
        <MenuIcon />
      </IconButton>

      <Drawer anchor="left" open={open} onClose={() => setOpen(false)}>
        <Box sx={{ width: 280 }}>
          <Box sx={{ p: 2 }}>
            <Typography variant="h6">サイドメニュー</Typography>
          </Box>
          <Divider />
          <List>
            {menuItems.map((item) => (
              <ListItem key={item.text} disablePadding>
                <ListItemButton onClick={() => setOpen(false)}>
                  <ListItemIcon>{item.icon}</ListItemIcon>
                  <ListItemText primary={item.text} />
                </ListItemButton>
              </ListItem>
            ))}
          </List>
        </Box>
      </Drawer>
    </>
  )
}
```

## 7. Tooltip
- ホバーやフォーカスで一時的に表示
- シンプルなヘルプテキスト
- 簡潔な補足情報の表示(アイコンボタンの説明等)

❌ Bad
- 長文を入れる
- 重要な情報の表示
- インタラクティブな要素を入れる

✅ Good
- 長文にはPopperを使用する
- インタラクティブな要素にはMenuやModalを使用する


### 実装例
```tsx
import { Box, IconButton, Tooltip } from '@mui/material'
import {
  Edit as EditIcon,
  Delete as DeleteIcon,
  Share as ShareIcon,
} from '@mui/icons-material'

export const SimpleTooltip = () => {
  return (
    <Box sx={{ display: 'flex', gap: 1 }}>
      <Tooltip title="編集する" arrow placement="top">
        <IconButton>
          <EditIcon />
        </IconButton>
      </Tooltip>

      <Tooltip title="共有する" arrow placement="top">
        <IconButton>
          <ShareIcon />
        </IconButton>
      </Tooltip>

      <Tooltip title="削除する" arrow placement="top">
        <IconButton color="error">
          <DeleteIcon />
        </IconButton>
      </Tooltip>
    </Box>
  )
}

```