컴포넌트/툴바

Toolbar (툴바)

VanillaFront 2026. 9. 20. 16:31

Va.Toolbar — 오버플로우 스크롤 지원 툴바 컨테이너

여러 버튼/컴포넌트를 나란히 배치하되, 폭/높이가 부족하면 자동으로 좌우(또는 상하) 스크롤 버튼이 나타나는 툴바입니다. Va.Div를 상속하며 반응형 툴바 UX를 내장합니다.

 

클래스 정보

Va.Toolbar extends Va.Div extends Va.Component
tagName: 'toolbar' (실제 렌더링은 <div>)
isContainer: true

기본 사용법

{
    tagName: 'toolbar',
    direction: 'horizontal',
    tags: [
        { tagName: 'button', text: '조회', icon: 'ico_search' },
        { tagName: 'button', text: '추가', icon: 'ico_add' },
        { tagName: 'button', text: '삭제', icon: 'ico_delete_fill' },
        { tagName: 'button', text: '엑셀', icon: 'ico_download' },
        { tagName: 'button', text: '인쇄', icon: 'ico_print' }
    ]
}

ActionBar와의 결정적 차이

항목Va.ActionBarVa.Toolbar

성격 얇은 flex 컨테이너 오버플로우 스크롤 자동 처리
폭 부족 시 자식이 잘림/줄바꿈 좌우 화살표 버튼 자동 표시
방향 horizontal 위주 horizontal/vertical 모두 지원
정렬 옵션 없음 (직접 spacer) align: 'center' | 'end' 내장
컨트롤 버튼 없음 자동 ← → 버튼
파일 크기 매우 얇음 로직 있음 (~200줄)

요약:

  • 자식이 늘 화면에 들어가는 확실한 상황 → Va.ActionBar
  • 자식 수가 유동적이거나 반응형 화면 → Va.Toolbar

속성

속성타입기본값설명

direction String 'horizontal' 'horizontal' 또는 'vertical'
align String (start) 'center', 'end' — 자식 정렬
controlButtonAppearance String 'transparent' 스크롤 버튼 외형
hideControlButton Boolean false 컨트롤 버튼 강제 숨김
itemMaxSize Number 200(h)/40(v) 아이템 최대 크기
popWidth Number 200 (내부용)
stopPropagation Boolean true 이벤트 전파 차단
appearance String 툴바 외형 (primary 등)

자동 스크롤 동작

horizontal 방향:

  • 자식들이 폭보다 크면 오른쪽에 → 버튼 자동 표시
  • → 클릭 시 내용이 왼쪽으로 밀림
  • 왼쪽으로 밀리면 왼쪽에 ← 버튼 표시
  • 처음/끝에 도달하면 해당 버튼 자동 숨김

vertical 방향:

  • 상하 방향으로 동일 로직 (↑↓ 버튼)

ResizeObserver로 폭 변경 감지 (200ms 디바운스):

  • 창 크기 조정 시 자동으로 컨트롤 버튼 표시/숨김 재계산

이벤트

Va.Div로부터 상속:

  • click — 클릭 시
  • contextmenu — 우클릭 시

메서드

  • Va.Div / Va.Component 공통 메서드
  • 스크롤 제어는 내부 컨트롤 버튼 자동 처리 (별도 API 불필요)

내부 구조

<div class="va-toolbar horizontal">
    <div class="toolbar-inner" elname="inner">
        <div class="toolbar-inner-content" elname="innerContent">
            <div elname="startBlank" style="flex:1"></div>   <!-- align center/end 시 -->
            <div class="content" elname="content">
                <!-- containerElement — 자식 버튼들 -->
                <button>조회</button>
                <button>추가</button>
                ...
            </div>
            <div elname="blank" style="flex:1"></div>
        </div>
        <button cpname="startButton"><!-- ← 스크롤 --></button>
        <button cpname="endButton"><!-- → 스크롤 --></button>
    </div>
</div>

사용 시점 — 언제 쓰나

  • 버튼이 많은 툴바 — 5~20개 액션 버튼, 화면 크기에 따라 잘릴 위험
  • 탭 스타일 툴바 — 항목 많은 카테고리 스위치
  • 반응형 화면 — 데스크톱/태블릿/모바일 모두 대응
  • 세로 사이드 툴바 — direction: 'vertical'
  • 다국어 UI — 언어별로 텍스트 길이 달라 잘릴 위험 있을 때

사용하지 말아야 할 때

  • 자식 개수 확정, 잘릴 일 없음  Va.ActionBar (더 얇음)
  • 제목 + 액션 조합  Va.TitleBar
  • 일반 flex 정렬  Va.Div + layout
  • 탭 인터페이스 (콘텐츠 전환)  Va.Tab (Toolbar는 순수 버튼 나열)

자주 쓰는 조합 예시

기본 가로 툴바 (많은 버튼)

{
    tagName: 'toolbar',
    tags: [
        { tagName: 'button', text: '조회', icon: 'ico_search' },
        { tagName: 'button', text: '추가', icon: 'ico_add' },
        { tagName: 'button', text: '편집', icon: 'ico_pen_fill' },
        { tagName: 'button', text: '삭제', icon: 'ico_delete_fill' },
        { tagName: 'button', text: '엑셀', icon: 'ico_download' },
        { tagName: 'button', text: 'PDF', icon: 'ico_document' },
        { tagName: 'button', text: '인쇄', icon: 'ico_print' },
        { tagName: 'button', text: '이메일', icon: 'ico_mail' },
        { tagName: 'button', text: '공유', icon: 'ico_share' }
        // 화면 좁으면 자동으로 → 버튼으로 스크롤 가능
    ]
}

