본문으로 건너뛰기

PlSegmentedButton

알약 하나에 담긴 두 개 이상의 선택지 중 정확히 하나가 선택됩니다. 타일이 떠난 세그먼트에서 고른 세그먼트로 미끄러집니다.

React
tsx
import { PlSegment, PlSegmentedButton } from 'plass-ui';

<PlSegmentedButton aria-label="Period" value={period} onValueChange={setPeriod}>
  <PlSegment value="day">Day</PlSegment>
  <PlSegment value="week">Week</PlSegment>
</PlSegmentedButton>;
dart
import 'package:plass_ui/plass_ui.dart';

PlSegmentedButton<String>(
  semanticLabel: 'Period',
  value: period,
  onChanged: (String next) => setState(() => period = next),
  segments: const <PlSegment<String>>[
    PlSegment<String>(value: 'day', label: Text('Day')),
    PlSegment<String>(value: 'week', label: Text('Week')),
  ],
);

Props

Prop타입기본값설명
variant공통'solid' | 'glass' | 'ghost''glass'홈과 그 위를 타는 타일의 재질. solid는 색 유리 키가 홈을 타고, glass는 맑은 타일, ghost는 홈 없음
size공통'xs' | 'sm' | 'md' | 'lg' | 'xl''md'세그먼트의 높이와 타입 스케일. PlButton과 같은 사다리
color공통'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'의미론적 색 역할. 임의 색상값은 받지 않습니다
density공통'default' | 'compact''default'여백만 바꿉니다. 높이와 글자 크기는 그대로
elevation공통0 | 1 | 2 | 30홈의 그림자 깊이. 홈은 페이지에 파인 것이므로 기본값은 0입니다
valuestring | number | null선택된 세그먼트. onValueChange와 함께 controlled로 씁니다
defaultValuestring | number | nullnulluncontrolled일 때 처음 선택된 세그먼트
onValueChange(value: string | number | null) => void선택이 바뀔 때 호출됩니다
fullWidthbooleanfalse세그먼트들이 전체 너비를 균등하게 나눠 가집니다
readOnlybooleanfalse선택은 보이지만 바꿀 수 없습니다
disabledbooleanfalse모든 세그먼트가 반응하지 않습니다
namestringform 제출 시 이 값을 식별하는 이름
childrenReactNodePlSegment 목록
Prop타입기본값설명
segments * List<PlSegment<T>>선택지들. children이 아니라 설명의 목록입니다 — 묶음이 roving focus와 화살표 키, 미끄러지는 타일을 소유합니다
value * T?선택된 세그먼트. onValueChange와 함께 controlled로 씁니다
onChangedValueChanged<T>?선택이 바뀔 때 호출됩니다
variant공통PlassVariantPlassVariant.glass홈과 그 위를 타는 타일의 재질. solid는 색 유리 키가 홈을 타고, glass는 맑은 타일, ghost는 홈 없음
size공통PlassSizePlassSize.md세그먼트의 높이와 타입 스케일. PlButton과 같은 사다리
color공통PlassColorPlassColor.primary의미론적 색 역할. 임의 색상값은 받지 않습니다
density공통PlassDensityPlassDensity.standard여백만 바꿉니다. 높이와 글자 크기는 그대로
elevation공통int0홈의 그림자 깊이. 홈은 페이지에 파인 것이므로 기본값은 0입니다
fullWidthboolfalse세그먼트들이 전체 너비를 균등하게 나눠 가집니다
readOnlyboolfalse선택은 보이지만 바꿀 수 없습니다
disabledboolfalse모든 세그먼트가 반응하지 않습니다
semanticLabelString?묶음을 스크린 리더가 부를 이름. 눈에 보이는 자기 라벨이 없습니다

네이티브 <div> 속성은 그대로 전달됩니다. color는 위 표의 color와 충돌해서, defaultValueonChange는 이 묶음이 각각 세그먼트 값으로서의 defaultValueonValueChange로 쓰기 때문에 제외됩니다.

묶음은 세그먼트 값의 타입에 대해 제네릭입니다(PlSegmentedButton<String>, PlSegmentedButton<Period>). 그래서 valueonChangeddynamic이 아니라 타입을 가지고, 패키지의 다른 모든 컨트롤과 마찬가지로 controlled입니다.

PlSegment

Prop타입기본값설명
value * string | number세그먼트를 식별하는 값. onValueChange가 보고하는 것
startIconReactNode라벨 앞에 놓이는 내용. 1.2em으로 그려져 라벨 크기를 따라갑니다
endIconReactNode라벨 뒤 — 개수, 상태 점
disabledbooleanfalse고를 수 없지만 여전히 묶음의 일부입니다
childrenReactNode세그먼트의 라벨
Prop타입기본값설명
value * T세그먼트를 식별하는 값. onValueChange가 보고하는 것
labelWidget?세그먼트의 라벨
startIconWidget?라벨 앞에 놓이는 내용. 1.2em으로 그려져 라벨 크기를 따라갑니다
endIconWidget?라벨 뒤 — 개수, 상태 점
disabledboolfalse고를 수 없지만 여전히 묶음의 일부입니다

variant, size, density는 세그먼트에 주는 것이 아니라 감싸는 PlSegmentedButton에서 내려받습니다. 세 번째 세그먼트만 크기가 다른 segmented button은 segmented button이 아닙니다.

세그먼트는 **위젯이 아니라 설명인 PlSegment**입니다. radio 옵션이 그런 것과 같은 이유로, 묶음이 roving focus와 화살표 키, 그리고 세그먼트 사이를 미끄러지는 타일을 소유하므로 어느 것이 선택되었고 각각이 어디 있는지를 알아야 합니다.

