@hjmds/design-contracts 1.48.0 → 1.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/card.d.ts +5 -0
- package/dist/card.d.ts.map +1 -1
- package/dist/card.js +3 -0
- package/dist/card.js.map +1 -1
- package/dist/catalog.d.ts +34 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +4 -0
- package/dist/catalog.js.map +1 -1
- package/dist/drawing-editor.d.ts +96 -0
- package/dist/drawing-editor.d.ts.map +1 -0
- package/dist/drawing-editor.js +43 -0
- package/dist/drawing-editor.js.map +1 -0
- package/dist/timeline.d.ts +7 -0
- package/dist/timeline.d.ts.map +1 -1
- package/dist/timeline.js +7 -0
- package/dist/timeline.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/drawing-editor.md +14 -0
- package/docs/generated/component-maturity.md +5 -4
- package/docs/generated/renderer-evidence.json +109 -9
- package/docs/generated/renderer-evidence.md +5 -3
- package/docs/generated/showcase-manifest.json +52 -1
- package/docs/timeline.md +13 -0
- package/docs/usage/README.md +2 -1
- package/docs/usage/components/card.md +1 -0
- package/docs/usage/components/drawing-editor.md +133 -0
- package/docs/usage/components/image-choice-group.md +1 -1
- package/docs/usage/components/sheet.md +13 -1
- package/docs/usage/components/timeline.md +27 -3
- package/package.json +6 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# DrawingEditor
|
|
2
|
+
|
|
3
|
+
- 단계: 컴포넌트
|
|
4
|
+
- 상태: 실험
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 미게시(1.48.0 이후)
|
|
7
|
+
- 검토일: 2026-10-27
|
|
8
|
+
- 근거: 다에리와 스핀트의 프로필 손그림 편집기가 복제돼 도구·이력·레이아웃이 갈라지는 것을 막으라는 사용자 요청
|
|
9
|
+
- 스토리북: `실험/컴포넌트/입력/손그림 편집기`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
펜·지우개로 그림을 그리고 이미지로 저장하는 전체 편집 화면에 쓴다. 프로필 사진뿐 아니라 사각 그림에도 사용할 수 있다.
|
|
14
|
+
도구 배치·그리기 상태·실행 취소·다시 실행·전체 지우기·펜 굵기·최근 색·배경·저장 진행·실패·닫기 확인을 공통으로 제공한다.
|
|
15
|
+
사진 선택·자르기에는 제품 이미지 피커를 사용한다. 문서 필기 인식이나 필압·벡터 파일 편집은 제공하지 않는다.
|
|
16
|
+
|
|
17
|
+
## 쓰지 않을 때
|
|
18
|
+
|
|
19
|
+
| 상황 | 대신 쓸 것 |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| 사진을 고르거나 촬영 | 제품 이미지 피커 |
|
|
22
|
+
| 색상 하나만 선택 | [ColorPicker](color-picker.md) |
|
|
23
|
+
| 일반 텍스트 편집 | [EditorScreen](editor-screen.md) |
|
|
24
|
+
|
|
25
|
+
## 공개 이름과 import
|
|
26
|
+
|
|
27
|
+
| 이름 | 역할 | Web | Native |
|
|
28
|
+
| --- | --- | --- | --- |
|
|
29
|
+
| `DrawingEditor` | 전체 편집 화면 | `@hjmds/react/drawing-editor` | `@hjmds/react-native/drawing-editor` |
|
|
30
|
+
| `DrawingEditorProps`, `DrawingEditorLabels`, `DrawingImage` | 공통 타입 | 같은 경로 | 같은 경로 |
|
|
31
|
+
| `drawingEditorRecipe`, `drawingReducer`, `drawingInkPath` | 배치·이력·선 생성 계약 | `@hjmds/design-contracts/components/drawing-editor` | 같음 |
|
|
32
|
+
|
|
33
|
+
루트 barrel로 내보내지 않는다. Web는 기존 optional `lucide-react`, Native는 `react-native-svg`와 `lucide-react-native` peer가 필요하다.
|
|
34
|
+
새 라이브러리는 추가하지 않는다. 기존 ColorPicker와 EditorScreen을 합성하며 별도 색상 피커나 화면 셸을 만들지 않는다.
|
|
35
|
+
|
|
36
|
+
## 최소 사용 예
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
// Native
|
|
40
|
+
import { DrawingEditor } from '@hjmds/react-native/drawing-editor';
|
|
41
|
+
|
|
42
|
+
<DrawingEditor
|
|
43
|
+
labels={localizedLabels}
|
|
44
|
+
cropPreview="circle"
|
|
45
|
+
safeAreaBottom={insets.bottom}
|
|
46
|
+
onCancel={close}
|
|
47
|
+
onSave={async image => {
|
|
48
|
+
// 제품이 계정 세대를 검증하고 서버가 받는 형식으로 변환한다.
|
|
49
|
+
await uploadProfileImage(image);
|
|
50
|
+
close();
|
|
51
|
+
}}
|
|
52
|
+
/>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
// Web
|
|
57
|
+
import { DrawingEditor } from '@hjmds/react/drawing-editor';
|
|
58
|
+
|
|
59
|
+
<DrawingEditor labels={localizedLabels} cropPreview="circle"
|
|
60
|
+
onSave={uploadAndClose} onCancel={close} />
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 축과 기본값
|
|
64
|
+
|
|
65
|
+
| prop | 값 | 기본값 | 설명 |
|
|
66
|
+
| --- | --- | --- | --- |
|
|
67
|
+
| labels | `DrawingEditorLabels` | 필수 | 모든 도구·오류·닫기 확인 문구 |
|
|
68
|
+
| onSave | `(image: DrawingImage) => Promise<void>` | 필수 | 이미지 업로드 성공/실패를 제품이 확정 |
|
|
69
|
+
| onCancel | `() => void` | 필수 | 변경 취소 확인 후 나가기 |
|
|
70
|
+
| cropPreview | `circle`, `none` | `none` | 출력에 포함하지 않는 원형 잘림 안내 |
|
|
71
|
+
| safeAreaBottom | number | 0 | Native host의 아래 안전 영역 |
|
|
72
|
+
| testID | string | `drawing-editor` | 테스트 식별자와 캔버스 접두사 |
|
|
73
|
+
|
|
74
|
+
## 배치
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
EditorScreen: 안전 영역 + 닫기/제목
|
|
78
|
+
되돌리기·다시 실행 배경·전체 지우기
|
|
79
|
+
정사각 캔버스(원형 잘림 안내 선택)
|
|
80
|
+
펜 / 지우개
|
|
81
|
+
굵기
|
|
82
|
+
빠른 색상 스트립 · 더 많은 색
|
|
83
|
+
EditorScreen: 저장 footer + 아래 안전 영역
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
| 항목 | 값 | 근거 |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| 크기 | 캔버스 최대 400, Native 가용 영역 안 정사각 | `drawingEditorRecipe.maxCanvas` |
|
|
89
|
+
| 간격 | Native 도구 묶음 12, Web Stack md | 기존 양 표면의 편집기 배치 보존 |
|
|
90
|
+
| 순서·정렬 | 이력 → 캔버스 → 도구 → 저장 | 원형 예고는 캔버스 위 오버레이 |
|
|
91
|
+
| 고정·스크롤 | Native 본문 비스크롤, 저장 footer | 세로 획을 부모 스크롤에서 보호 |
|
|
92
|
+
| 좁은 폭·큰 글자 | 가용 폭 안에서 캔버스 축소, 색상은 가로 스크롤 | 최대 글자 조건은 전사 지침대로 제외 |
|
|
93
|
+
|
|
94
|
+
## 꼭 지킬 것
|
|
95
|
+
|
|
96
|
+
- 그림 도구·이력을 제품에서 다시 조립하지 않는다.
|
|
97
|
+
- 저장 실패는 reject하고 성공 후 화면 이동은 제품이 수행한다.
|
|
98
|
+
- 업로드 전 계정 세대와 이미지 입력 한도를 검사한다.
|
|
99
|
+
|
|
100
|
+
## 상태와 제품 경계
|
|
101
|
+
|
|
102
|
+
`labels`는 모든 문구를 요구한다. 제품은 번역·계정·업로드·형식 변환·성공 후 탐색만 연결한다.
|
|
103
|
+
`onSave`가 reject되면 오류를 보이고 그림과 이력을 보존한다. 성공을 HJM이 추정해 화면을 닫지 않는다.
|
|
104
|
+
저장 중 반복 저장과 편집을 막으며 비어 있는 기본 배경은 저장할 수 없다. 배경만 바꾼 그림은 저장 가능하다.
|
|
105
|
+
이미지는 Native SVG의 PNG 또는 Web Canvas의 JPEG base64와 실제 `mimeType`으로 전달된다. 크기·압축·업로드 한도는 제품 계약에 맞춘다.
|
|
106
|
+
계정이 바뀌는 동안 이미지 추출이 끝날 수 있으므로 소비자는 렌더 시점의 계정 세대를 캡처해 업로드 전에 다시 검증한다.
|
|
107
|
+
|
|
108
|
+
## 레이아웃과 상호작용
|
|
109
|
+
|
|
110
|
+
상단 이력·배경·지우기 → 정사각 캔버스 → 펜/지우개 → 굵기 → 색상 스트립 순서다. 저장과 닫기는 EditorScreen이 소유한다.
|
|
111
|
+
Native는 부모 스크롤이 세로 획을 가져가지 않도록 `scroll="content"`를 사용하고 실제 가용 영역 안에서 캔버스 크기를 정한다.
|
|
112
|
+
Web는 pointer capture와 `touch-action: none`을 사용한다. 첫 터치/포인터만 획을 소유한다.
|
|
113
|
+
`cropPreview="circle"`은 잘릴 모서리를 표시하지만 출력 이미지에는 포함하지 않는다. 기본값 `none`은 사각 그림이다.
|
|
114
|
+
펜과 지우개의 실제 굵기는 슬라이더 값과 같다. 배경 변경 후 지우개 자국도 새 배경을 따른다.
|
|
115
|
+
|
|
116
|
+
## 확인 범위와 소비 이관
|
|
117
|
+
|
|
118
|
+
다에리 Native·Web, 스핀트 Native가 이 컴포넌트를 소비한다. 제품의 중복 캔버스·도구·이력 구현과 그 전용 계약 테스트는 제거하고
|
|
119
|
+
공통 계약/renderer 테스트로 옮긴다. 제품 테스트는 계정·업로드 실패·탐색 연결을 계속 확인한다.
|
|
120
|
+
소스 연결 개발에서만 제공되는 후보이며 npm 게시·스토어 반영·실기기 검증과 구분한다.
|
|
121
|
+
|
|
122
|
+
## 플랫폼 차이
|
|
123
|
+
|
|
124
|
+
| 항목 | Web | Native |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| 획 입력 | pointer capture | 단일 responder 소유 |
|
|
127
|
+
| 이미지 | Canvas JPEG | SVG PNG |
|
|
128
|
+
| 주변 스크롤 | touch-action none | 비스크롤 본문 |
|
|
129
|
+
|
|
130
|
+
## 근거
|
|
131
|
+
|
|
132
|
+
- 공통 소유권: 2026-10-27 사용자 지시로 다에리·스핀트의 복제 편집기를 HJM으로 옮겼다.
|
|
133
|
+
- 획 소유권과 스크롤: 기존 다에리의 두 번째 손가락·세로 획 결함 대응을 보존한다.
|
|
@@ -69,7 +69,7 @@ import { ImageChoiceGroup } from "@hjmds/react-native/image-choice";
|
|
|
69
69
|
| 크기 | 그림 72 정사각, 바깥 고리 2 + 간격 2(터치 영역 80), radius `radius.md` 12, 체크 원 20 | `imageChoiceRecipe` |
|
|
70
70
|
| 간격 | 타일 사이 `spacing.xs` 8(가로·세로), 라벨–타일 8, 그림–항목 라벨 `spacing.xxs` 4. 이름과 설명 사이 0 | `imageChoiceRecipe.gap`, `itemLabelGap` |
|
|
71
71
|
| 글자 칸 | 이름 줄(`itemLabelLines`) + 설명 줄(묶음에 설명이 있으면 1)을 caption 행간(16 × 글자 배율)으로 모든 타일이 같은 높이로 비워 둔다. 타일 높이 = 80 + 4 + 줄 수 × 행간(글자 칸이 없으면 80) | `resolveImageChoiceCaptionLines`, `resolveImageChoiceTileHeight` |
|
|
72
|
-
| 순서·정렬 | `items` 순서대로 시작 쪽부터 줄바꿈
|
|
72
|
+
| 순서·정렬 | `items` 순서대로 시작 쪽부터 줄바꿈 배치. 항목이 적어도 화면 폭에 맞는 빈 칸을 유지해 다음 항목이 중간 칸부터 채워진다(2026-10-27 Diairy 양끝 벌어짐 수정). 체크는 타일 끝 쪽 위 모서리 | 두 렌더러 |
|
|
73
73
|
| 고정·스크롤 | 기본은 자체 스크롤 없음(바깥 목록·화면이 스크롤). `virtualized`면 묶음이 `height` 높이로 스스로 스크롤한다. Web은 포커스 테두리가 잘리지 않게 안쪽 여백 4(`virtualViewportInset`), 열 수 = ⌊(폭 + 8) / 88⌋(최소 1) | 두 렌더러, `resolveImageChoiceColumns` |
|
|
74
74
|
| 좁은 폭·큰 글자 | 남은 폭에서 줄바꿈, 타일 크기는 유지 | flex wrap |
|
|
75
75
|
|
|
@@ -112,6 +112,18 @@ import { Sheet } from "@hjmds/react-native/overlays";
|
|
|
112
112
|
|
|
113
113
|
개발 소스·미게시: `size="small"`은 가용 높이의 40%로, `medium`(60%) 높이의 2/3이다. 짧은 안내에 내용 높이만 쓰면 지나치게 작고 medium은 빈 공간이 큰 경우 선택한다. Web·Native 공통 recipe가 크기를 소유한다.
|
|
114
114
|
|
|
115
|
+
### 크기 고르기 (2026-10-27)
|
|
116
|
+
|
|
117
|
+
2026-10-27 포트폴리오 전 앱 하단 시트 조사에서 고정 크기 16곳 중 6곳의 높이가 내용보다 과했다.
|
|
118
|
+
연도 선택기 하나뿐인 필터가 85%로 열렸고, 문단 하나짜리 안내가 60%였다. 사유 6개짜리 신고·몇 줄짜리 알림 설정·몇 개 안 되는 기록 목록도 85%로 열렸다.
|
|
119
|
+
그래서 크기는 아래 순서로 고른다.
|
|
120
|
+
|
|
121
|
+
- **기본은 `auto`다.** 내용이 정해진 보조 작업은 내용 높이로 연다. 신고, 행 몇 개짜리 설정, 확인, 조건 몇 개짜리 필터, 길이가 바뀌는 선택 목록이 여기에 든다. 길어지면 90%에서 멈추고 본문이 스크롤한다.
|
|
122
|
+
- **고정 크기(`medium`·`large`·`full`)는 본문이 남은 높이를 채워야 할 때만 쓴다.** 자기 스크롤과 상태 영역을 가진 검색·목록 화면(SearchScreen 등), 그리기처럼 화면을 쓰는 작업이 여기에 든다. 고르는 조건에 따라 구획이 나타나고 사라져 편집 중 높이가 흔들리는 폼도 고정 크기를 쓴다.
|
|
123
|
+
- **짧은 안내 문단에는 `medium` 대신 `small`(또는 `auto`)을 쓴다.**
|
|
124
|
+
- **화면 틀을 시트에 넣고 고정 크기로 채우지 않는다.** 시트 안 신고는 `ModerationScreen` 대신 [ModerationForm](moderation-screen.md)을 쓰고 `auto`로 연다. 화면 틀은 높이를 채워야 해서 시트를 large로 키우게 된다.
|
|
125
|
+
- **제품의 공통 시트 wrapper는 모든 사용처에 같은 고정 크기를 강제하지 않는다.** 기본은 `auto`로 두고, 위 고정 크기 조건에 맞는 사용처만 크기를 넘긴다.
|
|
126
|
+
|
|
115
127
|
Native 스크롤 본문은 viewport 높이만큼 content를 채운다(미게시). 고정 크기 시트의 빈/오류/로딩 상태는 `CompositionLayout layoutStyle={{ flex: 1 }}`로 연결해 손잡이·머리·footer를 제외한 본문 중앙에 둔다. 목록이 길면 같은 본문에서 스크롤한다.
|
|
116
128
|
|
|
117
129
|
선택형 Native `GestureSheetInput`의 실제 keyboard-tracking host에도 제품 `tokens.fontFamily.ui`가 이어진다. optional library·폰트 설치와 실제 기기 검증은 제품이 맡는다.
|
|
@@ -122,7 +134,7 @@ Native 스크롤 본문은 viewport 높이만큼 content를 채운다(미게시)
|
|
|
122
134
|
| --- | --- | --- |
|
|
123
135
|
| 크기 | 하단 시트 높이: `auto` 내용 높이(최대 화면 높이 × `maxHeightRatio` 0.9), `medium` 60%, `large` 85%, `full` 위 safe area 안 전체 높이(0.9 상한 없음). Web 하단 시트 최대 폭 640. 위 두 모서리만 `radius.xl` 24. 옆 시트(`start`·`end`): Web 폭 `min(28rem, 100%)`·높이 화면 − 32, Native 폭 88%(최대 420)·화면 높이 | `sheetRecipe.sizes`·`content`·`web`, `.hjm-sheet`, Native `Sheet` |
|
|
124
136
|
| 간격 | 두 플랫폼 같은 값: 좌우 `content.paddingHorizontal`(`spacing.lg` 20), 위·아래 `content.paddingTop/Bottom`(`spacing.sm` 12, 하단 시트는 아래 safe area를 더함), 머리·본문·footer 사이 `body.gap`(`spacing.md` 16), footer 위 `footer.paddingTop`(`spacing.sm` 12), 제목–닫기 `header.gap`(`spacing.sm` 12), footer 버튼 사이 `footer.gap`(`spacing.xs` 8) | `.hjm-sheet__*`, `sheetRecipe.content`·`body`·`footer` |
|
|
125
|
-
| 순서·정렬 | 손잡이(하단) → 머리(설명 + 닫기, 둘 다 없으면 그리지 않음) → 본문 → `footer`. 보이는 제목 줄은 없다(미게시). 머리 최소 높이 44(`control.minTouchTarget`), ×는 설명이 없어도 끝 쪽 같은 자리다. 기본 하단 시트는 머리가 없어 본문이 손잡이 영역 바로 아래 `body.gap` 16에서 시작한다. Web footer는 오른쪽 정렬 가로 줄 [보조][주]. Native footer는 세로 열이라 꽉 찬 폭 버튼을 주 행동 먼저 둔다. 하단 시트는 항상 위 가운데 36×4 손잡이(터치 최소 폭·높이44)를 둔다. `detents`가 있으면 높이 전환 버튼, 없고 ×가 없으면 `closeLabel` 이름의 닫기 버튼이다. `showClose: true`로 ×를 표시하면 손잡이는 장식으로 남겨 같은 닫기 버튼이 중복되지 않는다(미게시). 손잡이·머리 영역을 아래로 끌어 `sheetDragRecipe.dismissDistanceRatio`(시트 높이의 25%) 또는 `dismissVelocity` 0.5px/ms를 넘기면 `swipe`로 닫고, 아니면 제자리로 돌아간다(미게시, Web·Native 동일) | `sheetRecipe.header`·`handle`, `.hjm-sheet__footer`, `.hjm-sheet__handle` |
|
|
137
|
+
| 순서·정렬 | 손잡이(하단) → 머리(설명 + 닫기, 둘 다 없으면 그리지 않음) → 본문 → `footer`. 보이는 제목 줄은 없다(미게시). 머리 최소 높이 44(`control.minTouchTarget`), ×는 설명이 없어도 끝 쪽 같은 자리다. 기본 하단 시트는 머리가 없어 본문이 손잡이 영역 바로 아래 `body.gap` 16에서 시작한다. Web footer는 오른쪽 정렬 가로 줄 [보조][주]. Native footer는 세로 열이라 꽉 찬 폭 버튼을 주 행동 먼저 둔다. 하단 시트는 항상 위 가운데 36×4 손잡이(터치 최소 폭·높이44)를 둔다. `detents`가 있으면 높이 전환 버튼, 없고 ×가 없으면 `closeLabel` 이름의 닫기 버튼이다. `showClose: true`로 ×를 표시하면 손잡이는 장식으로 남겨 같은 닫기 버튼이 중복되지 않는다(미게시). 손잡이·머리 영역을 아래로 끌어 `sheetDragRecipe.dismissDistanceRatio`(시트 높이의 25%) 또는 `dismissVelocity` 0.5px/ms를 넘기면 `swipe`로 닫고, 아니면 제자리로 돌아간다(미게시, Web·Native 동일). 본문도 같은 끌기로 닫는다(2026-10-27 사용자 결정, 미게시). 스크롤 본문은 이미 맨 위일 때 시작한 아래 끌기만 시트가 가져가고, 스크롤된 상태에서는 같은 동작이 먼저 내용을 위로 되돌린다. iOS는 UIScrollView가 JS 응답자 아래서도 스스로 끌기를 가져가므로 본문 맨 위의 당김(overscroll)으로 시트를 움직이고 UIKit 탄성 거리로 환산한 같은 임계값을 쓴다. Web 본문 끌기는 터치만 받고 마우스는 글자 선택을 위해 손잡이를 쓴다. 입력칸에서 시작한 끌기와 가로 끌기는 본문 안의 행동으로 남긴다 | `sheetRecipe.header`·`handle`, `.hjm-sheet__footer`, `.hjm-sheet__handle` |
|
|
126
138
|
| 고정·스크롤 | 본문만 스크롤, 머리·`footer` 고정(Native는 `scrollable`일 때 ScrollView). 스크롤 영역은 시트 좌우 여백(`sheetRecipe.content.paddingHorizontal` 20) 끝까지 넓고 여백은 스크롤 내용 안쪽에 둔다(Web `.hjm-sheet__body` padding, Native ScrollView `marginHorizontal: -20` + 내용 `paddingHorizontal: 20`). 그래서 본문 ListRow의 8 표면이 잘리지 않고 내용 위치는 그대로다. 고정 높이(`medium`·`large`·`full`, 옆 시트)에서는 본문이 머리·footer를 뺀 남은 높이를 차지하고 `footer`는 시트 아래에 붙는다. 그래서 본문의 `flex: 1` 자식(SearchScreen, 목록 화면)이 그 높이를 채운다(Web `.hjm-sheet__body` `flex: 1 1 auto`, Native 본문 `flexGrow: 1`·`minHeight: 0`, 1.13.1 변경). `auto`는 내용 높이 그대로다. 저장·확인은 `footer`에. 아래 안전 영역은 시트가 자기 여백에 더한다(`spacing.sm` 12 + 아래 inset, Web은 `env(safe-area-inset-bottom)`). 위 inset은 시트가 올라갈 높이를 줄인다. 제품이 inset을 다시 더하지 않는다 | `.hjm-sheet__body`, `sheetRecipe.safeArea`, Native `Sheet` |
|
|
127
139
|
| 좁은 폭·큰 글자 | 폭 < 640이면 하단 시트가 화면 폭을 채운다. Web footer는 줄을 바꾼다. 큰 글자로 내용이 길어지면 `auto`는 최대 90%에서 멈추고 본문이 스크롤된다(`full`은 위 safe area까지) | `.hjm-sheet[data-placement="bottom"]`, `maxHeightRatio` |
|
|
128
140
|
|
|
@@ -4,15 +4,32 @@
|
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
6
|
- 적용: 1.12.1
|
|
7
|
-
- 검토일: 2026-10-
|
|
7
|
+
- 검토일: 2026-10-27
|
|
8
8
|
- 근거: [Timeline](../../timeline.md), `src/timeline.ts`(`timelineRecipe`)
|
|
9
9
|
- 스토리북: `배포/컴포넌트/데이터 표시/타임라인`
|
|
10
10
|
|
|
11
|
+
## 기본 표현: 여행 일지 (2026-10-27 개발 소스, 미게시)
|
|
12
|
+
|
|
13
|
+
사용자가 다이어리 비교 시안의 「여행 일지」를 선택한 뒤 옵션이 아닌 기본형으로 지정했다.
|
|
14
|
+
`variant` 기본값은 `journal`이다. 박스·선택 배경 없이 작은 점과 연결선으로 기록을 잇고 제목 아래에
|
|
15
|
+
시각을 둔다. 이전 면·테두리 표현이 꼭 필요한 경우에만 `variant="record"`를 명시한다.
|
|
16
|
+
|
|
17
|
+
- 수동 기록은 모든 설명·metadata·기존 행동을 계속 표시한다. 경기 중계·버전 복구·활동 이력이 자동으로 접히지 않는다.
|
|
18
|
+
- `selectedId`/`onSelectedIdChange`가 있으면 선택한 기록의 설명·metadata만 펼친다. 다시 누르면 상세만 접고 선택은 유지한다.
|
|
19
|
+
- 다른 행·지도·날짜에서 선택을 바꾸면 새 기록을 펼친다. 제목·시각은 접어도 남는다. 펼침 상태·chevron은 HJM이 소유한다.
|
|
20
|
+
- Web 기본형은 `<ol><li><button aria-expanded aria-controls>`의 disclosure 목록이다. 모든 기록 버튼에 Tab으로 접근하고 ↑/↓/Home/End로 이동·선택할 수 있다. Enter/Space는 native button 동작으로 펼침을 전환한다.
|
|
21
|
+
- Native는 `Pressable`의 selected·expanded 상태를 제공한다. 접힌 설명은 접근성 이름에서도 제외한다.
|
|
22
|
+
- `tone`의 의미와 독립 행동의 접근성·가상화 경계는 유지한다. `emphasized`의 큰 배경 면은 journal에 적용하지 않는다.
|
|
23
|
+
|
|
24
|
+
현재 사용처: 다이어리 웹·앱 여정, 번뚝 웹·앱 버전/이야기/피드 기록, 야잘알 앱 경기 중계·선수 경력,
|
|
25
|
+
Today갈게 partner-web·partner 활동 기록, portfolio-site 성과 이력. 이들은 기본 디자인 변경의 영향 대상이며
|
|
26
|
+
제품별 기능·데이터를 바꾸거나 운영 반영을 확인했다는 뜻은 아니다. 웹·앱 Storybook에도 여행 일지 예제를 둔다.
|
|
27
|
+
|
|
11
28
|
## 언제 쓰나
|
|
12
29
|
|
|
13
30
|
이미 일어난 일을 시간 순서대로 보여 줄 때 쓴다. 경기 플레이 기록, 이적·변경 이력, 주문 처리
|
|
14
31
|
기록처럼 "무슨 일이 언제 있었나"를 위→아래 한 방향으로 나열한다. 현재 위치(커서)는 없다.
|
|
15
|
-
기록 하나를 골라 옆의 지도·상세를 바꿔야 하면 `selectedId`/`onSelectedIdChange`로 기록 자체를
|
|
32
|
+
기록 하나를 골라 옆의 지도·상세를 바꿔야 하면 `selectedId`/`onSelectedIdChange`로 기록 자체를 선택·펼침 대상으로 만든다(개발 소스, 미게시).
|
|
16
33
|
|
|
17
34
|
## 쓰지 않을 때
|
|
18
35
|
|
|
@@ -20,7 +37,7 @@
|
|
|
20
37
|
| --- | --- |
|
|
21
38
|
| 흐름에서 지금 어디에 있는지(현재 단계) | [Steps](steps.md) |
|
|
22
39
|
| 항목을 눌러 다른 화면으로 이동 | [List](list.md), [ListRow](list-row.md) |
|
|
23
|
-
| 기록 옆에 번호·선택 `Button`을 두고 `tone`·`aria-pressed`로 선택을 흉내 | `selectedId`/`onSelectedIdChange`.
|
|
40
|
+
| 기록 옆에 번호·선택 `Button`을 두고 `tone`·`aria-pressed`로 선택을 흉내 | `selectedId`/`onSelectedIdChange`. 선택·펼침·키보드·접근성 의미는 Timeline이 소유한다 |
|
|
24
41
|
| 항목이 하나도 없음 | Timeline을 마운트하지 않고 [EmptyState](empty-state.md) |
|
|
25
42
|
| 과거 기록을 더 불러옴 | Timeline 아래에 [LoadMore](load-more.md)를 합성 |
|
|
26
43
|
| 항목별 짧은 배지 | `renderMetadata` 슬롯에 제품의 [Badge](badge.md) 또는 [Tag](tag.md)를 연결 |
|
|
@@ -73,6 +90,7 @@ import { Timeline } from "@hjmds/react-native/data-display";
|
|
|
73
90
|
|
|
74
91
|
| prop | 값 | 기본값 | 설명 |
|
|
75
92
|
| --- | --- | --- | --- |
|
|
93
|
+
| `variant` | `journal` · `record` | `journal` | 기본 여행 일지 / 이전 면·테두리 표현. 개발 소스, 미게시 |
|
|
76
94
|
| `items`(필수) | `id`·`label` 필수, `timestamp`·`description`·`tone` 선택 | — | 배열 순서가 곧 표시 순서 |
|
|
77
95
|
| `items[].dateTime` | string | — | timestamp의 원래 시각. Web time의 dateTime에 연결하며 표시 문구를 바꾸지 않는다. timestamp와 함께 사용 |
|
|
78
96
|
| `items[].emphasized` | boolean | false | 기록 배경의 은은한 강조. 진행 커서·선택 상태를 만들지 않는다 |
|
|
@@ -89,6 +107,10 @@ import { Timeline } from "@hjmds/react-native/data-display";
|
|
|
89
107
|
|
|
90
108
|
## 배치
|
|
91
109
|
|
|
110
|
+
기본 `journal`은 항목 사이 0, 위아래 `spacing.md` 16, 가로 안쪽 여백 0, dot 8, connector 1이다.
|
|
111
|
+
선택 배경·테두리는 없으며 펼친 설명 앞에는 `spacing.xs` 8(Web), 본문 기본 간격 `spacing.xxs` 4(Native)를 쓴다.
|
|
112
|
+
아래 표의 24/12/10 배치·중립 선택 면은 호환 `variant="record"`에 해당한다.
|
|
113
|
+
|
|
92
114
|
| 항목 | 값 | 근거 |
|
|
93
115
|
| --- | --- | --- |
|
|
94
116
|
| 크기 | 각 항목은 [레일 16][본문] 두 열. 레일은 지름 10의 dot과 다음 항목까지 이어지는 1px connector. 기록 본문은 누름 대상이 아니며 `renderActions`가 있으면 별도의 행동 열을 추가한다 | `timelineRecipe`, `.hjm-timeline__item`·`__dot`·`__connector` |
|
|
@@ -110,6 +132,8 @@ import { Timeline } from "@hjmds/react-native/data-display";
|
|
|
110
132
|
|
|
111
133
|
## 플랫폼 차이
|
|
112
134
|
|
|
135
|
+
아래 선택형 listbox/option 설명은 `variant="record"`이다. 기본 journal의 disclosure 의미는 위 기본 표현 절을 따른다.
|
|
136
|
+
|
|
113
137
|
| 항목 | Web | Native |
|
|
114
138
|
| --- | --- | --- |
|
|
115
139
|
| 구조 | `<ol>` / `<li>`, dot·connector `aria-hidden` | `View` 행, rail·본문은 접근성 트리에서 숨김 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hjmds/design-contracts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.49.0",
|
|
4
4
|
"description": "Renderer-neutral design contracts, tokens, recipes, and behaviors shared by HJM products.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -834,6 +834,11 @@
|
|
|
834
834
|
"react-native": "./dist/time-picker.js",
|
|
835
835
|
"import": "./dist/time-picker.js",
|
|
836
836
|
"default": "./dist/time-picker.js"
|
|
837
|
+
},
|
|
838
|
+
"./components/drawing-editor": {
|
|
839
|
+
"types": "./dist/drawing-editor.d.ts",
|
|
840
|
+
"import": "./dist/drawing-editor.js",
|
|
841
|
+
"default": "./dist/drawing-editor.js"
|
|
837
842
|
}
|
|
838
843
|
},
|
|
839
844
|
"files": [
|