본문으로 건너뛰기

활동 피드

누가 · 무엇을 · 언제 했는지 되짚는 읽기 목록입니다. 행은 솟거나 눌리지 않고, 누를 수 있는 것은 문장 안의 대상 링크뿐입니다.

날짜(보는 사람의 시간대)마다 제목과 목록으로 나눕니다. 같은 사람 · 같은 종류가 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 행위자.

오늘

  1. 김하늘님이 결제 개편 v2 문서에 댓글을 남겼습니다

    정산 표의 환불 열을 두 개로 나누면 어떨까요? 부분 환불이 따로 보여야 합니다.

    #결제-개편

  2. 박서준님이 PAY-142 환불 API 연결을 진행 중에서 완료로 바꿨습니다

  3. 이도윤님이 디자인 리뷰 채널에 파일 3개를 올렸습니다

    외 2건 펼치기
    1. refund-modal.png
    2. 결제-오류-문구.md

어제

  1. AI 비서가 주간 회의 10/21 회의록을 요약했습니다

    AI 생성출처 3개

페이지 · 새 활동 · 상태

데이터와 페이지는 소비자가 쥡니다. 처음 불러오기는 뼈대(300ms 뒤 · 최소 400ms), 실패는 알림 + 다시 시도입니다.

  1. 김하늘님이 결제 개편 v2 문서에 댓글을 남겼습니다

    정산 표의 환불 열을 두 개로 나누면 어떨까요? 부분 환불이 따로 보여야 합니다.

    #결제-개편

  2. 박서준님이 PAY-142 환불 API 연결을 진행 중에서 완료로 바꿨습니다

아직 활동이 없습니다

문서를 만들거나 멤버를 초대하면 여기에 쌓입니다.

  1. 김하늘님이 결제 개편 v2 문서에 댓글을 남겼습니다

    정산 표의 환불 열을 두 개로 나누면 어떨까요? 부분 환불이 따로 보여야 합니다.

    #결제-개편

카드 안

compact(아바타 20 · 글자 13) · 날짜 구분 없음.

최근 활동

  1. 김하늘님이 결제 개편 v2 문서에 댓글을 남겼습니다

    정산 표의 환불 열을 두 개로 나누면 어떨까요? 부분 환불이 따로 보여야 합니다.

    #결제-개편

  2. 박서준님이 PAY-142 환불 API 연결을 진행 중에서 완료로 바꿨습니다

키보드

키동작
TabShift+Tab문장 안 링크 → 그 항목의 묶음 펼치기 → 다음 항목 … → 더 보기
Enter링크로 이동합니다
EnterSpace묶음을 펼치고 접습니다(<summary> 의 브라우저 기본 동작)

props

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

prop타입기본값설명
items필수ActivityEntry[]—최신이 먼저
lang"ko" | "en" | "ja""ko"기본 "ko"
labelsPartial<ActivityFeedLabels>—
timeZonestring"Asia/Seoul"표시 시간대. 기본 "Asia/Seoul"(Timestamp 와 같다)
nowDate—날짜 구분 · 상대 시각의 기준. SSR 과 테스트에서 고정한다
density"comfortable" | "compact""comfortable"comfortable(아바타 24 · 14/20) · compact(20 · 13/19)
groupWindownumber600초. 0 이면 묶지 않는다. 기본 600(10분)
headingLevel2 | 3 | 4 | false3날짜 구분 제목 단계. false 면 날짜 구분 없이(카드 안 최근 n건)
timeFormat"time" | "relative""relative"relative(기본, 결정 #22): 1시간 안은 '3분 전', 그 밖은 시각('14:14'). time: 늘 시각. 어느 쪽이든 절대 시각(언어의 긴 꼴 + GMT+9)을 툴팁(title)에 둔다
aria-labelstring—목록의 이름. 없으면 labels.list. 카드 안이면 카드 제목을 aria-labelledby 로 잇는다
aria-labelledbystring—
loadingbooleanfalse처음 불러오기. 뼈대는 loadingDelay 뒤에 나타나 400ms 는 머문다(W1 결정 #35)
loadingDelaynumber300뼈대가 나타나기까지(ms). 빠른 응답에 뼈대가 번쩍이지 않게 한다
errorReactNode | boolean—처음 불러오기 실패. true 면 labels.error. 목록이 있으면 그리지 않는다
onRetry() => void—
hasMorebooleanfalse
loadingMorebooleanfalse
loadMoreErrorbooleanfalse이어 불러오기 실패. 목록은 그대로 두고 끝에 알림(role=alert). 다시 시도는 남아 있는 [더 보기] 다
onLoadMore() => void—[이전 활동 더 보기]. 스크롤 끝에서 저절로 부르지 않는다
newCountnumber0위에 '새 활동 n건' 버튼. 새 항목을 목록에 끼워 넣지 않는다(읽던 자리가 밀리지 않게)
onShowNew() => void—
emptyReactNode—빈 상태를 바꾼다. 기본은 labels.emptyTitle · emptyDesc
classNamestring—