Speed Dial

Speed Dial (스피드 다이얼)

플로팅 액션 버튼을 눌렀을 때 3~6개의 관련 동작을 Speed Dial 형태로 표시하는 컴포넌트에 대해 알아봅니다.

출처: 문서

본문

플로팅 액션 버튼이 눌리면 3~6개의 관련 동작을 Speed Dial 형태로 표시할 수 있어요.

6개 이상의 동작이 필요하다면, FAB가 아닌 다른 것을 사용해 그것들을 제시해야 합니다.

기본 speed dial (Basic speed dial)

플로팅 액션 버튼은 관련 동작들을 표시할 수 있어요.

import Box from '@mui/material/Box';
import SpeedDial from '@mui/material/SpeedDial';
import SpeedDialIcon from '@mui/material/SpeedDialIcon';
import SpeedDialAction from '@mui/material/SpeedDialAction';
import FileCopyIcon from '@mui/icons-material/FileCopyOutlined';
import SaveIcon from '@mui/icons-material/Save';
import PrintIcon from '@mui/icons-material/Print';
import ShareIcon from '@mui/icons-material/Share';

const actions = [
  { icon: <FileCopyIcon />, name: 'Copy' },
  { icon: <SaveIcon />, name: 'Save' },
  { icon: <PrintIcon />, name: 'Print' },
  { icon: <ShareIcon />, name: 'Share' },
];

export default function BasicSpeedDial() {
  return (
    <Box sx={{ height: 320, transform: 'translateZ(0px)', flexGrow: 1 }}>
      <SpeedDial
        ariaLabel="SpeedDial basic example"
        sx={{ position: 'absolute', bottom: 16, right: 16 }}
        icon={<SpeedDialIcon />}
      >
        {actions.map((action) => (
          <SpeedDialAction
            key={action.name}
            icon={action.icon}
            slotProps={{
              tooltip: {
                title: action.name,
              },
            }}
          />
        ))}
      </SpeedDial>
    </Box>
  );
}

플레이그라운드 (Playground)

import * as React from 'react';
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
import FormControl from '@mui/material/FormControl';
import FormControlLabel from '@mui/material/FormControlLabel';
import FormLabel from '@mui/material/FormLabel';
import Radio from '@mui/material/Radio';
import RadioGroup from '@mui/material/RadioGroup';
import Switch from '@mui/material/Switch';
import SpeedDial, { SpeedDialProps } from '@mui/material/SpeedDial';
import SpeedDialIcon from '@mui/material/SpeedDialIcon';
import SpeedDialAction from '@mui/material/SpeedDialAction';
import FileCopyIcon from '@mui/icons-material/FileCopyOutlined';
import SaveIcon from '@mui/icons-material/Save';
import PrintIcon from '@mui/icons-material/Print';
import ShareIcon from '@mui/icons-material/Share';

const StyledSpeedDial = styled(SpeedDial)(({ theme }) => ({
  position: 'absolute',
  '&.MuiSpeedDial-directionUp, &.MuiSpeedDial-directionLeft': {
    bottom: theme.spacing(2),
    right: theme.spacing(2),
  },
  '&.MuiSpeedDial-directionDown, &.MuiSpeedDial-directionRight': {
    top: theme.spacing(2),
    left: theme.spacing(2),
  },
}));

const actions = [
  { icon: <FileCopyIcon />, name: 'Copy' },
  { icon: <SaveIcon />, name: 'Save' },
  { icon: <PrintIcon />, name: 'Print' },
  { icon: <ShareIcon />, name: 'Share' },
];

export default function PlaygroundSpeedDial() {
  const [direction, setDirection] =
    React.useState<SpeedDialProps['direction']>('up');
  const [hidden, setHidden] = React.useState(false);

  const handleDirectionChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setDirection(
      (event.target as HTMLInputElement).value as SpeedDialProps['direction'],
    );
  };

  const handleHiddenChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setHidden(event.target.checked);
  };

  return (
    <Box sx={{ transform: 'translateZ(0px)', flexGrow: 1 }}>
      <FormControlLabel
        control={
          <Switch checked={hidden} onChange={handleHiddenChange} color="primary" />
        }
        label="Hidden"
      />
      <FormControl component="fieldset" sx={{ mt: 1, display: 'flex' }}>
        <FormLabel component="legend">Direction</FormLabel>
        <RadioGroup
          aria-label="direction"
          name="direction"
          value={direction}
          onChange={handleDirectionChange}
          row
        >
          <FormControlLabel value="up" control={<Radio />} label="Up" />
          <FormControlLabel value="right" control={<Radio />} label="Right" />
          <FormControlLabel value="down" control={<Radio />} label="Down" />
          <FormControlLabel value="left" control={<Radio />} label="Left" />
        </RadioGroup>
      </FormControl>
      <Box sx={{ position: 'relative', mt: 3, height: 320 }}>
        <StyledSpeedDial
          ariaLabel="SpeedDial playground example"
          hidden={hidden}
          icon={<SpeedDialIcon />}
          direction={direction}
        >
          {actions.map((action) => (
            <SpeedDialAction
              key={action.name}
              icon={action.icon}
              slotProps={{
                tooltip: {
                  title: action.name,
                },
              }}
            />
          ))}
        </StyledSpeedDial>
      </Box>
    </Box>
  );
}