variantsizedensity도 가지지 않으며, 가질 수도 없습니다. 세 번째 세그먼트만 크기가 다른 segmented button은 segmented button이 아닙니다.

라이브러리 전체에서 공유 축(variant size color density elevation)이 뜻하는 바는 prop 규칙에 있습니다.

Segmented button, tabs, select 중 고르기

  • Segmented button: 이미 화면에 있는 것을 걸러 내는, 짧고 서로 배타적인 선택지 몇 개. 기간, 범위, 레이아웃.
  • Tabs: 선택이 내용 패널 전체를 바꿀 때.
  • Select: 선택지가 다섯 개를 넘거나, 하나하나가 길 때.

Examples

variant

홈은 --plass-well을 씁니다. 라이브러리의 유일한 inset 그림자이자 solid field가 그려지는 것과 같은 그림자이고, 쓰이는 곳은 이 둘뿐입니다. 홈과 채워진 field는 둘 다 무언가가 들어앉는 상자입니다. slider의 레일은 그런 상자가 아니라서 더 이상 이 그림자를 쓰지 않습니다. 레일은 따라 보는 선입니다.

solid는 타일에 색 계열의 그러데이션을 넣고 그 아래에 같은 계열의 틴트 그림자를 깝니다. 디자인 언어의 문장을 그대로 옮긴 것입니다. 홈을 타고 가는 색 유리 키. glassghost는 대신 맑은 유리판을 들어 올리고 라벨은 accent 색으로 둡니다.

React

color

React

size

PlButton과 같은 높이 사다리를 씁니다. 툴바 안의 segmented button이 옆의 버튼들과 줄을 맞춥니다.

React

fullWidth

세그먼트들이 한 줄을 균등하게 나눠 가집니다. 타일은 배치가 끝날 때마다 다시 측정되므로, 컨테이너 너비가 변해도 자기 세그먼트 아래에 남아 있습니다.

React

startIcon과 endIcon

둘 다 em으로 크기가 정해지므로 라벨을 따라갑니다. 아이콘만 있는 세그먼트에는 aria-label이 필요합니다.

React

Accessibility

  • 묶음은 role="radiogroup"이고 각 세그먼트는 진짜 radio입니다. 접근성 논거는 이것이 전부입니다. segmented button은 "이 중 정확히 하나" 입니다. aria-pressed 토글로 만들었다면 독립된 스위치 네 개를 읽어 주고, 그중 셋은 마침 꺼져 있는 상태가 됩니다.
  • 묶음 전체가 tab stop 하나를 차지하고, 로 그 안에서 움직입니다. roving tab index는 Base UI의 것입니다.
  • 묶음에 aria-label을 주세요. 눈에 보이는 자기 라벨이 없고, 이름 없는 그룹은 스크린리더가 "radio group"이라고만 읽습니다.
  • focus ring은 안쪽으로 그려집니다. 홈 안의 세그먼트에 바깥쪽 ring을 그리면 이웃 위에 덧칠됩니다.
  • 타일은 transform이 아니라 left, top, width, height를 애니메이션합니다. 빈 상자라서 이동하는 동안 다시 샘플링되는 글자가 없습니다. 무언가 움직이는 것이 존재 이유인 컴포넌트에서도 no-transform 규칙이 살아남는 이유입니다.
  • 아무것도 선택되지 않은 묶음의 첫 선택은 왼쪽 끝에서 날아오지 않고 제자리에 나타납니다. 앉을 자리가 생기기 전까지 타일을 마운트하지 않기 때문입니다.
  • 각 세그먼트는 서로 배타적인 묶음의 하나로, 선택 여부와 함께 알려집니다. segmented button은 "이 중 정확히 하나" 입니다. 토글로 만들었다면 독립된 스위치 네 개를 읽어 주고, 그중 셋은 마침 꺼져 있는 상태가 됩니다.
  • 묶음 전체가 focus stop 하나를 차지합니다. 정확히 한 세그먼트만 tab 순서에 있고 나머지는 ExcludeFocus로 감싸여 있습니다. 가 선택을 옮기고, 양 끝에서 순환합니다.
  • focus ring은 안쪽으로 그려집니다. 홈 안의 세그먼트에 바깥쪽 ring을 그리면 이웃 위에 덧칠됩니다.
  • 타일은 측정된 사각형을 애니메이션합니다. 빈 상자라서 이동하는 동안 다시 샘플링되는 글자가 없습니다.
  • 묶음에 semanticLabel을 주세요. 눈에 보이는 자기 라벨이 없습니다.

React 빌드와 다른 점

ReactFlutter이유
<PlSegment> children설명으로서의 segments묶음이 roving focus와 화살표 키, 미끄러지는 타일을 소유하므로 어느 것이 선택되었고 각각이 어디 있는지 알아야 합니다.
defaultValue / onValueChangevalue / onChangedFlutter 자신의 컨트롤이 controlled이고, 콜백 이름도 Flutter의 것입니다.
string | number인 값제네릭 TDart에는 제네릭이 있어, 관례로 제한하는 대신 타입이 검사됩니다.
타일 위의 CSS 커스텀 속성 넷측정된 RectAnimatedPositioned같은 생각(선택된 세그먼트를 재고, 상자를 애니메이션한다)을 Flutter의 말로 한 것입니다. 어느 쪽도 transform하지 않습니다.
aria-labelsemanticLabelFlutter의 이름입니다.
name과 hidden input포함될 네이티브 form 제출이 없습니다.

Released under the MIT License