SegmentedControl

SegmentedControl

둘 이상의 세그먼트로 이루어진 선형 세트를 제공하는 컴포넌트예요. 라디오 그룹 요소를 기반으로 하며, 선택된 항목 위에 떠다니는 인디케이터를 표시해요.

출처: 문서

본문

사용법 (Usage)

SegmentedControl로 둘 이상의 세그먼트를 가지는 선형 세트를 만들어요. orientation(가로/세로), fullWidth, withItemsBorders, size, radius 등의 prop을 지원해요.

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

function Demo() {
  return (
    <SegmentedControl data={['React', 'Angular', 'Vue']} />
  );
}

제어 방식 (Controlled)

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

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

  return <SegmentedControl value={value} onChange={setValue} data={['react', 'angular', 'vue']} />;
}

비제어 방식 (Uncontrolled)

SegmentedControl은 네이티브 인풋 요소와 같은 방식으로 비제어 폼에서 사용할 수 있어요. 폼 제출 시 FormData 객체에 세그먼트 컨트롤 값을 포함하려면 name 속성을 설정해요. 비제어 폼에서 초기 값을 제어하려면 defaultValue prop을 사용해요.

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

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

function Demo() {
  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const formData = new FormData(event.currentTarget);
        console.log('Segmented control value:', formData.get('framework'));
      }}
    >
      <SegmentedControl name="framework" data={['react', 'angular', 'vue']} />
      <button type="submit">Submit</button>
    </form>
  );
}

data prop

SegmentedControl은 두 가지 data 형식을 지원해요.

  • 원시 값의 배열 – value와 label이 같은 때 사용
  • 객체의 배열 – value와 label이 다를 때 사용
import { SegmentedControl } from '@mantine/core';

function ArrayOfStrings() {
  return <SegmentedControl data={['react', 'angular', 'vue']} />;
}

function ArrayOfObjects() {
  return (
    <SegmentedControl
      data={[
        { value: 'react', label: 'React' },
        { value: 'angular', label: 'Angular' },
        { value: 'vue', label: 'Vue' },
      ]}
    />
  );
}

제네릭 값 타입 (Generic value type)

SegmentedControl은 제네릭 값 타입을 지원해요. 타입 인자로 원시 값(숫자, 문자열, boolean, null)을 전달할 수 있어요. 제네릭 타입은 value, defaultValue, onChange, data prop에 사용돼요.

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

function Demo() {
  return (
    <SegmentedControl<number | string>
      data={[
        { value: 16, label: '16' },
        { value: 17, label: '17' },
        { value: '18+', label: '18 or older' },
      ]}
    />
  );
}

문자열 유니온 예시:

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

function Demo() {
  return (
    <SegmentedControl<'orange' | 'grape' | 'apple'>
      data={[
        { value: 'orange', label: 'Orange' },
        { value: 'grape', label: 'Grape' },
        { value: 'apple', label: 'Apple' },
      ]}
    />
  );
}

비활성 (Disabled)

SegmentedControl 항목을 비활성화하려면 객체 배열 data 형식을 사용하고 비활성화할 항목에 disabled: true를 설정해요. 컴포넌트 전체를 비활성화하려면 disabled prop을 사용해요.

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

function Demo() {
  return (
    <>
      <SegmentedControl disabled data={['Preview', 'Code', 'Export']} />
      <SegmentedControl
        data={[
          { value: 'preview', label: 'Preview' },
          { value: 'code', label: 'Code', disabled: true },
          { value: 'export', label: 'Export' },
        ]}
      />
    </>
  );
}

라벨로 React 노드 (React node as label)

라벨로 어떤 React 노드든 사용할 수 있어요.

import { Center, SegmentedControl } from '@mantine/core';
import { EyeIcon, CodeIcon, ArrowSquareOutIcon } from '@phosphor-icons/react';

