Va.SplitButton — 액션 + 드롭다운이 나뉜 분할 버튼
버튼 하나가 두 영역으로 갈라져 있는 컴포넌트입니다. 왼쪽 영역은 즉시 실행되는 주 액션, 오른쪽 작은 화살표 영역은 드롭다운 메뉴 트리거. "저장" 버튼 옆에 "다른 이름으로 저장 / 사본으로 저장…" 같은 대안 액션들을 팝업으로 제공할 때 씁니다.
- 클래스: Va.SplitButton — va_component.js:14959
- short name: splitButton
- 상속: Va.Component
- isContainer: true — 자식으로 menuItem 담음
- 베이스 CSS: va-split-button

1. 기본 사용
{
tagName: 'splitButton',
text: '저장',
icon: 'ico_save',
onClick: 'onSave', // 왼쪽 본체 클릭
onSelect: 'onAlternateSave', // 팝업 항목 선택
tags: [
{ tagName: 'menuItem', key: 'save-as', innerHTML: '다른 이름으로 저장' },
{ tagName: 'menuItem', key: 'copy', innerHTML: '사본으로 저장' },
{ tagName: 'menuItem', key: 'export', innerHTML: '내보내기...' }
]
}
- 본체(text 영역) 클릭 → click 이벤트 (팝업 안 열림, 즉시 액션 실행)
- 오른쪽 화살표 클릭 → menuClick 이벤트 발생 + 팝업 열림
- 팝업 항목 클릭 → select 이벤트
2. Va.MenuButton과의 차이 (가장 헷갈리는 부분)
항목Va.MenuButtonVa.SplitButton
| 버튼 영역 | 1개 (전체가 팝업 트리거) | 2개 (본체 + 드롭다운 트리거) |
| 본체 클릭 시 | 팝업 열림 | 즉시 액션 실행 (팝업 안 열림) |
| 팝업 열기 트리거 | 버튼 어디를 눌러도 | 우측 화살표 영역만 |
| 이벤트 분화 | click / itemClick | click / menuClick / select (3개) |
| 키보드 네비 | Esc/Tab/Enter/↓↑ 완전 지원 | 기본 focus만 (화살표 네비 없음) |
| 팝업 자동 재배치 | IntersectionObserver로 뷰포트 벗어나면 조정 | 최초 위치만 계산 (열림 시 top/left 설정) |
한 줄 요약: MenuButton은 "메뉴만" 여는 버튼, SplitButton은 "기본 액션 + 옵션 팝업"이 하나로 붙은 버튼.
3. 주요 속성
버튼 표현 (Button 계열 공통)
속성설명
| text / innerHTML | 본체 버튼 라벨 |
| icon, iconPosition, iconOnly, iconRotate | 본체 아이콘 (드롭다운 아이콘과 별개) |
| appearance, shape, size | 시각 스타일 (본체 + 드롭다운 트리거 양쪽에 동시 적용) |
| textAlign | 텍스트 정렬 |
| disabled | 비활성화 (두 영역 모두) |
| badge | 배지 부착 |
| stopPropagation | 이벤트 버블링 차단 (기본 true) |
드롭다운 화살표
속성기본값설명
| menuIcon | 'ico_chevron_down' | 우측 화살표 아이콘 |
| menuIconExpand | 'ico_chevron_down' | 확장 상태 아이콘 (실제로는 update에서 chevron_down 하드코딩됨 — 커스터마이즈 유의) |
| menuIconVisible | true | 화살표 표시 여부 |
팝업 제어
속성설명
| expanded | 현재 팝업 열림 여부 |
| popWidth | 팝업 너비. 미지정 시 SplitButton 폭과 동일 |
| expandChildCheck | 자식 없으면 팝업 안 열림 (기본 true) |
4. 이벤트
이벤트발생 시점
| click | 본체 클릭 — 주 액션 트리거 |
| menuClick | 드롭다운 화살표 클릭 — 팝업 열기 트리거 |
| select | 팝업 내 항목 클릭 (onSelect(component, element, listItem, data, folder, evt)) |
| expand | 팝업 열림 |
| collapse | 팝업 닫힘 |
| beforePop / afterPop | 팝업 표시 직전/직후 |
| focus / blur | 포커스 진입/이탈 (본체 or 화살표 어느 쪽이든) |
| contextmenu | 본체 우클릭 |
핵심 3분화: click(본체) · menuClick(트리거) · select(항목). MenuButton에는 click이 곧 "팝업 열기"였지만, SplitButton에서는 click이 명확히 주 액션 실행을 의미합니다.
5. 메서드
메서드설명
| setText(text) / setInnerHTML(html) | 본체 라벨 변경 |
| setIcon(icon) | 본체 아이콘 변경 |
| setAppearance(v) / setShape(v) / setSize(v) | 시각 속성 변경 |
| setDisabled(bool) | 비활성화 |
| expand() | 팝업 강제 열기 (Va.hideOtherComponents(this) 호출 → 다른 팝업 닫고 200ms 지연 후 표시) |
| collapse() / hidePop() | 팝업 닫기 |
| showPop() | 팝업 위치 재계산 후 열기 |
| focus() / blur() | 본체 버튼에 포커스 |
6. 내부 구조
<div elname="element" class="va-split-button">
<inner elname="inner" class="split-button-inner">
<button elname="button" class="va-button" tabindex="0"> ← 본체 (주 액션)
<span elname="content" class="button-inner">
<span elname="icon" class="icon">…</span> ← icon 지정 시
<span elname="text" class="text">라벨</span>
</span>
</button>
<button elname="menuButton" class="menu-button" tabindex="-1"> ← 드롭다운 트리거
<span elname="menuIcon" class="icon menu ico_chevron_down">…</span>
</button>
<span cpname="badge">…</span> ← badge 지정 시
</inner>
</div>
<!-- 팝업은 열릴 때 hiddenArea로 이동 -->
<div elname="pop" class="va-menu-pop" style="position:absolute; display:none">
<div elname="popInner" class="menu-pop-inner">
<!-- menuItem들이 여기 렌더됨 -->
</div>
</div>
**두 개의 <button>**이 나란히 있는 게 이 컴포넌트의 시각적 정체성입니다. 드롭다운 트리거는 tabindex=-1로 기본 탭 순회에서 빠져 있어, 키보드 탭은 본체만 잡습니다.
7. 팝업 위치 로직
MenuButton과 유사하지만 더 단순합니다.
- menuClick 시 → getHiddenAreaElement()로 popElement 이동 → top = 버튼.bottom + 2px, left = 버튼.left, width = 버튼.width (또는 popWidth)
- 뷰포트 넘침 방지 없음 — MenuButton과 달리 위로 뒤집기나 IntersectionObserver 재조정이 없습니다. 화면 하단 근처에서 SplitButton을 열면 팝업이 잘릴 수 있음.
- 오토하이드 — Va.addAutoHide(this.popElement) 등록 → 외부 클릭 시 자동 닫힘 (va_component.js:15052)
8. 항목 선택 흐름
사용자가 팝업의 menuItem 클릭
↓
menuItem이 부모 SplitButton에 'itemClick' dispatch
↓
SplitButton이 'select' 이벤트로 재방출 (onSelect 콜백 호출)
↓
300ms 후 expanded=false, focused=true 처리 + 'collapse' dispatch
↓
본체 버튼에 focus 복귀
select 콜백 시그니처:
onSelect(component, element, listItem, data, folder, evt) {
// listItem: 선택된 menuItem
// data: 항목의 데이터
// folder: 서브메뉴 폴더 여부
}
9. 언제 쓰나
SplitButton이 맞을 때
- 주된 액션 하나 + 부수 액션 몇 개 ("저장" + 다양한 저장 옵션, "실행" + 실행 프로파일 목록)
- 사용자가 대부분 기본 액션을 원하지만, 가끔 다른 선택지가 필요할 때
- IDE 스타일 UI (Run/Debug, Commit/Amend, Deploy/Deploy&Rollback)
- 툴바에서 "즉시 실행"과 "옵션"을 시각적으로 분리하고 싶을 때
다른 걸 쓸 때
- 모든 선택지가 동등한 목록 (주 액션이 없음) → Va.MenuButton
- 기본 액션이 없고 그냥 접기/펼치기 → Va.ExpandButton
- 단일 액션만 필요 → Va.Button
- 여러 액션을 나란히 보여줄 공간이 충분함 → 개별 Va.Button 여러 개
10. 알아두면 좋을 주의사항
- 드롭다운 트리거는 tabindex=-1 — 키보드 사용자는 본체만 탭으로 접근 가능. 화살표 영역은 마우스 전용에 가까움.
- 팝업 뷰포트 벗어남 처리 없음 — 화면 아래쪽에서 열 때 잘림 가능. MenuButton과 달리 자동으로 위로 뒤집지 않음.
- menuIconExpand가 실효 없음 — update()에서 ico_chevron_down이 항상 add되어 열림 상태와 관계없이 동일 아이콘.
- expandChildCheck 기본 true — 자식 menuItem 없이 만들면 트리거를 눌러도 팝업이 안 열립니다. 데이터 지연 로드 시 유의.
- onClick과 onMenuClick 헷갈림 주의 — 본체 클릭은 onClick, 드롭다운 클릭은 onMenuClick. 팝업만 열고 싶은데 onClick에 로직 넣으면 본체를 눌렀을 때 원치 않는 코드가 돕니다.
'컴포넌트 > 버튼' 카테고리의 다른 글
| TabButton (탭버튼) (0) | 2026.09.09 |
|---|---|
| ToggleButton (토글버튼) (0) | 2026.09.09 |
| ResponsiveMenuButton (응답형메뉴버튼) (0) | 2026.09.09 |
| ExpandButton (확장버튼) (0) | 2026.09.09 |
| MenuButton (메뉴버튼) (0) | 2026.09.09 |