본문으로 건너뛰기

알림 센터

나에게 온 것(멘션 · 댓글 · 초대 · 승인 요청 · 끝난 작업 · 지난 토스트)을 모아 읽고 처리하는 판입니다.

상단 바의 종에서 열고, 넓은 화면은 팝오버(비모달), 640 미만은 아래 시트(모달)로 뜹니다. 안 읽음은 점 · 굵기 · 진한 글자로 말하고 개수 칩은 빨강을 쓰지 않습니다. 판을 연다고 다 읽음이 되지 않습니다 — 열린 동안 화면에 보인 안 읽은 항목만 판을 닫을 때 읽음으로 알립니다. 열린 동안 온 알림은 목록에 끼워 넣지 않고 ‘새 알림 n개’ 버튼으로 받습니다. 날짜 묶음은 ‘오늘 · 이전’ 둘뿐입니다.

import
import { NotificationBell, NotificationCenter, type NotificationItem } from "@uyoolabs/ui/feedback";
쓰는 경우
상단 바의 알림 판에 씁니다. 토스트로 알린 결과(내보내기 완료 · 업로드 실패)를 다시 볼 곳이기도 합니다(kind: toast).
쓰지 않는 경우
워크스페이스 전체 기록은 ActivityFeed, 방금 한 일의 결과 한 문장은 Toast, 알림 설정은 설정 화면입니다. 알림 안에 수락 · 거절 같은 결정 버튼을 두지 않고(대상 화면에서), 항목 문장 안에 링크를 두지 않습니다(항목 전체가 링크). 개수 칩에 빨강을 쓰지 않습니다.
알림
열린 동안 온 알림 수가 0 에서 n 이 될 때 ‘새 알림 n개’, ‘모두 읽음’ 뒤 ‘모든 알림을 읽음으로 표시했습니다’ 를 role=status 로 한 번 알립니다. 판이 닫혀 있을 때의 개수 변화는 읽지 않습니다(급한 것은 토스트).

NotificationCenter

읽음 · 모두 읽음 · 끄기 · 묶음 · 방해 금지.

종을 누르면 판이 열립니다. 판을 닫을 때 화면에 보인 안 읽은 항목만 읽음이 됩니다.

위 견본의 ‘출시 체크리스트’ 댓글 세 개가 한 항목으로 묶여 있습니다.

NotificationBell

없음 · 안 읽음 3 · 99+ · 방해 금지.

키보드

키동작
EnterSpace종에서 판을 엽니다. 포커스는 고른 거르기 칸으로 갑니다
←→거르기(전체 · 멘션 · 안 읽음)를 고릅니다
TabShift+Tab거르기 → 항목 → 그 항목의 동작 둘(읽음으로 표시 · 알림 끄기) → 다음 항목 … → 바닥
Enter항목의 대상으로 갑니다. 판은 닫힙니다. 한 · 일 조합 중 Enter 는 무시합니다
Esc판을 닫고 포커스를 종으로 돌립니다

props

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

NotificationCenter

prop타입기본값설명
items필수NotificationItem[]—
unreadCount필수number—종 칩 · '안 읽음' 거르기의 수
countsPartial<Record<NotificationFilter, number>>—거르기 칸의 수. unread 가 없으면 unreadCount
filter"all" | "mentions" | "unread"—
defaultFilter"all" | "mentions" | "unread""all"기본 "all"
onFilterChange(filter: NotificationFilter) => void—
openboolean—
defaultOpenbooleanfalse
onOpenChange(open: boolean) => void—
onOpenItem(item: NotificationItem, items: NotificationItem[]) => void—항목을 누르면 링크로 가기 전에 불린다(분석 등). 판은 닫힌다. 묶음이면 묶인 항목 전부를 둘째 인자로
onMarkRead(ids: string[]) => void—읽음 처리. 항목의 [읽음으로 표시]와, 판이 열린 동안 화면에 보인 안 읽은 항목(판을 닫을 때 한 번, 결정 #21)이 부른다. 낙관적으로 바꾸고 실패하면 소비자가 되돌린다
onMarkAllRead() => void—unreadCount 가 0 이면 버튼이 비활성이다
onMute(item: NotificationItem) => void—
settingsHrefstring—
allHrefstring—
loadingbooleanfalse머리 · 거르기는 바로, 목록 자리만 뼈대(loadingDelay 뒤 · 최소 400ms)
loadingDelaynumber300뼈대가 나타나기까지(ms)
errorReactNode | boolean—머리 · 거르기는 남기고 목록 자리에 알림 + 다시 시도. true 면 labels.error
onRetry() => void—
hasMorebooleanfalse
onLoadMore() => void—
pendingCountnumber0열려 있는 동안 온 알림 수. 목록 위 '새 알림 n개' 버튼으로만 받는다(끼워 넣지 않는다)
onShowPending() => void—
dnd{ until?: string | Date; onTurnOff?: () => void } | null—방해 금지. 판 위 안내 띠 + 종은 bell-off 와 조용한 칩
groupWindownumber600묶음 기준 시간(초). 0 이면 묶지 않는다. 기본 600(10분)
renderGroup(group: NotificationGroup) => ReactNode—묶음의 문장. 없으면 가장 최근 알림의 문장 + 메타 '외 n건'
presentation"auto" | "popover" | "sheet""auto""auto"(기본): 640 미만이면 아래 시트(모달), 이상이면 팝오버(비모달)
triggerReactElement—기본 <NotificationBell count={unreadCount} dnd={!!dnd} />. 요소 하나여야 한다(asChild)
lang"ko" | "en" | "ja""ko"
labelsPartial<NotificationCenterLabels>—
timeZonestring"Asia/Seoul"기본 "Asia/Seoul"
nowDate—상대 시각 · 오늘의 기준(테스트 · 스냅샷)

NotificationBell

prop타입기본값설명
countnumber0안 읽은 알림 수. 0 이면 칩이 없다
maxnumber99이보다 크면 칩에 99+
dndbooleanfalse방해 금지: bell-off 아이콘 + 조용한 칩(primary 면을 쓰지 않는다)
lang"ko" | "en" | "ja"—
labelsPartial<NotificationCenterLabels>—

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