제어되는 speed dial (Controlled speed dial)

컴포넌트의 열림 상태는 open/onOpen/onClose props로 제어할 수 있어요.

import * as React from 'react';
import Box from '@mui/material/Box';
import SpeedDial from '@mui/material/SpeedDial';
import SpeedDialIcon from '@mui/material/SpeedDialIcon';
import SpeedDialAction from '@mui/material/SpeedDialAction';
import FileCopyIcon from '@mui/icons-material/FileCopyOutlined';
import SaveIcon from '@mui/icons-material/Save';
import PrintIcon from '@mui/icons-material/Print';
import ShareIcon from '@mui/icons-material/Share';

const actions = [
  { icon: <FileCopyIcon />, name: 'Copy' },
  { icon: <SaveIcon />, name: 'Save' },
  { icon: <PrintIcon />, name: 'Print' },
  { icon: <ShareIcon />, name: 'Share' },
];

export default function ControlledOpenSpeedDial() {
  const [open, setOpen] = React.useState(false);
  const handleOpen = () => setOpen(true);
  const handleClose = () => setOpen(false);

  return (
    <Box sx={{ height: 320, transform: 'translateZ(0px)', flexGrow: 1 }}>
      <SpeedDial
        ariaLabel="SpeedDial controlled open example"
        sx={{ position: 'absolute', bottom: 16, right: 16 }}
        icon={<SpeedDialIcon />}
        onClose={handleClose}
        onOpen={handleOpen}
        open={open}
      >
        {actions.map((action) => (
          <SpeedDialAction
            key={action.name}
            icon={action.icon}
            slotProps={{
              tooltip: {
                title: action.name,
              },
            }}
            onClick={handleClose}
          />
        ))}
      </SpeedDial>
    </Box>
  );
}

커스텀 닫기 아이콘 (Custom close icon)

SpeedDialIcon 컴포넌트의 icon과 openIcon props로 닫힘/열림 상태에 대한 대체 아이콘을 제공할 수 있어요.

import Box from '@mui/material/Box';
import SpeedDial from '@mui/material/SpeedDial';
import SpeedDialIcon from '@mui/material/SpeedDialIcon';
import SpeedDialAction from '@mui/material/SpeedDialAction';
import FileCopyIcon from '@mui/icons-material/FileCopyOutlined';
import SaveIcon from '@mui/icons-material/Save';
import PrintIcon from '@mui/icons-material/Print';
import ShareIcon from '@mui/icons-material/Share';
import EditIcon from '@mui/icons-material/Edit';

const actions = [
  { icon: <FileCopyIcon />, name: 'Copy' },
  { icon: <SaveIcon />, name: 'Save' },
  { icon: <PrintIcon />, name: 'Print' },
  { icon: <ShareIcon />, name: 'Share' },
];

export default function OpenIconSpeedDial() {
  return (
    <Box sx={{ height: 320, transform: 'translateZ(0px)', flexGrow: 1 }}>
      <SpeedDial
        ariaLabel="SpeedDial openIcon example"
        sx={{ position: 'absolute', bottom: 16, right: 16 }}
        icon={<SpeedDialIcon openIcon={<EditIcon />} />}
      >
        {actions.map((action) => (
          <SpeedDialAction
            key={action.name}
            icon={action.icon}
            slotProps={{
              tooltip: {
                title: action.name,
              },
            }}
          />
        ))}
      </SpeedDial>
    </Box>
  );
}

상시 표시 동작 툴팁 (Persistent action tooltips)

SpeedDialAction의 툴팁은 상시 표시되도록 할 수 있어, 터치 기기에서 툴팁을 보기 위해 길게 누를 필요가 없게 해 줍니다.

데모 목적으로 여기서는 모든 기기에서 활성화했지만, 프로덕션에서는 isTouch 로직을 사용해 조건부로 prop을 설정할 수 있을 거예요.

