본문으로 건너뛰기

반응 · 이모지

메시지 아래의 작은 키캡 칩(반응)과, 반응 추가 · 작성창이 같이 쓰는 이모지 선택기입니다. 이모지는 기기 글꼴로 그립니다.

import
import { Reactions, ReactionChip } from "@uyoolabs/ui/chat";import { EmojiPicker, EmojiPickerPopover, SkinTonePicker, EmojiPickerSkeleton, searchEmoji } from "@uyoolabs/ui/emoji";
peer
radix-ui · emojibase-data(선택 peer, 17). 데이터는 언어마다 수백 KB 라 처음 열 때 lang 하나만 import() 로 받고, 다른 두 언어는 검색을 시작할 때 받습니다. ./chat 도 반응 추가가 ./emoji 를 지연 로드하므로 emojibase-data 를 설치합니다.
쓰는 경우
메시지 · 댓글에 단 이모지 반응. 숫자는 몇 명이, 눌린 모양은 나도 눌렀는지, 툴팁은 누가 눌렀는지를 말합니다. 선택기는 반응 추가(고르면 닫힘)와 작성창의 이모지 단추(keepOpen)에 씁니다.
쓰지 않는 경우
평점 · 투표처럼 결과를 세는 기능에 쓰지 않습니다. 반응 칩을 알약(9999 반경)이나 색 배경만으로 그리지 않습니다. 내 반응에 체크를 붙이지 않습니다(눌린 모양 · primary 경계 · 이름이 말합니다). 이모지를 자체 그림으로 바꿔 기기 이모지와 다르게 하지 않습니다. 칩이 많아도 +n 으로 접지 않고 줄을 바꿔 감쌉니다.

반응

칩 순서는 처음 단 순서 그대로입니다(흔들리지 않게). 내가 마지막으로 끈 칩은 0 이 되면 사라지고, 반응이 하나도 없으면 칩 줄 자체가 없습니다.

비활성(보관된 채널)

키동작
Tab·Shift+Tab칩 사이를 옮깁니다(roving 없음)
Space·Enter내 반응을 켜고 끕니다. 라이브 영역이 '남겼습니다 · 거뒀습니다' 를 한 번 읽습니다
포커스 머묾누가 눌렀나 툴팁이 뜨고 aria-describedby 로도 읽힙니다
Shift+F10onShowAll(전체 목록). 길게 누름 · 보조 클릭과 같습니다
반응 추가에서 Enter선택기를 엽니다. Esc · 고르기로 닫히면 반응 추가 칩으로 돌아갑니다

이모지 선택기

352 × (머리 · 탭 · 격자 252 · 미리보기 48), 9열 · 칸 36. 열면 검색칸에 포커스가 가고, 닫히면 연 단추로 돌아갑니다. 가상 목록 없이 1,900칸 남짓을 두되, 범주를 6줄 덩어리로 나눠 화면 가까이 올 때 그리고 한 번 그린 것은 지우지 않습니다.

팝오버(작성창 · keepOpen)

Shift+Enter · Shift+누르기로 여러 개를 넣습니다(keepOpen)

펼쳐 둔 선택기

고르면 EmojiSelection 이 여기에 나옵니다
키동작
글자(검색칸)결과로 격자를 바꾸고 범주 탭을 숨깁니다
↓검색칸 → 격자 첫 칸
←·→·↑·↓격자 안에서 칸을 옮깁니다. 줄 끝에서 다음 줄로 이어지고, 첫 줄에서 ↑ 는 검색칸입니다
Home·End줄 처음 · 끝. Ctrl 과 함께면 격자 처음 · 끝
PageUp·PageDown한 화면(7줄) 위 · 아래
Enter·Space고릅니다. 반응은 닫히고, keepOpen 이면 Shift 와 함께일 때 열어 둡니다
Enter(검색칸)첫 결과를 고릅니다. IME 조합 중이면 무시합니다
Tab·Shift+Tab검색칸 → 피부색 단추 → 범주 탭 → 격자 순입니다
←·→(범주 탭) 범주를 옮기고 그 범주로 스크롤합니다. 포커스는 탭에 남습니다
Esc피부색 칸이 열려 있으면 그것만, 아니면 선택기를 닫고 연 단추로 돌아갑니다

