Slider

Slider

단일 썸으로 값을 선택하는 슬라이더 컴포넌트예요.

출처: 문서

본문

사용법 (Usage)

Slider로 값을 선택해요. color, size, radius, label 관련 prop을 지원해요.

import { Slider } from '@mantine/core';

function Demo() {
  return <Slider defaultValue={50} />;
}

제어 방식 (Controlled)

import { useState } from 'react';
import { Slider } from '@mantine/core';

function Demo() {
  const [value, setValue] = useState(40);
  return <Slider value={value} onChange={setValue} />;
}

비제어 방식 (Uncontrolled)

Slider은 네이티브 인풋 요소와 같은 방식으로 비제어 폼에서 사용할 수 있어요. 폼 제출 시 FormData 객체에 슬라이더 값을 포함하려면 name 속성을 설정해요. 비제어 모드에서 초기 값을 설정하려면 defaultValue prop을 사용해요.

FormData와 함께 비제어 Slider을 사용하는 예시:

import { Slider } from '@mantine/core';

function Demo() {
  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const formData = new FormData(event.currentTarget);
        console.log('Slider value:', formData.get('volume'));
      }}
    >
      <Slider name="volume" />
      <button type="submit">Submit</button>
    </form>
  );
}

비활성 (Disabled)

import { Slider } from '@mantine/core';

function Demo() {
  return <Slider defaultValue={40} disabled />;
}

onChangeEnd

onChangeEnd 콜백은 사용자가 슬라이더 드래그를 멈추거나 키보드로 값을 변경할 때 호출돼요. 너무 빈번한 업데이트를 피하고자 디바운스된 콜백으로 사용할 수 있어요.

import { useState } from 'react';
import { Slider, Text, Box } from '@mantine/core';

function Demo() {
  const [value, setValue] = useState(50);
  const [endValue, setEndValue] = useState(50);

  return (
    <Box>
      <Slider value={value} onChange={setValue} onChangeEnd={setEndValue} />
      <Text>onChange value: {value}</Text>
      <Text>onChangeEnd value: {endValue}</Text>
    </Box>
  );
}

라벨 제어 (Control label)

라벨 동작과 모양을 바꾸려면 다음 prop을 설정해요.

  • label – 포맷터 함수, 값 인자를 받고, 라벨을 비활성화하려면 null 설정, 기본값은 f => f
  • labelAlwaysOn – true면 라벨이 항상 표시되고, 기본적으로는 사용자가 드래그할 때만 보여요
  • labelTransitionProps – Transition 컴포넌트에 전달되는 prop, 라벨 애니메이션을 커스터마이즈하는 데 사용
import { Slider, Text } from '@mantine/core';

function Demo() {
  return (
    <>
      <Text>No label</Text>
      <Slider label={null} defaultValue={40} />

      <Text>Formatted label</Text>
      <Slider label={(value) => `${value} °C`} defaultValue={40} />

      <Text>Label always visible</Text>
      <Slider labelAlwaysOn defaultValue={40} />

      <Text>Custom label transition</Text>
      <Slider labelTransitionProps={{ transition: 'skew-up' }} defaultValue={40} />
    </>
  );
}

min, max와 step

import { Slider, Text } from '@mantine/core';

const marks = [
  { value: 0, label: 'xs' },
  { value: 25, label: 'sm' },
  { value: 50, label: 'md' },
  { value: 75, label: 'lg' },
  { value: 100, label: 'xl' },
];

function Demo() {
  return (
    <>
      <Text>Decimal step</Text>
      <Slider min={0} max={1} label={(value) => value.toFixed(1)} step={0.1} styles={{ markLabel: { display: 'none' } }} />

      <Text>Step matched with marks</Text>
      <Slider label={(val) => marks.find((mark) => mark.value === val)!.label} step={25} marks={marks} styles={{ markLabel: { display: 'none' } }} />
    </>
  );
}

도메인 (Domain)

기본적으로 min과 max prop이 시각적 범위(트랙 표시)와 선택 가능한 범위(가능한 값)를 모두 정의해요. domain prop으로 선택 가능한 범위를 독립적으로 제어할 수 있어요. (컨텍스트를 위해) 더 넓은 트랙을 표시하면서 실제 선택을 일부로 제한하고 싶을 때 유용해요.

