SegmentedControl

Action

2~5개 옵션 중 하나만 고르는 세그먼트 컨트롤.

Usage

2~5개 배타 옵션을 가로로 배치해 즉시 전환(보기/상태/정렬). 페이지 이동 없음

import

import

import { SegmentedControl } from '@mildang/design-system/SegmentedControl';

SegmentedControl 하나만 가져오면 SegmentedControl.Root · SegmentedControl.Item 를 그 아래에서 쓸 수 있다.

예제를 복사해 쓸 때 필요한 준비

@mildang/icons 를 따로 설치한다. DS 패키지에 아이콘 컴포넌트가 포함되지 않는다.

@mildang/styled-system 은 이 저장소에서 Panda 가 생성하는 산출물이다. 저장소 안에서는 turbo run ship 이후 쓸 수 있고, 패키지 소비자는 자기 Panda 산출물이나 다른 레이아웃 수단으로 바꿔야 한다.

Anatomy

tsx

import { SegmentedControl } from '@mildang/design-system/SegmentedControl';

export default function Example() {
  return (
    <SegmentedControl>
      <SegmentedControl.Item />
    </SegmentedControl>
  );
}

부품

필수 여부

반복

위치

SegmentedControl.Root

필수

SegmentedControl.Item

필수

여러 개 가능

SegmentedControl 안에 옵션마다 둔다.

  • SegmentedControl 안에 SegmentedControl.Item을 두 개 이상 배치한다.

API Reference

SegmentedControl Props

Prop

Type

Default

children
필수

ReactNode

지정 안 함

aria-label

string

지정 안 함

aria-labelledby

string

지정 안 함

className

string

지정 안 함

defaultValue

string | (readonly string[] & string)

지정 안 함

disabled

boolean

false

fullWidth

boolean

false

onValueChange

(nextValue: string) => void

지정 안 함

size

"sm" | "md" | "lg"

md

value

string

지정 안 함

SegmentedControl.Item

하나의 선택 가능한 세그먼트 항목이다.

Prop

Type

Default

children
필수

ReactNode

지정 안 함

value
필수

string

지정 안 함

className

string

지정 안 함

count

ReactNode

지정 안 함

disabled

boolean

지정 안 함

endIcon

ReactNode

지정 안 함

startIcon

ReactNode

지정 안 함

아이콘·카운트 배치

SegmentedControl.Item 은 라벨 앞뒤에 아이콘을, 라벨 뒤에 카운트 배지를 붙일 수 있다.

prop위치설명
startIcon라벨 왼쪽항목을 아이콘으로 먼저 식별시킬 때
endIcon라벨 오른쪽보조 표시(정렬 방향 등)를 라벨 뒤에 붙일 때
count라벨 오른쪽(끝)size="xs" color="blue" 텍스트 칩으로 렌더되는 카운트 배지

startIcon·endIcon·count 는 서로 배타적이지 않고 함께 쓸 수 있다.

아이콘·카운트

startIcon(라벨 앞) · endIcon(라벨 뒤) · count(카운트 배지)를 보여줍니다. 셋 다 함께 쓸 수 있습니다.

import { useState } from 'react';
import { SegmentedControl as SegmentedControl } from '@mildang/design-system/SegmentedControl';
import HomeIcon from '@mildang/icons/react/home';
import BookmarkIcon from '@mildang/icons/react/bookmark';
import CalendarIcon from '@mildang/icons/react/calendar';
import BulletListIcon from '@mildang/icons/react/bullet-list';
import CardListIcon from '@mildang/icons/react/card-list';

const SegmentedControlWithIconsExample = () => {
    const [startValue, setStartValue] = useState('home');
    const [endValue, setEndValue] = useState('list');

    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 32 }}>
        <div>
          <p style={{ marginBottom: 8 }}>startIcon + 라벨</p>
          <SegmentedControl
            value={startValue}
            onValueChange={setStartValue}
            aria-label="View with leading icons"
          >
            <SegmentedControl.Item value="home" startIcon={<HomeIcon />}>

            </SegmentedControl.Item>
            <SegmentedControl.Item value="bookmark" startIcon={<BookmarkIcon />}>
              북마크
            </SegmentedControl.Item>
            <SegmentedControl.Item value="calendar" startIcon={<CalendarIcon />}>
              캘린더
            </SegmentedControl.Item>
          </SegmentedControl>
        </div>

        <div>
          <p style={{ marginBottom: 8 }}>라벨 + endIcon</p>
          <SegmentedControl
            value={endValue}
            onValueChange={setEndValue}
            aria-label="View with trailing icons"
          >
            <SegmentedControl.Item value="list" endIcon={<BulletListIcon />}>
              리스트
            </SegmentedControl.Item>
            <SegmentedControl.Item value="card" endIcon={<CardListIcon />}>
              카드
            </SegmentedControl.Item>
          </SegmentedControl>
        </div>
      </div>
    );
  };