검색 색인

emojibase-data 의 ko · ja 주석 · 영어 이름 · GitHub 단축어를 한 색인에 넣습니다. lang 과 상관없이 세 언어 검색어가 모두 맞고(하트 · ハート · heart → ❤️), 가타카나와 히라가나를 같게 봅니다. 순서는 최근 사용 → 정확히 맞음 → 앞부분 맞음입니다.

—

내장 문구

lang(ko · en · ja)으로 고르고 labels 로 일부를 바꿉니다(ADR 0011). 이모지 이름은 emojibase 데이터에서 옵니다.

키koenja
chip좋아요 반응 5 개, 내 반응thumbs up, 5 reactions, yoursサムズアップ 5 件、自分のリアクション
who김하린(나), 이도현 외 3명이 👍 로 반응했습니다Harin Kim (you), Dohyun Lee and 3 others reacted with 👍金ハリン(自分)、李ドヒョン ほか 3 人が 👍 でリアクションしました
empty'결제하트'에 일치하는 이모지가 없습니다No emoji match 'kitteh'「ぬこ」に一致する絵文字がありません

props

소스의 타입과 JSDoc 에서 생성한 표입니다. 컴포넌트를 고치면 pnpm gen 으로 다시 만듭니다.

Reactions

prop타입기본값설명
items필수Reaction[]—처음 단 순서. 개수가 바뀌어도 순서를 바꾸지 않는다(흔들리지 않게). 개수 0 인 칩은 그리지 않는다
onToggle필수(emoji: string, next: boolean) => void—칩을 누를 때. 앱이 낙관적으로 바꾸고, 실패하면 되돌리고 토스트를 띄운다
onAdd(emoji: string) => void—반응 추가 칩 → EmojiPicker → 고르면 닫힌다. 없으면 반응 추가 칩을 두지 않는다
onShowAll(emoji: string) => void—칩을 길게 누르거나(500ms) 보조 클릭 · 메뉴 키 — 전체 목록 팝오버를 연다
disabledbooleanfalse보관된 채널 · 읽기 전용 권한. 평평한 칩, 숫자는 읽히고 포커스도 받는다
selfReactionUser—나. 툴팁에서 '김하린(나)' 로 맨 앞에 둔다
recentreadonly string[]—EmojiPicker 최근 사용
skinTone0 | 1 | 2 | 3 | 4 | 5—EmojiPicker 피부색(제어). 앱이 기억한다
onSkinToneChange(tone: SkinTone) => void—
lang"ko" | "en" | "ja""ko"
labelsPartial<ReactionsLabels>—

이 밖에 <div> props(children · role · onToggle 제외)를 그대로 받습니다.

ReactionChip