아래 예시에서 트랙은 0100(min/max)을 표시하지만 썸은 2080(domain) 사이에서만 드래그할 수 있어요.

import { Slider } from '@mantine/core';

function Demo() {
  return <Slider min={0} max={100} domain={[20, 80]} defaultValue={50} />;
}

소수 값 (Decimal values)

Slider을 소수 값과 함께 사용하려면 min, max, step prop을 설정해요.

import { Slider } from '@mantine/core';

function Demo() {
  return <Slider min={0} max={1} step={0.1} defaultValue={0.4} />;
}

마크 (Marks)

marks prop을 객체 배열로 설정하면 슬라이더에 원하는 수의 마크를 추가할 수 있어요.

const marks = [
  { value: 20 }, // -> 슬라이더 트랙에 마크 표시
  { value: 40, label: '40%' }, // -> 슬라이더 트랙 아래에 마크 라벨 추가
];

마크 값은 너비가 아니라 슬라이더 값에 상대적이에요.

import { Slider } from '@mantine/core';

function Demo() {
  return (
    <Slider
      defaultValue={50}
      marks={[
        { value: 20, label: '20%' },
        { value: 50, label: '50%' },
        { value: 80, label: '80%' },
      ]}
    />
  );
}

마크로 선택 제한 (Restrict selection to marks)

restrictToMarks prop으로 슬라이더 값을 마크로만 제한할 수 있어요. 이 경우 step prop은 무시돼요.

import { Slider } from '@mantine/core';

function Demo() {
  return (
    <Slider defaultValue={50} restrictToMarks marks={[{ value: 0 }, { value: 25 }, { value: 50 }, { value: 75 }, { value: 100 }]} />
  );
}

썸 크기 (Thumb size)

thumbSize prop으로 썸 크기를 설정할 수 있어요.

import { Slider } from '@mantine/core';

function Demo() {
  return <Slider defaultValue={40} thumbSize={26} />;
}

썸 children (Thumb children)

썸 안에 아이콘 같은 React 노드를 넣을 수 있어요.

import { Slider, RangeSlider } from '@mantine/core';
import { HeartIcon, HeartBreakIcon } from '@phosphor-icons/react';

function Demo() {
  return (
    <>
      <Slider
        color="red"
        label={null}
        defaultValue={40}
        thumbSize={26}
        thumbChildren={<HeartIcon size={14} />}
        styles={{ thumb: { borderWidth: 2, padding: 3 } }}
      />
      <RangeSlider
        color="red"
        label={null}
        defaultValue={[25, 75]}
        thumbSize={26}
        thumbChildren={[<HeartBreakIcon key={1} size={14} />, <HeartIcon key={2} size={14} />]}
      />
    </>
  );
}

스케일 (Scale)

scale prop으로 값을 다른 스케일로 표현할 수 있어요. 아래 데모에서 값 x는 2^x를 나타내요. x를 1만큼 올리면 표현된 값이 x의 2제곱만큼 커져요.

import { RangeSlider, Slider } from '@mantine/core';

function valueLabelFormat(value: number) {
  const units = ['KB', 'MB', 'GB', 'TB'];

  let unitIndex = 0;
  let scaledValue = value;

  while (scaledValue >= 1024 && unitIndex < units.length - 1) {
    unitIndex += 1;
    scaledValue /= 1024;
  }

  return `${scaledValue} ${units[unitIndex]}`;
}

function Demo() {
  return (
    <>
      <Slider min={0} max={10} step={0.01} label={valueLabelFormat} scale={(v) => 2 ** v} defaultValue={250} />
    </>
  );
}

시작점 (Start point)

startPointValue prop으로 채워진 막대의 원점을 변경할 수 있어요. 설정하면 막대가 주어진 값에서 현재 값 쪽으로 뻗어요. 시작점보다 낮은 값에서는 왼쪽으로, 높은 값에서는 오른쪽으로 뻗어요. inverted가 설정되면 이 prop은 무시돼요.

import { Slider } from '@mantine/core';

function Demo() {
  return <Slider min={-100} max={100} startPointValue={0} defaultValue={50} />;
}

뒤집기 (Inverted)

inverted prop으로 트랙을 뒤집을 수 있어요.

import { Slider } from '@mantine/core';

function Demo() {
  return <Slider inverted defaultValue={40} />;
}