function Demo() {
  return (
    <SegmentedControl
      data={[
        { value: 'preview', label: (<Center><EyeIcon size={16}/></Center>) },
        { value: 'code', label: (<Center><CodeIcon size={16}/></Center>) },
        { value: 'export', label: (<Center><ArrowSquareOutIcon size={16}/></Center>) },
      ]}
    />
  );
}

색상 (Color)

기본적으로 SegmentedControl은 라이트 색상 스킴에서 shadow가 있는 theme.white를, 인디케이터 배경에는 var(--mantine-color-dark-6)를 사용해요. color prop으로 인디케이터 background-color를 변경해요.

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

function Demo() {
  return <SegmentedControl color="red" data={['React', 'Angular', 'Vue', 'Svelte']} />;
}

자동 대비 (Auto contrast)

SegmentedControl은 autoContrast prop을 지원해요. true로 설정하면 인디케이터 배경색에 대한 최적의 대비를 보장하도록 라벨 텍스트 색상이 자동으로 조정돼요.

import { SegmentedControl, Stack } from '@mantine/core';

function Demo() {
  return (
    <Stack>
      <SegmentedControl color="red" data={['React', 'Angular', 'Vue', 'Svelte']} />
      <SegmentedControl color="red" autoContrast data={['React', 'Angular', 'Vue', 'Svelte']} />
    </Stack>
  );
}

전환 (Transitions)

다음으로 전환 속성을 변경할 수 있어요.

  • transitionDuration – 모든 전환 지속 시간(ms), 기본값 200
  • transitionTimingFunction – 모든 전환 타이밍 함수, 기본값 ease
import { SegmentedControl, Text } from '@mantine/core';

function Demo() {
  return (
    <>
      <Text>No transitions</Text>
      <SegmentedControl transitionDuration={0} data={['React', 'Angular', 'Vue', 'Svelte']} />
      <Text>500ms linear transition</Text>
      <SegmentedControl transitionDuration={500} transitionTimingFunction="linear" data={['React', 'Angular', 'Vue', 'Svelte']} />
    </>
  );
}

readOnly

readOnly prop을 설정하면 값을 변경할 수 없어요.

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

function Demo() {
  return <SegmentedControl readOnly defaultValue="react" data={['React', 'Angular', 'Vue']} />;
}

Styles API

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

Styles API 셀렉터:

  • root – 루트 요소
  • control – 인풋과 라벨의 래퍼 요소
  • input – 인풋 요소 (input[type="radio"]), 기본적으로 숨김
  • label – 인풋과 연결된 라벨 요소
  • indicator – 항목 사이를 이동하는 떠다니는 인디케이터
  • innerLabel – 라벨 요소 children의 래퍼

접근성과 사용성 (Accessibility and usability)

SegmentedControl은 내부적으로 라디오 인풋을 사용하므로, 라벨에 텍스트가 있으면 추가 단계 없이 기본적으로 접근 가능해요. 컴포넌트는 일반 라디오 그룹과 동일한 키보드 이벤트를 지원해요.

라벨에 텍스트가 없다면(예: 아이콘만 사용하는 SegmentedControl), VisuallyHidden을 사용해 컴포넌트를 접근 가능하게 만들어요.

import { SegmentedControl, VisuallyHidden } from '@mantine/core';
import { EyeIcon, CodeIcon, ArrowSquareOutIcon } from '@phosphor-icons/react';

function Demo() {
  const iconProps = { style: { display: 'block' }, size: 20 };

  return (
    <SegmentedControl
      data={[
        { value: 'preview', label: (<><VisuallyHidden>Preview</VisuallyHidden><EyeIcon {...iconProps} /></>) },
        { value: 'code', label: (<><VisuallyHidden>Code</VisuallyHidden><CodeIcon {...iconProps} /></>) },
        { value: 'export', label: (<><VisuallyHidden>Export</VisuallyHidden><ArrowSquareOutIcon {...iconProps} /></>) },
      ]}
    />
  );
}

더 알아보기 (Learn more)