콘텐츠로 이동

switch —— 스위치

스위치는 “이상태 컨트롤”입니다: 켜짐이나 꺼짐, 세 번째 상태는 없습니다. checkbox (이상태)와 비슷해 보이지만 의미가 다릅니다——checkbox는 “어떤 옵션을 선택했는지”를 나타내고, 스위치는 “어떤 기능이 활성화되었는지”를 나타냅니다. APG는 이를 위해 전용 role="switch"를 정의합니다.

  • 각 말단 구현 LhSwitch: 설정 항목의 켜기/끄기, 권한 스위치, 구독 상태.
  • 이상태이면서 “즉시 적용” 의미를 가진 조작 (체크하면 바로 적용, 제출을 기다리지 않음).
  • 사용하면 안 되는 경우: 폼 안의 다중 선택 (checkbox 사용); 여러 단계 선택 (segmented 또는 radio 사용).
import { getSwitchClassNames, getSwitchLabel, getSwitchAria } from '@lh-ui/share/core/switch'
import type { LhSwitchState } from '@lh-ui/share/core/switch'

getSwitchAria의 disabled || loading 병합은 의도된 것입니다: 로딩 중인 스위치와 비활성화된 스위치는 접근성 측면에서 동일하게 작동합니다——둘 다 조작할 수 없으며, 스크린 리더는 “사용할 수 없음”이라고 읽어야지 “사용 가능한데 눌러도 반응 없음”이라고 읽어서는 안 됩니다.

getSwitchLabel은 activeText / inactiveText를 반환합니다: 스위치의 문구는 손잡이 양쪽에 붙으며, 이것이 checkbox에 대한 스위치의 시각적 장점입니다——상태를 한눈에 읽을 수 있고, 위치 기억에만 의존하지 않습니다.

함수 시그니처 반환 역할
getSwitchClassNames (state: LhSwitchState) => string[] 클래스명 배열 checked/disabled/loading/size 수식
getSwitchLabel (state: LhSwitchState) => string 문구 선택 시 activeText, 그 외에는 inactiveText
getSwitchAria (state: Pick<LhSwitchState, 'checked' | 'disabled' | 'loading'>) => LhAriaAttrs aria 속성 팩 role="switch" 의미

출력 예시:

getSwitchClassNames({ checked: true, disabled: false, loading: false, name: '', size: 'medium', activeText: '', inactiveText: '' })
// ['lh-switch', 'lh-switch--checked']
getSwitchLabel({ checked: true, activeText: '켬', inactiveText: '끔' }) // '켬'
getSwitchAria({ checked: true, disabled: false, loading: false })
// { role: 'switch', 'aria-checked': true }
getSwitchAria({ checked: false, disabled: false, loading: true })
// { role: 'switch', 'aria-checked': false, 'aria-disabled': true }
export interface LhSwitchState {
checked: boolean
disabled: boolean
loading: boolean
name: string // 폼 제출 시의 키 이름
size: LhComponentSize
activeText: string // 켜졌을 때 문구
inactiveText: string // 꺼졌을 때 문구
}

getSwitchAria는 APG의 switch 패턴 속성 팩입니다. 핵심은 네이티브 input을 쓰는지 커스텀 컨트롤을 쓰는지입니다:

// 방안 A (권장): 네이티브 <input type="checkbox"> + role="switch"로 의미 덮어쓰기
// 브라우저 자체의 checkbox 의미가 role="switch"로 강화되고, aria-checked는 :checked와 자동으로 동기화되며,
// 키보드 Space 전환, 폼 제출이 모두 무료
<input type="checkbox" role="switch" :aria-checked="checked" />
// 방안 B: 순수 커스텀 컨트롤 (div로 시뮬레이션), 모든 의미를 수동으로 부착해야 함
// getSwitchAria는 이것을 위해 준비됨 — role + aria-checked + aria-disabled

uniapp 등 h5가 아닌 말단은 종종 방안 B만 가능합니다 (네이티브 checkbox의 외형을 Customizing하기 어려움), 이때 getSwitchAria는 필수품입니다.

import { getSwitchClassNames, getSwitchAria } from '@lh-ui/share/core/switch'
getSwitchAria({ checked: true, disabled: false, loading: false })
// { role: 'switch', 'aria-checked': true }
<!-- Vue3 -->
<div :class="getSwitchClassNames({ checked, disabled, loading, name, size, activeText, inactiveText })">
<span class="lh-switch__label lh-switch__label--left">{{ inactiveText }}</span>
<div class="lh-switch__core" v-bind="getSwitchAria({ checked, disabled, loading })" @click="toggle">
<LhSpinner v-if="loading" class="lh-switch__loading" :size="12" />
</div>
<span class="lh-switch__label lh-switch__label--right">{{ activeText }}</span>
</div>
// React
const aria = getSwitchAria({ checked, disabled, loading })
return (
<div className="lh-switch">
<div className="lh-switch__core" {...aria} onClick={() => !disabled && !loading && onChange(!checked)} />
</div>
)
// uniapp: view로 손잡이를 시뮬레이션; h5가 아닌 말단에서는 role/aria 지원이 제한적이며 getSwitchAria의 결과물은 안전하게 간소화됨
// (undefined 값의 속성은 프레임워크가 무시하므로 잘못된 마크업이 만들어지지 않음)
클래스명 의미
.lh-switch 기본 클래스
.lh-switch--checked 켜짐 상태 (손잡이 오른쪽으로 이동, 바탕색이 주색으로)
.lh-switch--checked__core 켜짐 상태 손잡이 (중첩 작성, --checked와의 충돌 회피)
.lh-switch--{small|large} 크기 단계
.lh-switch--disabled / --loading 비활성 / 로딩
.lh-switch__core 미끄럼 트랙
.lh-switch__label 양쪽 문구
.lh-switch__loading 로딩 spinner

켜짐 색은 var(--lh-primary-color), 꺼짐 색은 --lh-fill-color (중성 회색)입니다.

알려진 문제 (“높이 토큰 특별 조사”에 기록): 스위치 손잡이의 translate(-50%,-50%)가 base와 keyframes에 중복해서 하드코딩되어 있어, 크기를 바꿀 때 두 곳을 함께 수정해야 합니다.

  • aria-checked와 시각의 일관성: 커스텀 컨트롤의 checked 상태는 반드시 getSwitchAria의 출력과 동일한 출처여야 하며, 그렇지 않으면 “aria는 켜졌다고 말하는데, 보기에는 꺼져 있는” 일이 생깁니다.
  • 로딩 상태의 클릭 무시: 로딩 중에는 클릭이 값을 전환하지 않아야 하며, UI 계층의 click 처리는 disabled || loading을 판정해야 합니다.
  • 문구의 방향: activeText는 켜짐 쪽 (보통 오른쪽)에, inactiveText는 꺼짐 쪽에 붙습니다; 문구가 비어 있으면 스타일이 양쪽 공간을 자동으로 거둡니다.
  • switch와 checkbox의 선택 기준: 즉시 적용 여부. 스위치는 전환 즉시 적용되고, checkbox는 보통 폼 제출을 따릅니다.
@lh-ui/share/core/switch → getSwitchAria( )
返回值
(填好参数后点运行)

Copyright © 2024-2026 FengMo WenXue