Join | CoUI

Join

여러 요소를 구분자 없이 시각적으로 결합하는 레이아웃 컴포넌트

Join#

버튼·입력 필드 등 여러 요소를 동일한 외곽 반경으로 묶어 하나의 툴바/그룹처럼 보이게 하는 레이아웃 컴포넌트입니다.

Row / Column은 각 자식의 모서리를 그대로 두지만, CoJoin첫 번째 아이템만 시작 측 radius, 마지막 아이템만 끝 측 radius, 중간 아이템은 radius 0으로 clip 합니다. 결과적으로 버튼 그룹의 외곽만 둥글고 내부는 평평하게 이어진 시각적 단위가 됩니다.

보더 두께 자체를 겹치거나 제거하는 처리(border-collapse 동작)는 하지 않습니다. 각 자식은 자신의 보더를 그대로 가진 채 외곽 반경만 통일됩니다.

Live Preview#

사용 시기 (When to Use)#

이 컴포넌트를 사용하세요:

  • 여러 버튼을 하나의 그룹처럼 붙여 툴바를 구성할 때
  • 입력 필드와 버튼을 하나의 검색 바로 결합할 때
  • 서로 연관된 버튼들이 독립적이지 않고 하나의 컨트롤로 인식되어야 할 때

대신 다른 컴포넌트를 사용하세요:

  • Gap: 요소 사이에 여백만 필요할 때
  • Row / Column: 각 자식이 독립된 모서리를 유지해야 할 때. Join은 외곽 반경 clip만 다르다

기본 사용법 (Basic Usage)#

// 수평 버튼 그룹 (기본).
CoJoin(
  children: [
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('1')),
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('2')),
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('3')),
  ],
)

// 수직 결합.
CoJoin(
  vertical: true,
  children: [
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('A')),
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('B')),
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('C')),
  ],
)

Props / Parameters#

속성타입기본값설명
children List<Widget> / List<Component> 필수 결합할 위젯 목록
vertical bool false true이면 수직 결합
borderRadius BorderRadiusGeometry? / double? CoreRadius.field 외곽 모서리 반경 오버라이드

변형 (Variants)#

수평 (기본)#

CoJoin(
  children: [
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('1')),
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('2')),
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('3')),
  ],
)

수직#

CoJoin(
  vertical: true,
  children: [
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('A')),
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('B')),
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('C')),
  ],
)

동작 스펙 (Behavior)#

인터랙션#

  • Join 컨테이너 자체는 인터랙션 없음
  • 각 자식 위젯이 독립적으로 인터랙션 처리
  • 첫 번째/마지막 아이템만 외곽 radius 유지. 중간 아이템은 radius 0으로 이어 붙여진 느낌 제공

상태 전환#

  • Join 컨테이너 자체 상태 없음
  • 자식 버튼의 hover/pressed 상태는 각 버튼이 독립 처리

사용 가이드라인 (Usage Guidelines)#

✅ Do — 의미적으로 연관된 버튼들만 묶기#

CoJoin(
  children: [
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('굵게')),
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('기울임')),
    CoButton(variant: CoreButtonVariant.outline, onPressed: () {}, child: const Text('밑줄')),
  ],
)

❌ Don't — 관련 없는 버튼을 묶지 않기#

CoJoin(
  children: [
    CoButton(variant: CoreButtonVariant.primary, onPressed: () {}, child: const Text('저장')),
    CoButton(variant: CoreButtonVariant.destructive, onPressed: () {}, child: const Text('삭제')),
  ],
)

관련 없는 버튼을 하나로 묶으면 사용자가 기능 관계를 잘못 이해할 수 있습니다.

접근성 (Accessibility)#

키보드 인터랙션#

동작
TabJoin 내 다음 포커스 가능 요소로 이동
Enter / Space포커스된 버튼 활성화

터치 타겟#

  • 각 자식 버튼 최소 터치 타겟: 48x48dp

크로스 플랫폼 차이점 (Platform Differences)#

항목FlutterWeb
클래스명CoJoinCoJoin
방향vertical: boolvertical: bool
borderRadius 타입 BorderRadiusGeometry? double? (px)
렌더링 Row / Column + ClipRRect <div> + inline border-radius + overflow: hidden
  • Gap: 요소 사이에 단순 여백을 추가할 때 사용
  • Divider: 구분선이 필요할 때 사용