Styles API

Slider은 Styles API를 지원해요. classNames prop으로 컴포넌트의 내부 요소에 스타일을 추가할 수 있어요.

Styles API 셀렉터:

  • root – 루트 요소
  • label – 썸 라벨
  • thumb – 썸 요소
  • trackContainer – 트랙 요소를 감싸는 요소
  • track – 슬라이더 트랙
  • bar – 트랙의 채워진 부분
  • markWrapper – mark와 markLabel 요소를 포함
  • mark – 트랙에 표시되는 마크
  • markLabel – 연관 마크의 라벨, 트랙 아래에 표시

세로 슬라이더 (Vertical slider)

orientation="vertical"로 설정하면 슬라이더를 세로로 렌더링해요. 세로 방향에서는 최소값이 아래, 최대값이 위에 있어요.

import { RangeSlider, Slider } from '@mantine/core';

const marks = [
  { value: 20, label: '20%' },
  { value: 50, label: '50%' },
  { value: 80, label: '80%' },
];

function Demo() {
  return (
    <>
      <Slider orientation="vertical" marks={marks} defaultValue={50} />
      <RangeSlider orientation="vertical" marks={marks} defaultValue={[20, 80]} />
    </>
  );
}

숨겨진 마크 (Hidden marks)

숨겨진 마크를 사용하면 트랙에 시각적으로 표시하지 않고 특정 값에 스냅할 수 있어요. 사용자에게 보여주고 싶지 않은 특정 값에 "끈적이는" 스냅 동작을 만들고 싶을 때 유용해요. 이 기능은 restrictToMarks prop과 함께 사용해요.

import { Slider, Text, Box } from '@mantine/core';
import { useState } from 'react';

function Demo() {
  const [value, setValue] = useState(50);

  return (
    <>
      <Text size="sm">
        Hidden marks allow you to snap to specific values without displaying them visually.
        Current value: {value}
      </Text>
      <Slider value={value} onChange={setValue} restrictToMarks hiddenMarks={[0, 25, 50, 75, 100]} />
    </>
  );
}

커스텀 슬라이더 만들기 (Build custom slider)

Slider 컴포넌트가 요구사항을 충족하지 못하면 use-move 훅으로 커스텀 슬라이더를 만들 수 있어요.

import { useState } from 'react';
import { DotsSixVerticalIcon } from '@phosphor-icons/react';
import { clamp, useMove } from '@mantine/hooks';
import classes from './Demo.module.css';

function Demo() {
  const [value, setValue] = useState(0.3);
  const { ref } = useMove(({ x }) => setValue(clamp(x, 0.1, 0.9)));
  const labelFloating = value > 0.7 || value < 0.3;

  return (
    <div>
      <div ref={ref} />
      {(value * 100).toFixed(0)}
      {((1 - value) * 100).toFixed(0)}
    </div>
  );
}

접근성 (Accessibility)

Slider 컴포넌트는 기본적으로 접근 가능해요.

  • 썸은 포커스 가능해요
  • 사용자가 마우스로 슬라이더를 조작하면 포커스가 슬라이더 트랙으로 이동하고, 화살표를 누르면 썸으로 이동해요
  • 화살표 키로 step만큼 값을 증감할 수 있어요

스크린 리더를 위해 컴포넌트에 라벨을 지정하려면 썸에 라벨을 추가해요.

import { Slider } from '@mantine/core';

function Demo() {
  return <Slider aria-label="Volume" />;
}

scale을 사용하거나 표시 값을 형식화(예: 통화나 백분율)한 경우, 스크린 리더가 읽을 수 있는 값을 제공하려면 thumbValueText를 설정해요. 이는 썸에 aria-valuetext로 렌더링돼요. 함수가 제공되면 스케일된 값을 받아요.

import { Slider } from '@mantine/core';

function Demo() {
  return <Slider scale={(v) => v * 10} thumbValueText={(value) => `$${value}`} />;
}

키보드 상호작용 (Keyboard interactions)

Key Description
ArrowRight/ArrowUp 슬라이더 값을 한 단계 증가
ArrowLeft/ArrowDown 슬라이더 값을 한 단계 감소
Home 슬라이더 값을 min 값으로 설정
End 슬라이더 값을 max 값으로 설정

더 알아보기 (Learn more)