import * as React from 'react';
import Box from '@mui/material/Box';
import Backdrop from '@mui/material/Backdrop';
import SpeedDial from '@mui/material/SpeedDial';
import SpeedDialIcon from '@mui/material/SpeedDialIcon';
import SpeedDialAction from '@mui/material/SpeedDialAction';
import FileCopyIcon from '@mui/icons-material/FileCopyOutlined';
import SaveIcon from '@mui/icons-material/Save';
import PrintIcon from '@mui/icons-material/Print';
import ShareIcon from '@mui/icons-material/Share';

const actions = [
  { icon: <FileCopyIcon />, name: 'Copy' },
  { icon: <SaveIcon />, name: 'Save' },
  { icon: <PrintIcon />, name: 'Print' },
  { icon: <ShareIcon />, name: 'Share' },
];

export default function SpeedDialTooltipOpen() {
  const [open, setOpen] = React.useState(false);
  const handleOpen = () => setOpen(true);
  const handleClose = () => setOpen(false);

  return (
    <Box sx={{ height: 330, transform: 'translateZ(0px)', flexGrow: 1 }}>
      <Backdrop open={open} />
      <SpeedDial
        ariaLabel="SpeedDial tooltip example"
        sx={{ position: 'absolute', bottom: 16, right: 16 }}
        icon={<SpeedDialIcon />}
        onClose={handleClose}
        onOpen={handleOpen}
        open={open}
      >
        {actions.map((action) => (
          <SpeedDialAction
            key={action.name}
            icon={action.icon}
            slotProps={{
              tooltip: {
                open: true,
                title: action.name,
              },
            }}
            onClick={handleClose}
          />
        ))}
      </SpeedDial>
    </Box>
  );
}

전환 (Transitions)

Speed Dial은 기본적으로 Zoom을 사용합니다. 다른 전환 효과로 바꾸거나 전환 관련 props를 넘기고 싶다면 slots.transition과 slotProps.transition을 쓰면 돼요. 타이밍을 제어하려면 transitionDuration을 사용하세요.

접근성 (Accessibility)

ARIA

필수 (Required)
  • speed dial 컴포넌트에 ariaLabel을 제공해야 합니다.
  • 각 speed dial 동작에 slotProps.tooltip.title을 사용해 툴팁 제목을 제공해야 합니다.
제공됨 (Provided)
  • Fab은 aria-haspopup, aria-expanded, aria-controls 속성을 가집니다.
  • speed dial 동작 컨테이너는 direction에 따라 설정된 role="menu"와 aria-orientation을 가집니다.
  • speed dial 동작은 role="menuitem"과, 관련 툴팁을 참조하는 aria-describedby 속성을 가집니다.

키보드 (Keyboard)

  • speed dial은 포커스를 받으면 열립니다.
  • Space와 Enter 키는 선택된 speed dial 동작을 트리거하고, speed dial의 열림 상태를 토글합니다.
  • 커서 키는 포커스를 다음 또는 이전 speed dial 동작으로 이동시킵니다. (어떤 커서 방향이든 speed dial을 여는 데 처음에 사용될 수 있습니다. 이는 speed dial의 실제 또는 지각된 방향에 대한 기대 동작을 가능하게 해 줍니다. 예를 들어 드롭다운 메뉴로 인식하는 스크린 리더 사용자의 경우가 그렇죠.)
  • Escape 키는 speed dial을 닫고, speed dial 동작이 포커스되어 있었다면 포커스를 Fab으로 되돌립니다.

SpeedDial API

Demos

이 React 컴포넌트의 사용 예시와 자세한 내용은 컴포넌트 데모 페이지에서 확인할 수 있어요.

Import

import SpeedDial from '@mui/material/SpeedDial';
// or
import { SpeedDial } from '@mui/material';

Props

Name Type Default Required Description
ariaLabel string - Yes
children node - No
classes object - No Override or extend the styles applied to the component.
direction 'down' | 'left' | 'right' | 'up' 'up' No
FabProps object {} No
hidden bool false No
icon node - No
onClose function(event: object, reason: string) => void - No
onOpen function(event: object, reason: string) => void - No
open bool - No
openIcon node - No
slotProps { root?: func | object, transition?: func | object } {} No
slots { root?: elementType, transition?: elementType } {} No
sx Array<func | object | bool> | func | object - No The system prop that allows defining system overrides as well as additional CSS styles.
transitionDuration number | { appear?: number, enter?: number, exit?: number } `{
enter: theme.transitions.duration.enteringScreen,
exit: theme.transitions.duration.leavingScreen,
}` No

참고: ref는 루트 요소 (HTMLDivElement).

그 외에 제공된 props는 루트 요소 (native element).

