데이터 표시
라벨 · 값, 시각, 금액, ID, 지표를 같은 규칙으로 보여 줍니다. 숫자 서식은 lib/format 한 곳에서 합니다.
값이 없으면 빈칸 대신 '—' 를 두고 스크린 리더에는 '없음' 을 읽힙니다. 시각은 늘 UTC 로 받아 보여 줄 때 시간대를 정하고, time 요소의 dateTime 에 기계가 읽는 값을, title 에 언어의 긴 꼴을 둡니다. 금액은 정수 최소 단위로 받고 축약하지 않습니다.
지표는 마크의 옆면 타일을 2px 이음새로 붙이고, 화면에서 가장 중요한 하나만 앞면입니다. 변화량은 화살표 모양 · 부호 · 글자(sr)로 말하고 색은 좋고 나쁨만 거듭니다. 나빠짐에도 빨강을 쓰지 않습니다.
- import
- import { KeyValue, Timestamp, Money, IdValue, CopyButton, Stat, StatGroup } from "@uyoolabs/ui/ui";
- 쓰는 경우
- 설정 · 프로젝트 정보 · 청구서 상세는 KeyValue, 표의 날짜 · 금액 · ID 셀은 Timestamp · Money · IdValue(reveal='hover'), 개요의 KPI 는 StatGroup · Stat 을 씁니다.
- 쓰지 않는 경우
- 앱에서 toLocaleString 을 직접 부르지 않고 formatNumber · formatMoney 를 씁니다. 시간대 없는 시각 문자열을 넘기지 않습니다(개발 중에 경고합니다). 한 지표 묶음에 앞면을 둘 이상 두지 않습니다. 금액이 음수라고 빨강을 쓰지 않습니다(기한 지난 청구만 overdue).
- 서식 import
- import { formatNumber, formatMoney, formatTimestamp, formatRelative } from "@uyoolabs/ui";
KeyValue · IdValue · Money
split(기본) · grid · stacked.
- ID
- prj_01HZX8
- 상태
- 운영 중
- 생성
- 금액
- 290,000원
- 메모
- 없음
Timestamp
datetime · date 는 언어와 상관없이 숫자, short · time · relative 는 언어의 꼴입니다.
- datetime
- date
- short
- time
- relative
Stat · StatGroup
놓인 자리가 560px 보다 좁으면 4 · 3 열이 2 열로 접힙니다.
- 활성 멤버
- 1,284명
- 지난주보다 12.4% 늘었습니다
- 메시지
- 48,210
- 지난 7일 3.1% 늘었습니다
- 회의 시간
- 312시간
- 지난달보다 8% 줄었습니다
- 결제 실패
- 3건
- 어제보다 2건 늘었습니다(나빠짐)
숫자 · 금액 서식
formatNumber · formatMoney 의 기준값입니다. lang 은 필수이고 로캘을 추측하지 않습니다.
| 경우 | ko | en | ja |
|---|---|---|---|
| 1만 이상 축약 | 1.2만 | 12K | 1.2万 |
| 백만 | 123만 | 1.2M | 123万 |
| 1만 미만(축약 안 함) | 9,999 | 9,999 | 9,999 |
| 사람 | 48명 | 48 members | 48人 |
| 시간 | 3시간 12분 | 3 hr 12 min | 3時間12分 |
| KRW | 290,000원 | ₩290,000 | ₩290,000 |
| USD | US$290.00 | $290.00 | $290.00 |
| JPY | JP¥29,000 | ¥29,000 | 29,000円 |
키보드
| 키 | 동작 |
|---|---|
| Tab | 복사 단추 · 링크 지표로 옮깁니다. 표 안에서 숨은 복사 단추도 Tab 순서에 남고 포커스를 받으면 보입니다. |
| EnterSpace | 복사합니다. 성공하면 role=status 로 알리고 포커스는 단추에 남습니다. 막히면 펼친 칸으로 포커스를 옮기고 전체를 고릅니다. |
| Enter | 링크 지표(href)로 상세에 갑니다. |
props
소스의 타입과 JSDoc 에서 생성한 표입니다. 컴포넌트를 고치면 pnpm gen 으로 다시 만듭니다.
KeyValue
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| items필수 | KeyValueItem[] | — | |
| layout | "split" | "grid" | "stacked" | "split" | 기본 split |
| columns | 2 | 3 | 4 | 3 | stacked 의 열. 기본 3 |
| lang | "ko" | "en" | "ja" | — | |
| labels | Partial<ValueLabels> | — | |
| className | string | — |
DefinitionList
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| rows필수 | Array<{ label: string; value: ReactNode }> | — | |
| className | string | — |
Timestamp
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| value필수 | string | Date | null | undefined | — | Z 또는 오프셋이 있는 ISO · Date. 없으면 '—' |
| format | "datetime" | "date" | "short" | "time" | "relative" | — | 기본 datetime. 표 · 로그 · 상세는 datetime · date, 본문은 short · time · relative |
| dateOnly | boolean | — | v1 호환 = format "date" |
| timeZone | string | "Asia/Seoul" | IANA 시간대. 없으면 서울. 사용자의 시간대를 아는 제품이 넘긴다. |
| lang | "ko" | "en" | "ja" | "ko" | short · time · relative · title 의 언어 |
| now | Date | — | relative 의 기준(테스트 · SSR). 없으면 렌더 시각이고 1분마다 갱신한다 |
| title | boolean | true | 절대 시각(언어의 긴 꼴 + GMT+9)을 title 에 둔다 |
| className | string | — |
Money
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| amount필수 | number | bigint | null | undefined | — | |
| currency필수 | string | — | ISO 4217 |
| lang | "ko" | "en" | "ja" | "ko" | |
| overdue | boolean | — | 기한 지남. 오류 글자색 · 500 |
| className | string | — |
IdValue
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| value필수 | string | — | |
| copy | boolean | true | 기본 true |
| truncate | "none" | "middle" | "none" | middle: 앞 10 · 뒤 4 를 남기고 '…'. 전체는 title 과 복사 값 |
| reveal | "always" | "hover" | "always" | 표 안은 hover |
| lang | "ko" | "en" | "ja" | — | |
| labels | Partial<CopyLabels> | — | |
| className | string | — |
CopyButton
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| text필수 | string | (() => string) | — | 누르는 순간 만들어야 하면 함수 |
| label | string | — | 단추 이름(기본 labels.copy) |
| lang | "ko" | "en" | "ja" | — | |
| labels | Partial<CopyLabels> | — | |
| onCopied | () => void | — | |
| reveal | "always" | "hover" | — | 표 안에서 행에 손을 올릴 때만 보인다(IdValue) |
이 밖에 <button> props(children · onClick 제외)를 그대로 받습니다.
StatGroup
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| columns | 1 | 2 | 3 | 4 | 4 | 기본 4 |
| className | string | — | |
| children필수 | ReactNode | — | <Stat> |
Stat
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| label필수 | ReactNode | — | |
| value필수 | ReactNode | null | — | null 이면 '—' + sr labels.empty |
| unit | ReactNode | — | "명" · "시간". 한 · 일은 붙여 쓰고, 영어의 앞 공백은 앱이 넣는다 |
| fullValue | string | — | 축약한 값의 전체("48,210"). title 과 스크린 리더가 읽는다 |
| delta | StatDelta | — | |
| period | ReactNode | — | "지난주보다" · "지난 7일" |
| trend | number[] | — | 작은 추세(장식) |
| emphasis | boolean | — | 앞면. 화면에서 가장 중요한 하나에만 |
| href | string | — | 상세로 가는 지표. 링크 컴포넌트는 LinkProvider 가 준다 |
| asChild | boolean | — | 자식 링크 요소(라우터 Link)를 그대로 쓴다. 자식의 글은 label 로 바뀐다 |
| children | ReactElement | — | |
| loading | boolean | — | 라벨은 진짜 글, 값 · 기간은 뼈대 |
| error | boolean | string | — | 값 자리 '—' + 아래 한 줄. 문자열이면 그 문장 |
| lang | "ko" | "en" | "ja" | — | |
| labels | Partial<StatLabels> | — | |
| className | string | — |
Stats
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| items필수 | Array<{ label: string; value: ReactNode; hint?: string }> | — |