Va.ButtonGroup — 상호 배타적 선택을 자동화하는 버튼 컨테이너
여러 버튼을 나란히 담고, 하나가 선택되면 나머지는 자동으로 해제되는 라디오형 컨테이너입니다. Va.Div를 상속해 레이아웃 컨테이너 특성을 그대로 갖고, 그 위에 그룹 배타 선택 로직만 얹은 얇은 확장.
- 클래스: Va.ButtonGroup — va_component.js:17484
- short name: buttonGroup
- 상속: Va.Div (다른 버튼 계열과 달리 Component 직속 아님)
- isContainer: true
- 베이스 CSS: va-div (Div의 것을 그대로 씀 — 자체 va-button-group 클래스 없음)

1. 기본 사용
{
tagName: 'buttonGroup',
layout: 'ds-flex fd-row ai-center gap-s',
tags: [
{ tagName: 'toggleButton', text: '왼쪽 정렬' },
{ tagName: 'toggleButton', text: '가운데 정렬' },
{ tagName: 'toggleButton', text: '오른쪽 정렬' }
]
}
사용자가 어떤 버튼을 클릭하면:
- 그룹 안 모든 자식이 unselect() 호출됨
- toggleButton이면 unpress()까지 호출됨
- 클릭된 버튼만 select() (+toggleButton이면 press()) 됨
결과 — 언제나 하나만 선택된 라디오 UI가 자동으로 완성.
2. 다른 버튼/컨테이너와의 차이
항목Va.DivVa.ButtonGroup개별 Va.ToggleButton 나열
| 레이아웃 | ✓ | ✓ (Div 상속) | ✕ (부모가 제공해야) |
| 배타 선택 자동 | ✕ | ✓ | ✕ (수동 처리 필요) |
| toggleButton press/unpress 자동 처리 | ✕ | ✓ | ✕ |
| 이벤트 발화 | 없음 | 없음 (개별 버튼이 발화) | ✓ |
| 자체 상태 | 없음 | 없음 | pressed |
한 줄 요약: "여러 버튼을 라디오처럼 만드는 얇은 마법 컨테이너".
3. 속성
독자적 속성이 없습니다 — va_component.js:17489-17491에서 properties, events를 배열 spread만 하고 아무것도 추가하지 않습니다.
Va.Div의 모든 속성이 그대로 유효:
속성설명
| layout | Flex/Grid 레이아웃 (예: 'ds-flex fd-row ai-center gap-s') |
| mobileLayout | 모바일 전용 레이아웃 |
| style / mobileStyle | 인라인 스타일 |
| mediaQuery | 반응형 속성 |
| border, padding, margin 등 | Div 공통 |
그룹 자체가 어떻게 정렬될지(가로/세로/gap)는 전적으로 layout 속성에 달려 있습니다.
4. 이벤트
독자 이벤트 없음. 그룹 자체는 이벤트를 dispatch하지 않고, 개별 자식 버튼의 이벤트에 의존합니다.
{
tagName: 'buttonGroup',
tags: [
{
tagName: 'toggleButton',
text: '옵션 1',
onClick: 'onOption1' // ← 각 버튼이 자기 이벤트 처리
},
{
tagName: 'toggleButton',
text: '옵션 2',
onClick: 'onOption2'
}
]
}
⚠️ "어떤 게 선택됐는지" 이벤트가 없음 — Combobox처럼 change(selectedIndex) 같은 통합 이벤트가 그룹에서 발화되지 않습니다. 필요하면 개별 버튼 onClick에서 처리하거나, getChildComponents()로 순회해서 selected/pressed 상태를 조회해야 합니다.
5. 메서드
커스텀 메서드 없음 — Va.Div의 것을 그대로 씁니다.
메서드설명
| append(button) | 오버라이드됨 — 자식 추가 시 자동으로 배타 선택 클릭 핸들러 바인딩 |
| getChildComponents() | 자식 버튼 조회 (Component 공통) |
| removeChild(child) | 자식 제거 |
append의 특별한 동작
동적으로 버튼을 추가할 때도 배타 선택이 유지됩니다 (va_component.js:17521-17536):
onAppend() {
const btn = new Va.Button({ text: '동적 추가' });
this.getRef('refButtonGroup').append(btn);
// ← 여기 시점부터 이 새 버튼도 그룹의 배타 선택에 참여
}
Demo의 onAppend가 정확히 이 패턴을 보여줍니다 → DemoButtonGroup.js:13-23
6. 어떻게 동작하나 (내부 로직)
두 곳에서 배타 선택 핸들러를 바인딩합니다:
mounted() — 초기 자식들 (va_component.js:17504-17520)
mounted(){
let buttons = this.getChildComponents();
for(let i=0; i < buttons.length; i++){
this._bindEvent(buttons[i].element, 'click', (evt) => {
for(let j=0; j < buttons.length; j++){
buttons[j].unselect();
if(buttons[j].tagName == 'toggleButton'){
buttons[j].unpress();
}
}
buttons[i].select();
if(buttons[i].tagName == 'toggleButton'){
buttons[i].press();
}
})
}
}
append(button) — 동적으로 추가되는 자식 (va_component.js:17521-17536)
동일한 로직을 새 버튼에도 붙임.
주목할 점:
- unselect() / select()는 모든 자식에게 호출되지만, 이 메서드가 없는 컴포넌트를 넣으면 크래시합니다. 자식은 반드시 select/unselect를 가진 버튼 계열이어야 합니다.
- toggleButton만 press/unpress가 추가로 호출됨 — 다른 종류 버튼과 섞으면 시각 상태가 서로 어긋날 수 있습니다.
7. 내부 구조
<div elname="element" class="va-div [layout classes]">
<!-- 자식 버튼들이 여기 flex/grid 레이아웃대로 나열됨 -->
<button class="va-button">…</button>
<button class="va-button">…</button>
...
</div>
전용 CSS 클래스 없음 — _beginClassBuild('va-button-group') 같은 호출이 없습니다. CSS만으로 그룹 시각 표현(붙어있는 세그먼트 버튼 같은 look)을 강조하려면 style/layout에 직접 규칙을 넣거나 커스텀 클래스를 추가해야 합니다.
8. 언제 쓰나
ButtonGroup이 맞을 때
- 라디오형 옵션 선택을 버튼 UI로 표현할 때 (텍스트 정렬 L/C/R, 뷰 모드 List/Grid, 기간 필터 Day/Week/Month)
- 세그먼트 컨트롤 (iOS Segmented Control 유사)
- 툴바에서 상호 배타적 모드 전환 (편집/보기/디자인)
- 동적으로 버튼이 추가돼도 배타 선택을 유지해야 하는 상황
다른 걸 쓸 때
- 다중 선택 필터 (여러 개 동시 선택 가능) → 단순 Div 안에 ToggleButton 여러 개, 배타 로직 안 씀
- 단순한 여러 버튼 나열 (선택 개념 없음) → Va.Rows / Va.Div
- 값이 있는 폼 필드 → Va.RadioGroup / Va.RadioGroupField
- 드롭다운 형태의 옵션 선택 → Va.Combobox / Va.SegmentedControl