prop타입기본값설명
emoji필수string—
count필수number—
minebooleanfalsearia-pressed. 체크를 붙이지 않는다(W2~W5 결정 #29) — 눌린 모양 · 경계 · 이름이 말한다

이 밖에 <button> props(children · type 제외)를 그대로 받습니다. 기본값을 바꾼 것은 disabled=false 입니다.

EmojiPicker

prop타입기본값설명
onSelect필수(selection: EmojiSelection) => void—고를 때. 반응은 고르면 닫히고, 작성창(keepOpen)은 Shift 와 함께면 열어 둔다
recentreadonly string[]—최근 사용(최대 9, 맨 앞 한 줄). 저장은 앱이 한다. 없으면 👍 ✅ 👀 🎉 🙏 😂 ❤️ 🔥 💯
skinTone0 | 1 | 2 | 3 | 4 | 5—피부색(제어)
defaultSkinTone0 | 1 | 2 | 3 | 4 | 5—처음 피부색(비제어)
onSkinToneChange(tone: SkinTone) => void—
keepOpenboolean—작성창: true 면 Shift+Enter · Shift+누르기에서 닫지 않는다. 반응은 false
lang"ko" | "en" | "ja"—검색 색인 · 이름 · 범주 이름의 언어. 검색어는 언어와 상관없이 세 언어가 모두 맞는다
labelsPartial<EmojiPickerLabels>—
defaultQuerystring—처음 검색어. 작성창의 : 목록에서 '더 보기'로 열 때 친 글자를 이어 받는다
onClose() => void—Esc · 고르고 닫힐 때. 선택기를 담은 쪽(대화상자 · 패널)이 닫는다
autoFocusbooleantrue그리면 검색칸에 포커스(열면 검색칸). 문서처럼 늘 펼쳐 둔 자리에서는 false

이 밖에 <div> props(children · defaultValue · onSelect · role 제외)를 그대로 받습니다.

EmojiPickerPopover

prop타입기본값설명
onSelect필수(selection: EmojiSelection) => void—고를 때. 반응은 고르면 닫히고, 작성창(keepOpen)은 Shift 와 함께면 열어 둔다
recentreadonly string[]—최근 사용(최대 9, 맨 앞 한 줄). 저장은 앱이 한다. 없으면 👍 ✅ 👀 🎉 🙏 😂 ❤️ 🔥 💯
skinTone0 | 1 | 2 | 3 | 4 | 5—피부색(제어)
defaultSkinTone0 | 1 | 2 | 3 | 4 | 5—처음 피부색(비제어)
onSkinToneChange(tone: SkinTone) => void—
keepOpenboolean—작성창: true 면 Shift+Enter · Shift+누르기에서 닫지 않는다. 반응은 false
lang"ko" | "en" | "ja"—검색 색인 · 이름 · 범주 이름의 언어. 검색어는 언어와 상관없이 세 언어가 모두 맞는다
labelsPartial<EmojiPickerLabels>—
defaultQuerystring—처음 검색어. 작성창의 : 목록에서 '더 보기'로 열 때 친 글자를 이어 받는다
trigger필수ReactElement—여는 단추. asChild 로 받는다(반응 추가 칩 · 작성창 이모지 단추)
tooltipstring—트리거의 이름표 툴팁(아이콘만 있는 단추). 트리거의 aria-label 과 같은 글자다
openboolean—열림(제어)
defaultOpenbooleanfalse처음 열림(비제어)
onOpenChange(open: boolean) => void—
onClose() => void—Esc · 바깥 누름 · 고르고 닫힐 때
side"top" | "bottom""top"기본 top(작성창 위로). 자리가 없으면 반대로 뒤집힌다

SkinTonePicker

prop타입기본값설명
value0 | 1 | 2 | 3 | 4 | 5—고른 피부색(제어)
defaultValue0 | 1 | 2 | 3 | 4 | 50처음 피부색(비제어)
onValueChange(tone: SkinTone) => void—화살표로 옮기거나 누를 때마다
onPick(tone: SkinTone) => void—고르기를 마칠 때(누름 · Enter · Space). 화살표 이동은 고름이지만 마침은 아니다. 선택기는 이때 칸을 닫는다
autoFocusbooleanfalse처음 그릴 때 고른 칸에 포커스를 둔다(선택기 머리에서 열 때)
lang"ko" | "en" | "ja"—
labelsPartial<EmojiPickerLabels>—

이 밖에 <div> props(children · onChange · defaultValue · role 제외)를 그대로 받습니다.

EmojiPickerSkeleton

prop타입기본값설명
lang"ko" | "en" | "ja"—
labelsPartial<EmojiPickerLabels>—

이 밖에 <div> props(children 제외)를 그대로 받습니다.