Switch

Switch

사용자로부터 불리언(참/거짓) 입력을 받는 컴포넌트예요. 네이티브 input[type="checkbox"] 기반이에요.

출처: 문서

본문

사용법 (Usage)

Switch 컴포넌트로 색상, 라벨 위치, 설명, 오류, 크기, 반경(radius), 비활성 상태 등을 지정할 수 있어요.

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

function Demo() {
  return <Switch label="I agree to sell my privacy" />;
}

제어 컴포넌트 (Controlled)

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

function Demo() {
  const [checked, setChecked] = useState(false);
  return (
    <Switch
      checked={checked}
      onChange={(event) => setChecked(event.currentTarget.checked)}
    />
  );
}

비제어 컴포넌트 (Uncontrolled)

Switch는 네이티브 input[type="checkbox"]와 같은 방식으로 비제어 폼과 함께 사용할 수 있어요. 폼 제출 시 FormData 객체에 스위치 값을 포함하려면 name 속성을 설정해요. 비제어 폼에서 초기 체크 상태를 제어하려면 defaultChecked prop을 사용해요.

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

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

상태 (States)

기본 스위치, 체크된 스위치, 비활성 스위치, 체크된 비활성 스위치 등 다양한 상태를 만들 수 있어요.

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

function Demo() {
  return (
    <Stack>
      <Switch label="Default switch" />
      <Switch label="Checked switch" defaultChecked />
      <Switch label="Disabled switch" disabled />
      <Switch label="Disabled checked switch" disabled defaultChecked />
    </Stack>
  );
}

내부 라벨 (Inner Labels)

onLabel과 offLabel prop으로 스위치 트랙 안쪽에 라벨을 표시할 수 있어요.

import { Switch, Group } from '@mantine/core';

function Demo() {
  return <Switch onLabel="ON" offLabel="OFF" />;
}

아이콘 라벨 (Icon labels)

스위치 온/오프 상태에 아이콘을 표시할 수 있어요.

import { Switch } from '@mantine/core';
import { SunIcon, MoonStarsIcon } from '@phosphor-icons/react';

function Demo() {
  return (
    <Switch
      onLabel={<SunIcon size={16} />}
      offLabel={<MoonStarsIcon size={16} />}
    />
  );
}

썸 아이콘 (Thumb icon)

thumbIcon prop으로 스위치 썸(thumb) 안에 아이콘을 표시할 수 있어요.

import { useState } from 'react';
import { Switch } from '@mantine/core';
import { CheckIcon, XIcon } from '@phosphor-icons/react';

function Demo() {
  const [checked, setChecked] = useState(false);

  return (
    <Switch
      checked={checked}
      onChange={(event) => setChecked(event.currentTarget.checked)}
      color="teal"
      size="md"
      label="Switch with thumb icon"
      thumbIcon={checked ? <CheckIcon size={14} /> : <XIcon size={14} />}
    />
  );
}

툴팁과 함께 (With tooltip)

Tooltip 및 이와 유사한 컴포넌트에 refProp="rootRef"를 설정하면 Switch와 함께 동작하게 할 수 있어요.

import { Switch, Tooltip } from '@mantine/core';

function Demo() {
  return (
    <Tooltip label="Switch with tooltip" refProp="rootRef">
      <Switch label="Switch" />
    </Tooltip>
  );
}

포인터 커서 (Pointer cursor)

기본적으로 스위치 입력과 라벨은 네이티브 input[type="checkbox"]와 같이 cursor: default를 사용해요. 커서를 포인터로 바꾸려면 theme에서 cursorType을 설정해요.

import { createTheme, MantineProvider, Switch } from '@mantine/core';

const theme = createTheme({
  cursorType: 'pointer',
});

루트 요소에 props 추가하기 (Add props to the root element)

컴포넌트에 전달된 모든 props는 입력 요소로 전달돼요. 루트 요소에 props를 추가하려면 wrapperProps를 사용해요. 예를 들어 data-testid="wrapper"는 루트 요소에, data-testid="input"은 입력 요소에 추가돼요.

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

function Demo() {
  return (
    <Switch
      label="Switch"
      wrapperProps={{ 'data-testid': 'wrapper' }}
      inputProps={{ 'data-testid': 'input' }}
    />
  );
}

Switch.Group

여러 Switch를 묶어 그룹으로 만들 수 있어요. 라벨, 설명, 오류, 별표(asterisk) 등을 지원해요.

import { Switch, Group } from '@mantine/core';

