활동 피드
누가 · 무엇을 · 언제 했는지 되짚는 읽기 목록입니다. 행은 솟거나 눌리지 않고, 누를 수 있는 것은 문장 안의 대상 링크뿐입니다.
날짜(보는 사람의 시간대)마다 제목과 목록으로 나눕니다. 같은 사람 · 같은 종류가 10분 안에 이어지면 한 줄로 묶고 ‘외 n건 펼치기’로 엽니다. 시각은 1시간 안이면 ‘3분 전’, 그 밖은 ‘14:14’ 이고, 절대 시각(GMT+9 까지)은 툴팁에 있습니다. 사람이 아닌 행위자(AI · 시스템)는 네모 아바타이고, AI 결과에는 ‘AI 생성’ 배지가 늘 붙습니다.
- import
- import { ActivityFeed, type ActivityEntry } from "@uyoolabs/ui/ui";
- 쓰는 경우
- 프로젝트 홈 · 문서 기록 · 멤버 프로필 · 대시보드 '최근 활동' 카드처럼 워크스페이스에서 일어난 일을 되짚는 곳에 씁니다.
- 쓰지 않는 경우
- 읽고 처리해야 하는 것(멘션 · 승인 요청)은 NotificationCenter, 누가 언제 무엇을 바꿨는지 증명하는 기록은 감사 로그 표입니다. 행 전체를 링크로 만들지 않고, 스크롤 끝에서 저절로 더 부르지 않습니다(더 보기 버튼). 인용에 왼쪽 강조선을 쓰지 않습니다.
- 포커스
- [새 활동 n건]을 누르면 새 첫 항목의 첫 링크로, [더 보기] 뒤에는 버튼에 남고 다 불러와 버튼이 사라지면 마지막 새 항목의 첫 링크로, 다시 시도가 성공하면 목록 첫 링크로 갑니다.
- 알림
- 새 활동 수가 0 에서 n 이 될 때 ‘새 활동 n건’, 더 불러오기가 끝나면 ‘활동 n건을 더 불러왔습니다’ 를 role=status 로 한 번 알립니다. 처음 불러오기 실패는 role=alert 입니다. 아바타 · 종류 표시는 장식(aria-hidden)이고 종류는 문장이 말합니다.
ActivityFeed
종류 · 묶음 · 날짜 구분 · AI 행위자.
오늘
김하늘님이 결제 개편 v2 문서에 댓글을 남겼습니다
정산 표의 환불 열을 두 개로 나누면 어떨까요? 부분 환불이 따로 보여야 합니다.박서준님이 PAY-142 환불 API 연결을 진행 중에서 완료로 바꿨습니다
이도윤님이 디자인 리뷰 채널에 파일 3개를 올렸습니다
외 2건 펼치기
어제
AI 비서가 주간 회의 10/21 회의록을 요약했습니다
페이지 · 새 활동 · 상태
데이터와 페이지는 소비자가 쥡니다. 처음 불러오기는 뼈대(300ms 뒤 · 최소 400ms), 실패는 알림 + 다시 시도입니다.
김하늘님이 결제 개편 v2 문서에 댓글을 남겼습니다
정산 표의 환불 열을 두 개로 나누면 어떨까요? 부분 환불이 따로 보여야 합니다.박서준님이 PAY-142 환불 API 연결을 진행 중에서 완료로 바꿨습니다
아직 활동이 없습니다
문서를 만들거나 멤버를 초대하면 여기에 쌓입니다.
김하늘님이 결제 개편 v2 문서에 댓글을 남겼습니다
정산 표의 환불 열을 두 개로 나누면 어떨까요? 부분 환불이 따로 보여야 합니다.
카드 안
compact(아바타 20 · 글자 13) · 날짜 구분 없음.
최근 활동
김하늘님이 결제 개편 v2 문서에 댓글을 남겼습니다
정산 표의 환불 열을 두 개로 나누면 어떨까요? 부분 환불이 따로 보여야 합니다.박서준님이 PAY-142 환불 API 연결을 진행 중에서 완료로 바꿨습니다
키보드
| 키 | 동작 |
|---|---|
| TabShift+Tab | 문장 안 링크 → 그 항목의 묶음 펼치기 → 다음 항목 … → 더 보기 |
| Enter | 링크로 이동합니다 |
| EnterSpace | 묶음을 펼치고 접습니다(<summary> 의 브라우저 기본 동작) |
props
소스의 타입과 JSDoc 에서 생성한 표입니다. 컴포넌트를 고치면 pnpm gen 으로 다시 만듭니다.
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| items필수 | ActivityEntry[] | — | 최신이 먼저 |
| lang | "ko" | "en" | "ja" | "ko" | 기본 "ko" |
| labels | Partial<ActivityFeedLabels> | — | |
| timeZone | string | "Asia/Seoul" | 표시 시간대. 기본 "Asia/Seoul"(Timestamp 와 같다) |
| now | Date | — | 날짜 구분 · 상대 시각의 기준. SSR 과 테스트에서 고정한다 |
| density | "comfortable" | "compact" | "comfortable" | comfortable(아바타 24 · 14/20) · compact(20 · 13/19) |
| groupWindow | number | 600 | 초. 0 이면 묶지 않는다. 기본 600(10분) |
| headingLevel | 2 | 3 | 4 | false | 3 | 날짜 구분 제목 단계. false 면 날짜 구분 없이(카드 안 최근 n건) |
| timeFormat | "time" | "relative" | "relative" | relative(기본, 결정 #22): 1시간 안은 '3분 전', 그 밖은 시각('14:14'). time: 늘 시각. 어느 쪽이든 절대 시각(언어의 긴 꼴 + GMT+9)을 툴팁(title)에 둔다 |
| aria-label | string | — | 목록의 이름. 없으면 labels.list. 카드 안이면 카드 제목을 aria-labelledby 로 잇는다 |
| aria-labelledby | string | — | |
| loading | boolean | false | 처음 불러오기. 뼈대는 loadingDelay 뒤에 나타나 400ms 는 머문다(W1 결정 #35) |
| loadingDelay | number | 300 | 뼈대가 나타나기까지(ms). 빠른 응답에 뼈대가 번쩍이지 않게 한다 |
| error | ReactNode | boolean | — | 처음 불러오기 실패. true 면 labels.error. 목록이 있으면 그리지 않는다 |
| onRetry | () => void | — | |
| hasMore | boolean | false | |
| loadingMore | boolean | false | |
| loadMoreError | boolean | false | 이어 불러오기 실패. 목록은 그대로 두고 끝에 알림(role=alert). 다시 시도는 남아 있는 [더 보기] 다 |
| onLoadMore | () => void | — | [이전 활동 더 보기]. 스크롤 끝에서 저절로 부르지 않는다 |
| newCount | number | 0 | 위에 '새 활동 n건' 버튼. 새 항목을 목록에 끼워 넣지 않는다(읽던 자리가 밀리지 않게) |
| onShowNew | () => void | — | |
| empty | ReactNode | — | 빈 상태를 바꾼다. 기본은 labels.emptyTitle · emptyDesc |
| className | string | — |