세로 사이드 툴바

{
    tagName: 'toolbar',
    direction: 'vertical',
    style: { height: '400px', width: '60px' },
    tags: [
        { tagName: 'button', iconOnly: true, icon: 'ico_home' },
        { tagName: 'button', iconOnly: true, icon: 'ico_search' },
        { tagName: 'button', iconOnly: true, icon: 'ico_settings' },
        { tagName: 'button', iconOnly: true, icon: 'ico_profile' },
        { tagName: 'button', iconOnly: true, icon: 'ico_notification' }
    ]
}

중앙 정렬

{
    tagName: 'toolbar',
    align: 'center',
    tags: [
        { tagName: 'button', text: '이전' },
        { tagName: 'button', text: '재생', appearance: 'primary' },
        { tagName: 'button', text: '다음' }
    ]
}

우측 정렬

{
    tagName: 'toolbar',
    align: 'end',
    tags: [
        { tagName: 'button', text: '취소' },
        { tagName: 'button', text: '저장', appearance: 'primary' }
    ]
}

컨트롤 버튼 강제 숨김 (스크롤 필요 없음 확신 시)

{
    tagName: 'toolbar',
    hideControlButton: true,
    tags: [ /* 짧은 목록 */ ]
}

Primary 외형

{
    tagName: 'toolbar',
    appearance: 'primary',
    style: { padding: '10px', borderRadius: 'var(--sizeRadiusM)' },
    tags: [
        { tagName: 'button', text: '저장', appearance: 'primary' },
        { tagName: 'button', text: '전송', appearance: 'primary' }
    ]
}

혼합 컴포넌트 툴바

{
    tagName: 'toolbar',
    tags: [
        { tagName: 'inputField', label: '검색', ref: 'refKeyword' },
        { tagName: 'button', text: '조회', appearance: 'primary' },
        { tagName: 'combobox', items: [ /* 필터 옵션 */ ] },
        { tagName: 'button', text: '새로고침', icon: 'ico_refresh' }
    ]
}

실전 예시 — 문서 편집기 툴바

config(){
    return {
        tagName: 'page',
        tags: [{
            tagName: 'toolbar',
            style: {
                padding: '5px',
                borderBottom: '1px solid var(--colorNeutralStroke)'
            },
            tags: [
                { tagName: 'button', iconOnly: true, icon: 'ico_bold', onClick: 'onClickBold' },
                { tagName: 'button', iconOnly: true, icon: 'ico_italic', onClick: 'onClickItalic' },
                { tagName: 'button', iconOnly: true, icon: 'ico_underline', onClick: 'onClickUnderline' },
                { tagName: 'div', style: { width: '1px', background: 'var(--colorNeutralStroke)', margin: '0 5px' } },
                { tagName: 'button', iconOnly: true, icon: 'ico_align_left', onClick: 'onClickAlignLeft' },
                { tagName: 'button', iconOnly: true, icon: 'ico_align_center', onClick: 'onClickAlignCenter' },
                { tagName: 'button', iconOnly: true, icon: 'ico_align_right', onClick: 'onClickAlignRight' },
                { tagName: 'div', style: { width: '1px', background: 'var(--colorNeutralStroke)', margin: '0 5px' } },
                { tagName: 'button', iconOnly: true, icon: 'ico_link', onClick: 'onClickLink' },
                { tagName: 'button', iconOnly: true, icon: 'ico_image', onClick: 'onClickImage' },
                { tagName: 'button', iconOnly: true, icon: 'ico_table', onClick: 'onClickTable' }
                // 화면 좁으면 자동 스크롤
            ]
        },{
            tagName: 'section',
            style: { flex: 1 },
            tags: [ /* 편집 영역 */ ]
        }]
    };
}

주의사항

  • 컨트롤 버튼 자동 표시 — 자식이 폭을 넘으면 자동으로 ← → 버튼 표시. 예상 못한 UX면 hideControlButton: true
  • ResizeObserver 200ms 디바운스 — 창 크기 변경 시 약간의 지연 후 재계산
  • direction: 'vertical' 사용 시 크기 명시 필수  height를 style로 지정 안 하면 컨트롤 버튼 표시 로직 오동작
  • align: 'start'(기본) — startBlank는 숨김 상태. 'center'/'end'일 때만 활성
  • itemMaxSize 자동 결정 — direction에 따라 200 or 40. 명시 안 하면 기본값 사용
  • appearance primary 사용 시 아이콘 색상 자동 변경  menuIconBackgroundColor 처리
  • Va.Div 상속 — Div의 gridHeader/gridBody 등 사용 가능
  • 컨트롤 버튼 자체는 자식으로 취급 안 됨  startButton/endButton은 innerComponents에 등록되어 자동 관리
  • 모바일 터치 스크롤은 안 됨 — 컨트롤 버튼으로만 스크롤. 모바일 UX면 ScrollView 등 별도 검토

Toolbar vs ActionBar vs TitleBar 비교

항목ToolbarActionBarTitleBar

오버플로우 자동 스크롤
방향 (h/v) 모두 대개 h 대개 h
정렬 옵션 내장
제목 결합 ✅ (Title 자식)
파일 크기 크다 얇다 얇다
주 용도 버튼 많은 툴바 폼 하단 액션 제목+액션 조합

대안 비교

상황추천

버튼 많고 반응형 필요 Va.Toolbar
확정된 몇 개 액션 버튼 Va.ActionBar
제목 + 액션 조합 Va.TitleBar
탭 인터페이스 (콘텐츠 전환) Va.Tab
순수 그루핑 Va.Div

참고