function Demo() {
  return (
    <Switch.Group label="Select your favorite framework/library" description="This is anonymous" withAsterisk>
      <Group mt="xs">
        <Switch value="react" label="React" />
        <Switch value="svelte" label="Svelte" />
        <Switch value="angular" label="Angular" />
        <Switch value="vue" label="Vue" />
      </Group>
    </Switch.Group>
  );
}

비제어 폼과 함께 사용하는 Switch.Group (Switch.Group with uncontrolled forms)

Switch.Group는 비제어 폼과 함께 사용할 수 있어요. 체크된 모든 값을 hiddenInputValuesSeparator prop으로 하나의 문자열로 합치는 숨은 입력(hidden input)을 렌더링해요.

비제어 폼 사용을 위한 props:

  • name – 숨은 입력에 전달되는 name 속성
  • hiddenInputValuesSeparator – 체크된 값을 하나의 문자열로 합치는 데 사용되는 문자열, 기본값 ','
  • hiddenInputProps – 숨은 입력에 전달되는 추가 props
function UncontrolledForm() {
  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const formData = new FormData(event.currentTarget);
        console.log('Switch group value:', formData.get('frameworks'));
      }}
    >
      <Switch.Group name="frameworks" label="Select your favorite frameworks">
        <Switch value="react" label="React" />
        <Switch value="svelte" label="Svelte" />
      </Switch.Group>
      <button type="submit">Submit</button>
    </form>
  );
}

maxSelectedValues

maxSelectedValues prop으로 Switch.Group에서 선택할 수 있는 값의 개수를 제한할 수 있어요. 한도에 도달하면 나머지 스위치는 비활성화되어 선택할 수 없어요.

import { Group, Switch } from '@mantine/core';

function Demo() {
  return (
    <Switch.Group label="Select your favorite framework" maxSelectedValues={2}>
      <Group mt="xs">
        <Switch value="react" label="React" />
        <Switch value="svelte" label="Svelte" />
        <Switch value="angular" label="Angular" />
        <Switch value="vue" label="Vue" />
      </Group>
    </Switch.Group>
  );
}

비활성 Switch.Group (Switch.Group disabled)

import { Switch, Group } from '@mantine/core';

function Demo() {
  return (
    <Switch.Group label="Select your favorite framework/library" description="This is anonymous" disabled>
      <Group mt="xs">
        <Switch value="react" label="React" />
        <Switch value="svelte" label="Svelte" />
        <Switch value="angular" label="Angular" />
        <Switch value="vue" label="Vue" />
      </Group>
    </Switch.Group>
  );
}

제어 Switch.Group (Controlled Switch.Group)

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

function Demo() {
  const [value, setValue] = useState<string[]>([]);

  return <Switch.Group value={value} onChange={setValue} />;
}

체크 상태에 따른 스타일 변경 (Change styles based on checked state)

input:checked + & 선택자를 사용해 체크 상태에 따른 스타일을 지정할 수 있어요.

.track {
  transition:
    background-color 200ms ease,
    border-color 200ms ease;

  input:checked + & {
    background-color: var(--mantine-color-lime-5);
    border-color: var(--mantine-color-lime-5);

    & > .thumb {
      background-color: var(--mantine-color-black);

      &::before {
        background-color: var(--mantine-color-lime-5);
      }
    }
  }
}

Styles API

Switch는 Styles API를 지원해요. classNames prop으로 컴포넌트의 내부 요소에 스타일을 추가할 수 있어요. 자세한 내용은 Styles API 문서를 참고해요.

selector 설명
root 루트 요소
track 스위치 트랙, thumb과 trackLabel을 포함해요
trackLabel track 안에 표시되는 라벨
thumb track 안에 표시되는 썸
input 입력 요소(input[type="checkbox"]), 기본적으로 숨겨져 있어요
body 입력 바디, 다른 모든 요소를 포함해요
labelWrapper label, description, error를 포함해요
label 라벨 요소
description 라벨 아래에 표시되는 설명
error 라벨 아래에 표시되는 오류 메시지

입력 ref 가져오기 (Get input ref)

import { useRef } from 'react';
import { Switch } from '@mantine/core';

function Demo() {
  const ref = useRef<HTMLInputElement>(null);
  return <Switch ref={ref} />;
}

접근성 (Accessibility)

Switch는 일반적인 input[type="checkbox"]예요. Switch를 label prop 없이 사용하면 입력에 라벨이 없으므로 aria-label을 설정해야 해요.

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

// -> ok, input has aria-label
function Good() {
  return <Switch aria-label="Enable notifications" />;
}

// -> ok, input has associated label
function AlsoGood() {
  return <Switch label="Enable notifications" />;
}

더 알아보기 (Learn more)