export default SegmentedControlWithIconsExample;

예제

기본 사용

선택지 두세 개를 한 줄에 붙여 즉시 전환합니다 — 목록·카드처럼 같은 데이터를 다르게 보는 자리에 씁니다.

import { SegmentedControl as SegmentedControl, type SegmentedControlProps } from '@mildang/design-system/SegmentedControl';
import { useState } from 'react';
import { css } from '@mildang/styled-system/css';

const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as SegmentedControlProps;

const SegmentedControlBasicExampleRender = (args: SegmentedControlProps) => {
    const [value, setValue] = useState('a');
    return (
      <div className={css({ display: 'flex', flexDirection: 'column', gap: 32 })}>
        <SegmentedControl {...args} value={value} onValueChange={setValue} aria-label="Segmented example">
          <SegmentedControl.Item value="a">Button</SegmentedControl.Item>
          <SegmentedControl.Item value="b">Button</SegmentedControl.Item>
          <SegmentedControl.Item value="c">Button</SegmentedControl.Item>
          <SegmentedControl.Item value="d">Button</SegmentedControl.Item>
          <SegmentedControl.Item value="e">Button</SegmentedControl.Item>
        </SegmentedControl>
      </div>
    );
  };

export default function SegmentedControlBasicExample(props: Partial<SegmentedControlProps>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as SegmentedControlProps;
  return SegmentedControlBasicExampleRender(mergedProps);
}

크기

sm·md·lg 의 높이를 비교합니다. 툴바 안에 들어가면 sm, 본문 상단이면 md 가 기준입니다.

import { useState } from 'react';
import { SegmentedControl as SegmentedControl } from '@mildang/design-system/SegmentedControl';

const SegmentedControlSizesExample = () => {
    const [largeValue, setLargeValue] = useState('a');
    const [mediumValue, setMediumValue] = useState('b');
    const [smallValue, setSmallValue] = useState('c');

    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 32 }}>
        <div>
          <p style={{ marginBottom: 8 }}>Large</p>
          <SegmentedControl
            size="lg"
            value={largeValue}
            onValueChange={setLargeValue}
            aria-label="Large size"
          >
            <SegmentedControl.Item value="a">Button</SegmentedControl.Item>
            <SegmentedControl.Item value="b">Button</SegmentedControl.Item>
            <SegmentedControl.Item value="c">Button</SegmentedControl.Item>
            <SegmentedControl.Item value="d">Button</SegmentedControl.Item>
            <SegmentedControl.Item value="e">Button</SegmentedControl.Item>
          </SegmentedControl>
        </div>

        <div>
          <p style={{ marginBottom: 8 }}>Medium</p>
          <SegmentedControl
            size="md"
            value={mediumValue}
            onValueChange={setMediumValue}
            aria-label="Medium size"
          >
            <SegmentedControl.Item value="a">Button</SegmentedControl.Item>
            <SegmentedControl.Item value="b">Button</SegmentedControl.Item>
            <SegmentedControl.Item value="c">Button</SegmentedControl.Item>
            <SegmentedControl.Item value="d">Button</SegmentedControl.Item>
            <SegmentedControl.Item value="e">Button</SegmentedControl.Item>
          </SegmentedControl>
        </div>

        <div>
          <p style={{ marginBottom: 8 }}>Small</p>
          <SegmentedControl
            size="sm"
            value={smallValue}
            onValueChange={setSmallValue}
            aria-label="Small size"
          >
            <SegmentedControl.Item value="a">Button</SegmentedControl.Item>
            <SegmentedControl.Item value="b">Button</SegmentedControl.Item>
            <SegmentedControl.Item value="c">Button</SegmentedControl.Item>
            <SegmentedControl.Item value="d">Button</SegmentedControl.Item>
            <SegmentedControl.Item value="e">Button</SegmentedControl.Item>
          </SegmentedControl>
        </div>
      </div>
    );
  };