Theme default props

MuiSpeedDial을 사용하면 테마에서 이 컴포넌트의 기본 props를 바꿀 수 있어요.

Slots

Name Default Class Description
root 'div' .MuiSpeedDial-root The component that renders the root slot.
transition Zoom - The component that renders the transition.
Follow this guide to learn more about the requirements for this component.

CSS

Rule name

Global class Rule name Description
- actions Styles applied to the actions (children wrapper) element.
- actionsClosed Styles applied to the actions (children wrapper) element if open={false}.
- directionDown Styles applied to the root element if direction="down"
- directionLeft Styles applied to the root element if direction="left"
- directionRight Styles applied to the root element if direction="right"
- directionUp Styles applied to the root element if direction="up"
- fab Styles applied to the Fab component.

Source code

이 페이지에서 원하는 정보를 찾지 못했다면, 더 자세한 내용은 컴포넌트 구현을 살펴보는 것도 좋아요.

SpeedDialAction API

Demos

이 React 컴포넌트의 사용 예시와 자세한 내용은 컴포넌트 데모 페이지에서 확인할 수 있어요.

Import

import SpeedDialAction from '@mui/material/SpeedDialAction';
// or
import { SpeedDialAction } from '@mui/material';

Props

Name Type Default Required Description
classes object - No Override or extend the styles applied to the component.
delay number 0 No
icon node - No
id string - No
open bool - No
slotProps { fab?: func | object, staticTooltip?: func | object, staticTooltipLabel?: func | object, tooltip?: func | object } {} No
slots { fab?: elementType, staticTooltip?: elementType, staticTooltipLabel?: elementType, tooltip?: elementType } {} No
sx Array<func | object | bool> | func | object - No The system prop that allows defining system overrides as well as additional CSS styles.

참고: ref는 루트 요소 (HTMLButtonElement).

그 외에 제공된 props는 루트 요소 (Tooltip).

Inheritance

위에서 명시적으로 다루진 않았지만, Tooltip 컴포넌트의 props도 SpeedDialAction에서 사용할 수 있어요.

Theme default props

MuiSpeedDialAction을 사용하면 테마에서 이 컴포넌트의 기본 props를 바꿀 수 있어요.

Slots

Name Default Class Description
fab Fab .MuiSpeedDialAction-fab The component that renders the fab.
tooltip Tooltip - The component that renders the tooltip.
staticTooltip 'span' .MuiSpeedDialAction-staticTooltip The component that renders the static tooltip.
staticTooltipLabel 'span' .MuiSpeedDialAction-staticTooltipLabel The component that renders the static tooltip label.

CSS

Rule name

Global class Rule name Description
- fabClosed Styles applied to the Fab component if open={false}.
- staticTooltipClosed Styles applied to the root element if tooltipOpen={true} and open={false}.
- tooltipPlacementLeft Styles applied to the root element if tooltipOpen={true} and `tooltipPlacement="left"``
- tooltipPlacementRight Styles applied to the root element if tooltipOpen={true} and `tooltipPlacement="right"``

Source code

이 페이지에서 원하는 정보를 찾지 못했다면, 더 자세한 내용은 컴포넌트 구현을 살펴보는 것도 좋아요.

SpeedDialIcon API

Demos

이 React 컴포넌트의 사용 예시와 자세한 내용은 컴포넌트 데모 페이지에서 확인할 수 있어요.

Import

import SpeedDialIcon from '@mui/material/SpeedDialIcon';
// or
import { SpeedDialIcon } from '@mui/material';

Props

Name Type Default Required Description
classes object - No Override or extend the styles applied to the component.
icon node - No
openIcon node - No
sx Array<func | object | bool> | func | object - No The system prop that allows defining system overrides as well as additional CSS styles.

참고: ref는 루트 요소 (HTMLSpanElement).

그 외에 제공된 props는 루트 요소 (native element).

Theme default props

MuiSpeedDialIcon을 사용하면 테마에서 이 컴포넌트의 기본 props를 바꿀 수 있어요.

CSS

Rule name

Global class Rule name Description
- icon Styles applied to the icon component.
- iconOpen Styles applied to the icon component if open={true}.
- iconWithOpenIconOpen Styles applied to the icon when an openIcon is provided and if open={true}.
- openIcon Styles applied to the openIcon if provided.
- openIconOpen Styles applied to the openIcon if provided and if open={true}.
- root Styles applied to the root element.

Source code

이 페이지에서 원하는 정보를 찾지 못했다면, 더 자세한 내용은 컴포넌트 구현을 살펴보는 것도 좋아요.

더 알아보기 (Learn more)