사람함께에이전트▾
사람 — 직접 정하고 책임지는 비즈니스 규칙과 흐름. 직접 읽어보세요.
함께 — 개념은 알아두고, 세부 규칙은 에이전트가 따릅니다.
에이전트 — 에이전트가 따르는 규칙과 레퍼런스. 필요할 때 찾아보세요.
CLI 레퍼런스▾
AkanJS 레퍼런스▾
오버레이 UI
akanjs/ui의 오버레이 컴포넌트입니다. 모달 창, 확인 팝오버, 바텀 시트, 메뉴, 힌트, 복사 동작을 다룹니다. 먼저 Modal을 쓰고, 배치를 직접 짜야 할 때만 headless Dialog를 조합하세요.이 페이지에서 쓰는 말
용어설명
portal
요소를 DOM의 다른 자리, 여기서는
document.body 끝에 그리는 것입니다. 부모 상자가 잘라내지 못합니다.trigger
사용자가 눌러 오버레이를 여는 요소입니다.
trigger나 children으로 넘깁니다.controlled (제어형)
open을 넘기고 onCancel에서 다시 바꿔 주는 방식입니다. 빼면 컴포넌트가 열림 상태를 직접 관리합니다.오버라이드 슬롯
_overrides.tsx에서 컴포넌트를 라우트 하위 트리 단위로 바꿔 끼울 때 쓰는 이름입니다.scrim
팝오버 뒤에 깔리는 투명한 층입니다. 팝오버 바깥을 누른 클릭을 받아 냅니다.
컴포넌트 고르기
컴포넌트
portal로 그림
document.body
트리거 기준
오버라이드 슬롯
_overrides.tsx
화면을 덮는 창
Modal
✓
✓
제목·본문·하단 슬롯이 있는 가운데 창입니다. 모달이 필요하면 먼저 이것을 씁니다.
Dialog
✓
Modal을 이루는 headless 부품입니다. 배치를 직접 짜거나 에이전트에 이름을 알릴 때 씁니다.BottomSheet
아래 가장자리에서 올라오는 모바일 패널입니다. 제자리에 그려지고 화면에 고정됩니다.
트리거에 붙는 것
Popconfirm
✓
✓
✓
되돌릴 수 없는 동작 앞에 뜨는 작은 확인/취소 팝오버입니다.
Dropdown
✓
✓
✓
목록 행의 동작 같은 짧은 동작 메뉴입니다.
Tooltip
✓
마우스를 올리거나 포커스하면 뜨는 순수 CSS 힌트입니다. 화면 가장자리에서는 옮겨지지 않고 잘립니다.
탐색과 도우미
Menu
✓
items 트리로 만드는 내비게이션 메뉴입니다. 사이드바나 상단 바에 씁니다.Portal
자식을
id로 지정한 호스트 요소 안에 그립니다.Copy
텍스트를 클립보드에 복사하고 성공 토스트를 띄웁니다.
✓해당해당 없음
- portal로 그리는 오버레이는 잘리지 않습니다.
Modal,Dropdown,Popconfirm,Select는document.body에 그려지므로 스크롤되는 모달 본문이나 테이블의 overflow 컨테이너가 잘라내지 못합니다. Portal은 같은 장치에 이름을 붙인 것입니다. body 끝이 아니라id로 고른 호스트 요소 안에 그립니다.Tooltip은 일부러 이 중 어느 것도 하지 않습니다. 순수 CSS라 비용이 거의 없는 대신, 화면 가장자리에서는 옮겨지지 않고 잘립니다.
함께 볼 페이지
Modal
headless
Dialog 위에 만든, 제목·본문·하단 슬롯이 있는 가운데 창입니다. 모달이 필요하면 먼저 이것을 쓰고, 배치를 직접 짜야 할 때만 Dialog를 조합하세요.속성과 API
openboolean
controlled 열림 상태입니다.
trigger를 넘기면 빼도 됩니다.onCancel() => void
모달이 스스로 닫힐 때 호출됩니다. 닫기 버튼, 배경 클릭, Escape가 여기에 해당합니다.
triggerReactNode
모달을 여는 요소입니다. 넘기면 모달이 열림 상태를 직접 관리합니다.
titlestring | ReactNode
맨 위 제목 행입니다. 빼면 제목 행을 그리지 않습니다.
actionReactNode
오른쪽으로 정렬되는 하단 행입니다. 보통 버튼을 넣습니다.
closeButtonReactNode | false
모서리의 닫기 컨트롤입니다. 슬롯이 직접 닫으므로 바꿔 넣은 요소에 핸들러가 필요 없고,
false면 그리지 않습니다.confirmCloseboolean = false
닫기 전에 브라우저 확인 창으로 한 번 더 묻습니다.
className / bodyClassNamestring
창 자체와 스크롤되는 본문에 적용할 class입니다.
- 움직이지 않습니다. 전환 효과도 제스처도 없어서 사용자가 읽는 내용이 흔들리지 않습니다. 이전의 스프링 애니메이션은
LegacyModal에 남아 있으며,open과onCancel이 필수이고trigger와closeButton은 없습니다. - 포커스와 스크롤은 알아서 처리합니다. 열리면 포커스가 창 안으로 옮겨지고 페이지 스크롤이 잠기며, 닫히면 포커스가 원래 자리로 돌아갑니다.
- Modal 오버라이드 슬롯입니다.
_overrides.tsx에서 바꿔 넣으면Model.*이 그리는 것까지 모든<Modal>이 따라갑니다.LegacyModal은 바꿔 넣을 수 없습니다.
사용 예시
Dialog
Modal을 이루는 headless compound 부품입니다. Modal의 정해진 배치가 맞지 않거나, 인페이지 에이전트가 이 창을 열고 닫게 하고 싶을 때 조합합니다.속성과 API
Dialog{ open?, defaultOpen? = false, namespace?, className? }
열림 상태를 쥐는 루트입니다.
open이 바뀌면 그 값을 따라갑니다.namespacestring
인페이지 에이전트에게 이 창의 이름을 알립니다. 없으면 툴을 하나도 공개하지 않습니다.
Dialog.Trigger{ className?, children }
안에 있는 무엇을 눌러도 창이 열립니다.
Dialog.Modal{ onCancel?, confirmClose?, closeButton?, className?, bodyClassName? }
Modal이 그리는 기본 창입니다. Escape, 배경 클릭, 모서리 버튼으로 닫힙니다.Dialog.LegacyModal{ onCancel?, confirmClose?, className?, bodyClassName? }
이전 창입니다. 스프링 애니메이션으로 열리고 닫히며, 터치로 끌어내려 닫을 수 있습니다.
Dialog.Title / Dialog.Action{ children }
쓴 자리에는 아무것도 그리지 않고, 자식을 제목 행과 하단 행으로 넘깁니다.
Dialog.Content{ className?, children }
전체 너비를 차지하는 본문입니다.
- namespace 하나가 이름 셋을 공개합니다.
namespace="share"를 주면 에이전트가openDialogInShare,closeDialogInShare툴과dialogInShare상태를 봅니다. 한 화면에 창이 둘이면 이름을 다르게 주세요. - 에이전트도 사람과 같은 길로 닫습니다. 창 자체의 닫기 동작을 거치므로
confirmClose와onCancel도 그대로 실행됩니다.Modal은namespace를 받지 않아 아무것도 공개하지 않습니다.
사용 예시
Popconfirm
되돌릴 수 없는 동작 앞에 세우는 작은 확인/취소 팝오버입니다. 트리거를 감싸고, 실행할 동작은
onConfirm으로 넘깁니다.속성과 API
titleReactNode
굵게 표시되는 질문입니다.
descriptionReactNode
제목 아래에 붙는 선택적 설명입니다.
onConfirm() => void
사용자가 확인을 누르면 호출됩니다. 팝오버가 먼저 닫힙니다.
okText / cancelTextReactNode
버튼 라벨입니다. 기본값은 사전의
base.ok와 base.cancel입니다.okButtonProps / cancelButtonPropsButtonHTMLAttributes & { loading? }
기본 버튼 두 개에 그대로 펼쳐 넣는 속성입니다.
iconReactNode | false
메시지 옆 아이콘이며 기본값은 경고 아이콘입니다.
false면 그리지 않습니다.actionsReactNode
하단 전체를 바꿉니다. 바꿔 넣으면 확인과 닫기를 모두 직접 처리해야 합니다.
triggerClassName / decoClassNamestring
트리거 래퍼와 말풍선 꼭지에 적용할 class입니다.
decoClassName을 주면 꼭지 위치도 직접 정해야 합니다.- 잘리지 않습니다. 팝오버는
document.body에 portal로 그려지고 트리거의 끝 쪽 아래에 자리 잡습니다. 아래 공간이 모자라면 위로 뒤집히고, 꼭지도 따라갑니다. - 바깥 클릭은 scrim이 받습니다. 바깥을 누르거나 Escape를 누르면 팝오버만 취소됩니다. 이것을 연
Dropdown이나 모달은 열린 채로 남습니다. - 모델 레코드를 지우는 거라면
Model.RemoveWrapper를 쓰세요. 이 팝오버를 그리고 삭제 동작을 에이전트 툴로 공개하는 일까지 해 줍니다.
사용 예시
Dropdown
트리거 버튼 아래로 펼쳐지는 작은 동작 메뉴입니다. 목록의 행 동작, 댓글 메뉴 같은 문맥 동작을 주로 담습니다.
속성과 API
valueReactNode
기본 트리거인 ghost 버튼 안에 들어갈 내용입니다.
triggerReactNode
버튼 대신 쓸 트리거입니다. 복제(clone)되므로 className, onClick, aria-*를 그대로 전달해야 합니다.
contentReactNode
메뉴 행입니다.
<ul> 안에 그려지므로 <li>로 씁니다.align"start" | "end" = "end"
메뉴를 맞출 트리거의 가장자리입니다.
left-0 같은 class로는 바꿀 수 없습니다.namespacestring
인페이지 에이전트에게 이 메뉴의 이름을 알립니다. 없으면 툴을 하나도 공개하지 않습니다.
className / buttonClassName / dropdownClassNamestring
래퍼, 트리거 버튼, 메뉴 패널에 각각 적용할 class입니다.
data-dropdown-keep-openattribute
스위치처럼 자체 동작이 있는 행에 붙이면, 그 행을 눌러도 메뉴가 닫히지 않습니다.
- 잘리지 않습니다. 메뉴는
document.body에 portal로 그려져 트리거 기준으로 배치되므로, 모달, 스크롤되는 모달 본문, 테이블 스크롤 컨테이너가 잘라내지 못합니다. - 행에서 Modal을 열어도 됩니다. 닫힌 메뉴는 unmount되지 않고 숨겨지므로 모달이 그대로 남습니다. 이 메뉴가 연 오버레이 안의 클릭은 바깥 클릭으로 치지 않고, 다른 오버레이는 평소처럼 메뉴를 닫습니다.
- 행을 누르면 메뉴가 닫힙니다. 행에
data-dropdown-keep-open(상수DROPDOWN_KEEP_OPEN_ATTR)이 있으면 예외입니다. 직접 넣은trigger의onClick이 먼저 실행되며, 여기서preventDefault()를 호출하면 메뉴가 열리거나 닫히지 않습니다.
사용 예시
BottomSheet
모바일용 오버레이로, 아래 가장자리에서 올라오는 패널입니다. 거의 모든 것은
type이 정하며, Modal처럼 controlled로도, 자기 trigger로도 동작합니다.속성과 API
type"full" | "half"
필수입니다.
half는 화면 높이의 90%에 손잡이를 그리고, full은 화면 전체를 덮고 닫기 행을 그립니다.open / onCancelboolean / () => void
controlled 상태입니다. 빼면 시트가 상태를 직접 관리하며,
trigger나 ref로 엽니다.triggerReactNode
시트를 여는 요소입니다.
header / handle / closeReactNode
header는 맨 위 행 전체를, handle과 close는 그 안의 표시만 바꿉니다.className / bodyClassNamestring
시트 표면과 스크롤되는 본문에 적용할 class입니다.
refBottomSheetRef
{ open, close } 명령형 핸들입니다. 트리거 없이 시트를 열 때 씁니다.- 닫는 길은 넷입니다.
half시트의 손잡이를 높이의 1/3 넘게 끌어내리기, 배경 누르기, Escape,full시트의 닫기 행입니다. 어느 쪽이든onCancel이 호출됩니다.
사용 예시
Tooltip
마우스를 올리거나 키보드로 포커스하면 뜨는 힌트이며, 순수 CSS로 그립니다. 힌트 전용이라, 반드시 읽혀야 하는 내용은 툴팁에 넣지 마세요.
속성과 API
contentReactNode
힌트 내용입니다. 비어 있거나
null·undefined면 children만 그립니다.childrenReactNode
말풍선이 붙는 트리거입니다.
side"top" | "right" | "bottom" | "left" = "top"
말풍선이 놓일 방향입니다.
variant"default" | "primary" | "info" = "default"
말풍선 색입니다.
Field.Label은 필드 설명 옆 도움말 아이콘에 info를 씁니다.classNamestring
말풍선에 적용할 class입니다.
- 가볍지만 움직이지 않습니다. 상태도 portal도 위치 계산도 없어서 서버가 그린 HTML만으로 동작합니다. 대신 뷰포트 가장자리에서는 말풍선이 뒤집히지 않고 잘립니다.
- 조건부 힌트에 래퍼가 필요 없습니다.
content를 비워 두면 트리거만 그립니다. 말풍선은 마우스를 올리고 300ms 뒤에, 키보드 포커스에는 바로 뜹니다. - 뒤집히거나 포인터를 따라가는 툴팁이 필요하다면
_overrides.tsx에서 직접 만든 것을 연결하세요.Tooltip은 오버라이드 슬롯이라 기존 호출부가 모두 따라옵니다.
사용 예시
Menu
마크업이 아니라 데이터로 만드는 내비게이션 메뉴입니다.
items 트리를 넘기면 행, 하위 메뉴, 활성 상태를 컴포넌트가 그립니다. 사이드바인지 상단 바인지는 mode로 고릅니다.속성과 API
items{ key, label, icon?, children?, type? }[]
MenuItem 트리입니다. children 배열이 있으면 그 행이 하위 메뉴가 됩니다.mode"horizontal" | "inline" = "inline"
사이드바는
inline입니다. 상단 바는 horizontal이며, 넘치는 항목을 … 메뉴로 접습니다.selectedKeys / defaultSelectedKeysstring[]
선택된 항목의 key입니다.
selectedKeys는 controlled 값이고, defaultSelectedKeys는 시작값이며 첫 key만 씁니다.onClick(item: MenuItem) => void
클릭된 항목을 받습니다.
inline에서 하위 메뉴가 있는 행은 펼쳐지기만 합니다.inlineCollapsedboolean
라벨을 숨기고 아이콘만 남깁니다.
activeStyle"bordered" | "active" = "bordered"
활성 행을 표시하는 방식입니다. 아래 테두리 또는
bg-border 배경입니다.renderItem(item, active) => ReactNode
항목 하나의 본문을 직접 그립니다. 행, 클릭, 하위 메뉴는 프레임워크가 계속 맡습니다.
ulClassName / liClassName / labelClassNamestring / string / (isActive) => string
목록, 각 행, 각 라벨에 적용할 class입니다.
className은 바깥 래퍼에 적용됩니다.Menu와Dropdown은 다릅니다.Menu는 내비게이션 구조이고Dropdown은 잠깐 열리는 동작 목록입니다. 겉모습은 비슷해도 서로 대신할 수 없으며, 목록의 행 동작은Dropdown에 넣습니다.
사용 예시
Portal
children을 페이지에 이미 있는 요소 안에 그리며, 그 요소는 id로 지정합니다. Layout.Navbar가 이것으로 동작합니다. 라우트 깊숙한 컴포넌트가 서로를 모른 채 상단 바를 채웁니다.속성과 API
idstring
호스트 요소의
id입니다. 그 요소가 마운트되기 전에는 아무것도 그리지 않습니다.childrenReactNode
호스트 안에 그릴 내용입니다.
- 프레임 슬롯은 Layout 컴포넌트로 채우세요.
Layout.Navbar,Layout.TopInset,Layout.TopLeftAction,Layout.BottomInset이 CSR 빌드에서도 맞는 호스트를 고르고,TopLeftAction을 뺀 나머지는 슬롯 높이까지 잡아 줍니다. - 프레임 슬롯은 첫 HTML에 들어 있습니다. SSR 중에 내용을 바로 써 넣으므로 hydration이 끝난 뒤에야 나타나는 일이 없습니다. 직접 만든 호스트는 브라우저에서 채워집니다.
- 잘리는 부모에서 빠져나오는 수단이 아닙니다.
Modal은 이미document.body에 portal로 그려지고,Dropdown,Popconfirm,Select는 여기에 더해 트리거 기준으로 자리를 잡습니다.
사용 예시
Copy
트리거를 감싸서, 누르면
text를 클립보드에 복사하고 성공 토스트를 띄웁니다.속성과 API
textstring = ""
클립보드에 복사할 텍스트입니다.
copyMessagestring
성공 토스트 문구입니다. 기본값은 언어에 맞춘 "복사되었습니다"입니다.
childrenReactNode
트리거입니다. 요소라면 원래
onClick이 유지되고, 복사보다 먼저 실행됩니다.- Clipboard API가 없어도 동작합니다.
navigator.clipboard가 없으면 숨긴textarea로 복사합니다. 토스트는 store의showMessage로 띄웁니다.
사용 예시