export default SegmentedControlSizesExample;

비활성 상태

전환할 수 없는 상황에서는 컨트롤 전체를 disabled 로 잠급니다.

import { SegmentedControl as SegmentedControl } from '@mildang/design-system/SegmentedControl';
import { css } from '@mildang/styled-system/css';

// States stories for docs (Default, Active, Active Hover, Hover, Pressed)
const stateWrap = css({ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '8px' });

const SegmentedControlStateActiveExample = () => (
    <div className={stateWrap}>
      <SegmentedControl size="lg" defaultValue="a" aria-label="Active state">
        <SegmentedControl.Item value="a">Button</SegmentedControl.Item>
      </SegmentedControl>
    </div>
  );

export default SegmentedControlStateActiveExample;

FullWidth

import { useState } from 'react';
import { SegmentedControl as SegmentedControl } from '@mildang/design-system/SegmentedControl';

const SegmentedControlFullWidthExample = () => {
    const [value, setValue] = useState('b');
    return (
      <div style={{ width: 420 }}>
        <SegmentedControl value={value} onValueChange={setValue} fullWidth aria-label="Full width">
          <SegmentedControl.Item value="a">Button</SegmentedControl.Item>
          <SegmentedControl.Item value="b">Button</SegmentedControl.Item>
          <SegmentedControl.Item value="c">Button</SegmentedControl.Item>
          <SegmentedControl.Item value="d">Button</SegmentedControl.Item>
          <SegmentedControl.Item value="e" disabled>
            Button
          </SegmentedControl.Item>
        </SegmentedControl>
      </div>
    );
  };

export default SegmentedControlFullWidthExample;

State/Default

import { SegmentedControl as SegmentedControl } from '@mildang/design-system/SegmentedControl';
import { css } from '@mildang/styled-system/css';

// States stories for docs (Default, Active, Active Hover, Hover, Pressed)
const stateWrap = css({ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '8px' });

const SegmentedControlStateDefaultExample = () => (
    <div className={stateWrap}>
      <SegmentedControl size="lg" aria-label="Default state">
        <SegmentedControl.Item value="a">Button</SegmentedControl.Item>
      </SegmentedControl>
    </div>
  );

export default SegmentedControlStateDefaultExample;

State/Active Hover

import { SegmentedControl as SegmentedControl } from '@mildang/design-system/SegmentedControl';
import { css } from '@mildang/styled-system/css';

// States stories for docs (Default, Active, Active Hover, Hover, Pressed)
const stateWrap = css({ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '8px' });

const SegmentedControlStateActiveHoverExample = () => (
    <div id="sc-active-hover" className={stateWrap}>
      <SegmentedControl size="lg" defaultValue="a" aria-label="Active hover state">
        <SegmentedControl.Item value="a">Button</SegmentedControl.Item>
      </SegmentedControl>
    </div>
  );

export default SegmentedControlStateActiveHoverExample;

State/Hover

import { SegmentedControl as SegmentedControl } from '@mildang/design-system/SegmentedControl';
import { css } from '@mildang/styled-system/css';

// States stories for docs (Default, Active, Active Hover, Hover, Pressed)
const stateWrap = css({ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '8px' });

const SegmentedControlStateHoverExample = () => (
    <div id="sc-hover" className={stateWrap}>
      <SegmentedControl size="lg" defaultValue="a" aria-label="Hover state">
        <SegmentedControl.Item value="a">Button</SegmentedControl.Item>
        <SegmentedControl.Item value="b" className="hover-target">
          Button
        </SegmentedControl.Item>
      </SegmentedControl>
    </div>
  );

export default SegmentedControlStateHoverExample;

State/Pressed

import { SegmentedControl as SegmentedControl } from '@mildang/design-system/SegmentedControl';
import { css } from '@mildang/styled-system/css';

// States stories for docs (Default, Active, Active Hover, Hover, Pressed)
const stateWrap = css({ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '8px' });

const SegmentedControlStatePressedExample = () => (
    <div id="sc-pressed" className={stateWrap}>
      <SegmentedControl size="lg" defaultValue="a" aria-label="Pressed state">
        <SegmentedControl.Item value="a">Button</SegmentedControl.Item>
        <SegmentedControl.Item value="b" className="press-target">
          Button
        </SegmentedControl.Item>
      </SegmentedControl>
    </div>
  );

export default SegmentedControlStatePressedExample;