작성창
채널 · 스레드 · DM 아래에서 메시지를 씁니다. Enter 가 보내기이고 한 · 일 IME 의 조합 Enter 는 보내지 않습니다.
바깥 API 는 마크다운입니다. 멘션은 [@이름](mention:id), 채널은 [#이름](channel:id) 로 오갑니다. 지금 편집 영역은 textarea 이고, 편집기 message 프리셋(./editor)이 생기면 편집 영역만 그것으로 바꿔 끼웁니다(첫 화면 번들에는 넣지 않습니다). 조합 중의 Enter · 목록 고르기 · Esc · @ 판정은 uyoopack 작성창의 동작을 테스트로 옮겨 고정했습니다.
- import
- import { Composer } from "@uyoolabs/ui/chat";
- 쓰는 경우
- 채널 · 스레드 · DM 의 메시지 작성. 스레드 패널의 답글 작성창도 같습니다.
- 쓰지 않는 경우
- 댓글 · 문서처럼 Enter 가 줄바꿈이어야 하는 곳(편집기 comment · document 프리셋), 한 줄 검색칸(SearchResults · Input)에는 쓰지 않습니다. 한도를 넘은 글을 자르지 않습니다.
데모
@ · # · / 를 쳐 보고, 빈 칸에서 ↑ 를 눌러 수정 모드를 엽니다.
키보드
| 키 | 동작 |
|---|---|
| Enter | 보낸다. 빈 내용이면 아무것도 하지 않는다. IME 조합 중이면 확정만(isComposing · keyCode 229) |
| Shift+Enter | 줄바꿈 |
| ⌘+Enter·Ctrl+Enter | 보낸다(enterToSend=false 인 사람을 위해) |
| ↑ | 빈 작성창에서 내 마지막 메시지 수정(onEditLast) |
| Esc | 목록 → 답장 · 수정 → 서식 막대 순서로 하나씩 닫는다. 쓰던 글은 지우지 않는다 |
| @·@·#·/ | 멘션 · 채널 · 명령 목록. @ # 는 줄 처음이나 공백 뒤, / 는 줄 처음에서만 |
| ↑·↓·Enter·Tab | 목록이 열려 있으면 고르기 · 넣기(조합 중 제외). 포커스는 작성창에 남는다 |
| ⌘B·⌘I·⌘E·⌘⇧X | 굵게 · 기울임 · 코드 · 취소선. 선택한 글에 URL 을 붙여넣으면 링크 |
props
소스의 타입과 JSDoc 에서 생성한 표입니다. 컴포넌트를 고치면 pnpm gen 으로 다시 만듭니다.
Composer
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| value | string | — | 마크다운. 멘션 [@이름](mention:id) · 채널 [#이름](channel:id). 제어 |
| defaultValue | string | "" | 비제어 |
| onChange | (markdown: string) => void | — | |
| onSubmit필수 | (markdown: string, attachments: ComposerAttachment[]) => void | — | Enter · Mod+Enter. 빈 내용 · 공백만이면 부르지 않는다. 조합 중에는 부르지 않는다. 보내면 작성창을 비운다 |
| placeholder필수 | string | — | '#디자인 에 메시지 보내기'. 접근성 이름도 된다(채널 이름이 들어가 내장하지 않는다) |
| replyTo | { id: string; author: string; excerpt: string } | — | |
| onCancelReply | () => void | — | |
| editing | { id: string } | — | 수정 모드: 메시지 자리에서 연다. 내용을 다 지우고 저장하면 onRequestDelete |
| onSaveEdit | (markdown: string) => void | — | |
| onCancelEdit | () => void | — | |
| onRequestDelete | () => void | — | |
| onEditLast | () => void | — | 빈 작성창에서 ↑ |
| attachments | ComposerAttachment[] | [] | |
| onAttach | (files: File[]) => void | — | 클립 · 붙여넣기 · 끌어 놓기. 올리기는 앱이 한다 |
| onRemoveAttachment | (id: string) => void | — | |
| onRetryAttachment | (id: string) => void | — | |
| maxLength | number | 4000 | 기본 4000. 95% 부터 글자 수를 보이고, 넘으면 data-invalid + 보내기 막힘(자르지 않는다) |
| mentions | Provider<MentionOption> | — | |
| channels | Provider<ChannelOption> | — | # 자동 완성 |
| commands | SlashCommand[] | — | / 명령(줄 처음에서만) |
| onEmoji | () => void | — | 이모지 단추. 앱이 EmojiPicker 를 연다. 닫히면 focus() 로 돌려보낸다 |
| onTyping | (typing: boolean) => void | — | 3초에 한 번까지만 true. 보내거나 비우면 false |
| draftKey | string | — | 앱이 초안을 저장하는 열쇠. 바뀌면 value · defaultValue 의 초안으로 갈아 끼운다 |
| enterToSend | boolean | true | 기본 true. false 면 Enter 줄바꿈 · Mod+Enter 보내기(사용자 설정) |
| sending | boolean | false | 보내기 단추를 스피너로(앱이 보내는 중) |
| disabled | boolean | false | |
| lockedReason | ReactNode | — | 있으면 작성창 대신 이유 한 줄 + 동작(보관된 채널 · 권한 없음) |
| autoFocus | boolean | true | 처음 그릴 때 포커스(포인터가 정밀한 화면에서만). 기본 true |
| ref | Ref<ComposerHandle> | — | 명령형 조작(React 19 ref prop) |
| lang | "ko" | "en" | "ja" | "ko" | |
| labels | Partial<ComposerLabels> | — | |
| className | string | — |