@hjmds/design-contracts 1.22.0 → 1.24.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.
Files changed (121) hide show
  1. package/dist/catalog.d.ts +16 -0
  2. package/dist/catalog.d.ts.map +1 -1
  3. package/dist/component-recipes.d.ts +16 -0
  4. package/dist/component-recipes.d.ts.map +1 -1
  5. package/dist/component-recipes.js +3 -0
  6. package/dist/component-recipes.js.map +1 -1
  7. package/dist/conversation-row.d.ts +13 -0
  8. package/dist/conversation-row.d.ts.map +1 -0
  9. package/dist/conversation-row.js +14 -0
  10. package/dist/conversation-row.js.map +1 -0
  11. package/dist/version.d.ts +1 -1
  12. package/dist/version.js +1 -1
  13. package/dist/version.js.map +1 -1
  14. package/docs/generated/component-maturity.md +1 -1
  15. package/docs/generated/renderer-evidence.json +3 -3
  16. package/docs/generated/renderer-evidence.md +1 -1
  17. package/docs/generated/showcase-manifest.json +1 -1
  18. package/docs/screen-patterns.md +11 -4
  19. package/docs/usage/README.md +3 -1
  20. package/docs/usage/SELECTION.md +36 -23
  21. package/docs/usage/STANDARD.md +5 -0
  22. package/docs/usage/components/comment-thread-screen.md +4 -1
  23. package/docs/usage/components/composition-layout.md +93 -0
  24. package/docs/usage/components/conversation-row.md +93 -0
  25. package/docs/usage/components/list-row.md +7 -0
  26. package/docs/usage/components/screen-layout.md +4 -1
  27. package/docs/usage/components/segmented-control.md +7 -0
  28. package/docs/usage/components/top-bar.md +1 -0
  29. package/docs/usage/compositions/action-feedback.md +3 -1
  30. package/docs/usage/compositions/action-recovery-optimistic.md +4 -2
  31. package/docs/usage/compositions/action-recovery-save.md +4 -2
  32. package/docs/usage/compositions/action-recovery-undo.md +4 -2
  33. package/docs/usage/compositions/activity-status.md +3 -1
  34. package/docs/usage/compositions/adaptive-content.md +4 -2
  35. package/docs/usage/compositions/authoring-tools.md +2 -0
  36. package/docs/usage/compositions/collection-detail.md +3 -1
  37. package/docs/usage/compositions/collection-editing.md +2 -0
  38. package/docs/usage/compositions/command-records.md +3 -1
  39. package/docs/usage/compositions/common-message.md +3 -1
  40. package/docs/usage/compositions/common-notification.md +3 -1
  41. package/docs/usage/compositions/communication.md +2 -0
  42. package/docs/usage/compositions/compound-controls.md +4 -2
  43. package/docs/usage/compositions/content-transition-comparison.md +3 -1
  44. package/docs/usage/compositions/context-toolbar.md +4 -2
  45. package/docs/usage/compositions/data-layouts.md +4 -2
  46. package/docs/usage/compositions/data-workflows.md +2 -0
  47. package/docs/usage/compositions/date-entry.md +3 -1
  48. package/docs/usage/compositions/date-time-selection.md +3 -1
  49. package/docs/usage/compositions/design-profile-comparison.md +3 -1
  50. package/docs/usage/compositions/disclosure.md +6 -4
  51. package/docs/usage/compositions/document-resource.md +3 -1
  52. package/docs/usage/compositions/environment-matrix.md +3 -1
  53. package/docs/usage/compositions/expo-interactions.md +4 -2
  54. package/docs/usage/compositions/family-drawer.md +4 -2
  55. package/docs/usage/compositions/field-group.md +3 -1
  56. package/docs/usage/compositions/floating-action-button.md +3 -1
  57. package/docs/usage/compositions/illustrated-outcome.md +3 -1
  58. package/docs/usage/compositions/input-sheet.md +3 -1
  59. package/docs/usage/compositions/interaction-adapters.md +3 -1
  60. package/docs/usage/compositions/interaction-flow-apply.md +3 -1
  61. package/docs/usage/compositions/interaction-flow-draft.md +3 -1
  62. package/docs/usage/compositions/interaction-flow-search.md +3 -1
  63. package/docs/usage/compositions/live-list.md +3 -1
  64. package/docs/usage/compositions/media-transport.md +2 -0
  65. package/docs/usage/compositions/native-renderers.md +3 -1
  66. package/docs/usage/compositions/navigation-bar-collection.md +3 -1
  67. package/docs/usage/compositions/optional-adapters.md +3 -1
  68. package/docs/usage/compositions/optional-motion.md +3 -1
  69. package/docs/usage/compositions/origin-dialog.md +3 -1
  70. package/docs/usage/compositions/paper-surface.md +3 -1
  71. package/docs/usage/compositions/photo-source.md +3 -1
  72. package/docs/usage/compositions/purpose-input-comment.md +8 -2
  73. package/docs/usage/compositions/purpose-input-message.md +4 -2
  74. package/docs/usage/compositions/reference-first.md +3 -1
  75. package/docs/usage/compositions/reference-review.md +3 -1
  76. package/docs/usage/compositions/reference-settings.md +3 -1
  77. package/docs/usage/compositions/selection-motion.md +4 -2
  78. package/docs/usage/compositions/selection-scope.md +3 -1
  79. package/docs/usage/compositions/social-compositions.md +2 -0
  80. package/docs/usage/compositions/stea-event-ticket.md +3 -1
  81. package/docs/usage/compositions/stea-flip-card.md +4 -2
  82. package/docs/usage/compositions/stea-order-progress.md +5 -3
  83. package/docs/usage/compositions/stea-otp-verify.md +5 -3
  84. package/docs/usage/compositions/stea-pixel-empty.md +5 -3
  85. package/docs/usage/compositions/stea-schedule-card.md +5 -3
  86. package/docs/usage/compositions/stea-stat-summary.md +4 -2
  87. package/docs/usage/compositions/streaming-answer.md +2 -0
  88. package/docs/usage/compositions/task-controls.md +2 -0
  89. package/docs/usage/compositions/texture-comparison.md +3 -1
  90. package/docs/usage/compositions/time-selection.md +5 -3
  91. package/docs/usage/compositions/toast-layout.md +3 -1
  92. package/docs/usage/compositions/toggle-feedback.md +2 -0
  93. package/docs/usage/compositions/transfer-task.md +2 -0
  94. package/docs/usage/compositions/upload-recovery.md +4 -2
  95. package/docs/usage/compositions/video-dialog.md +3 -1
  96. package/docs/usage/compositions/visual-foundations.md +5 -3
  97. package/docs/usage/compositions/web-additions.md +6 -4
  98. package/docs/usage/compositions/web-navigation.md +4 -2
  99. package/docs/usage/screens/common-chat.md +3 -1
  100. package/docs/usage/screens/common-comments.md +6 -4
  101. package/docs/usage/screens/common-inbox.md +3 -1
  102. package/docs/usage/screens/common-login.md +3 -1
  103. package/docs/usage/screens/common-profile.md +3 -1
  104. package/docs/usage/screens/common-saved.md +3 -1
  105. package/docs/usage/screens/common-search.md +3 -1
  106. package/docs/usage/screens/common-settings.md +3 -1
  107. package/docs/usage/screens/common-shell.md +3 -1
  108. package/docs/usage/screens/dashboard.md +3 -1
  109. package/docs/usage/screens/discovery-gallery.md +3 -1
  110. package/docs/usage/screens/flow-collection.md +3 -1
  111. package/docs/usage/screens/flow-editor.md +3 -1
  112. package/docs/usage/screens/flow-media.md +3 -1
  113. package/docs/usage/screens/flow-moderation.md +3 -1
  114. package/docs/usage/screens/flow-onboarding.md +3 -1
  115. package/docs/usage/screens/flow-permission.md +3 -1
  116. package/docs/usage/screens/landing.md +4 -2
  117. package/docs/usage/screens/mockup-studio.md +3 -1
  118. package/docs/usage/screens/notification-settings.md +32 -138
  119. package/docs/usage/screens/product-bento.md +3 -1
  120. package/docs/usage/screens/reference-comparison.md +3 -1
  121. package/package.json +6 -1
@@ -0,0 +1,13 @@
1
+ /** Conversation previews reserve metadata and unread status while truncating the message. */
2
+ export declare const conversationRowRecipe: {
3
+ readonly avatarSize: 56;
4
+ readonly unreadSize: 10;
5
+ readonly gap: 12;
6
+ readonly paddingInline: 16;
7
+ readonly paddingBlock: 10;
8
+ readonly contentGap: 2;
9
+ readonly titleWeight: "500";
10
+ readonly unreadTitleWeight: "800";
11
+ readonly unreadPreviewWeight: "700";
12
+ };
13
+ //# sourceMappingURL=conversation-row.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-row.d.ts","sourceRoot":"","sources":["../src/conversation-row.ts"],"names":[],"mappings":"AAEA,6FAA6F;AAC7F,eAAO,MAAM,qBAAqB;;;;;;;;;;CAUxB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { spacing } from "./foundations.js";
2
+ /** Conversation previews reserve metadata and unread status while truncating the message. */
3
+ export const conversationRowRecipe = {
4
+ avatarSize: 56,
5
+ unreadSize: 10,
6
+ gap: spacing.sm,
7
+ paddingInline: spacing.md,
8
+ paddingBlock: 10,
9
+ contentGap: 2,
10
+ titleWeight: "500",
11
+ unreadTitleWeight: "800",
12
+ unreadPreviewWeight: "700",
13
+ };
14
+ //# sourceMappingURL=conversation-row.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-row.js","sourceRoot":"","sources":["../src/conversation-row.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,6FAA6F;AAC7F,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,UAAU,EAAE,EAAE;IACd,UAAU,EAAE,EAAE;IACd,GAAG,EAAE,OAAO,CAAC,EAAE;IACf,aAAa,EAAE,OAAO,CAAC,EAAE;IACzB,YAAY,EAAE,EAAE;IAChB,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,KAAK;IAClB,iBAAiB,EAAE,KAAK;IACxB,mBAAmB,EAAE,KAAK;CAClB,CAAC","sourcesContent":["import { spacing } from \"./foundations.js\";\n\n/** Conversation previews reserve metadata and unread status while truncating the message. */\nexport const conversationRowRecipe = {\n avatarSize: 56,\n unreadSize: 10,\n gap: spacing.sm,\n paddingInline: spacing.md,\n paddingBlock: 10,\n contentGap: 2,\n titleWeight: \"500\",\n unreadTitleWeight: \"800\",\n unreadPreviewWeight: \"700\",\n} as const;\n"]}
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Package release shown by documentation surfaces. Kept in sync by a test. */
2
- export declare const designSystemVersion: "1.22.0";
2
+ export declare const designSystemVersion: "1.24.0";
3
3
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Package release shown by documentation surfaces. Kept in sync by a test. */
2
- export const designSystemVersion = "1.22.0";
2
+ export const designSystemVersion = "1.24.0";
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.22.0\" as const;\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.24.0\" as const;\n"]}
@@ -1,6 +1,6 @@
1
1
  # Component maturity and evidence manifest
2
2
 
3
- > Generated from `@hjmds/design-contracts` 1.22.0. Do not edit this file directly; run `pnpm contracts:sync`.
3
+ > Generated from `@hjmds/design-contracts` 1.24.0. Do not edit this file directly; run `pnpm contracts:sync`.
4
4
 
5
5
  This document is the human-readable projection of the same catalog and Showcase manifest consumed by product CI.
6
6
 
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "designSystemVersion": "1.22.0",
3
+ "designSystemVersion": "1.24.0",
4
4
  "source": "packages/design-contracts/docs/generated/showcase-manifest.json",
5
5
  "surfaces": {
6
6
  "web": {
7
7
  "packageName": "@hjmds/react",
8
- "packageVersion": "1.22.0",
8
+ "packageVersion": "1.24.0",
9
9
  "activeComponents": 104,
10
10
  "claimedComponents": 103,
11
11
  "completeScenarioComponents": 103,
@@ -5672,7 +5672,7 @@
5672
5672
  },
5673
5673
  "native": {
5674
5674
  "packageName": "@hjmds/react-native",
5675
- "packageVersion": "1.22.0",
5675
+ "packageVersion": "1.24.0",
5676
5676
  "activeComponents": 85,
5677
5677
  "claimedComponents": 83,
5678
5678
  "completeScenarioComponents": 83,
@@ -1,6 +1,6 @@
1
1
  # Renderer evidence coverage
2
2
 
3
- > Generated for HJM 1.22.0. Do not edit directly; run `pnpm evidence:sync`.
3
+ > Generated for HJM 1.24.0. Do not edit directly; run `pnpm evidence:sync`.
4
4
 
5
5
  This projection joins the canonical surface maturity manifest with first-party renderer claims. Missing scenarios are explicit beta promotion debt; stable surfaces are blocked by CI until none remain.
6
6
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "designSystemVersion": "1.22.0",
3
+ "designSystemVersion": "1.24.0",
4
4
  "packageName": "@hjmds/design-contracts",
5
5
  "components": [
6
6
  {
@@ -1,6 +1,6 @@
1
1
  # 반복 화면 조합
2
2
 
3
- 검토일: 2026-10-06 · 상태: Storybook 배포(2026-10-06 사용자 승인) · 패키지 게시/제품 적용 전
3
+ 검토일: 2026-10-15 · 상태: Storybook 배포(2026-10-06 사용자 승인) · 패키지 적용 범위는 개별 API 사용 지침 참조
4
4
 
5
5
  사용자가 로그인·설정·알림·채팅 화면을 미리 일반화해 개발 생산성을 높이도록 요청했다.
6
6
  기존 AuthScreenLayout, Section, ListRow, TopBar, Layout, EmptyState, action-session,
@@ -23,6 +23,9 @@ Flutter 앱에는 React 컴포넌트를 주입할 수 없다. 화면 역할과
23
23
 
24
24
  ## 선택 기준과 소유권
25
25
 
26
+ 모든 화면·구성의 공통 구조는 HJM이 제공한다. [선택 지침](usage/SELECTION.md)의 소유권을 따르며, 제품은 데이터·문구·추가 기능만 연결한다. 전용 구성이 없는 영역은 `CompositionLayout`, 가상화 댓글 행은 `CommentThreadRow`를 사용해 기본 배치를 복제하지 않는다. 두 API는 1.23.0부터 제공한다.
27
+
28
+
26
29
  - 로그인: 기존 `auth-screen`의 `AuthScreenLayout` + `AuthProviderButton`을 그대로 쓴다.
27
30
  새 LoginScreen alias나 제공자 preset은 만들지 않는다. 카드 유지·중앙 로딩·정책 고지는
28
31
  [로그인 계약](auth-screen.md)을 따른다.
@@ -38,8 +41,10 @@ Flutter 앱에는 React 컴포넌트를 주입할 수 없다. 화면 역할과
38
41
  - `ChatMessage`: 발신/수신 정렬, 작성자, 아바타, 답장 인용, 전송 상태와 시각을 합성한다.
39
42
  기존 Asset/VoiceNote 등은 children에 넣고 영수증·반응·재시도는 제품이 넘긴다. 전체 타임라인을
40
43
  live region으로 만들지 않으므로 과거 메시지 로딩 때 읽기 순서를 가로채지 않는다.
41
- - `MessageComposer`: 기존 TextArea와 Button을 조합한다. 빈 입력·pending·disabled에서는
42
- 전송하지 않고 Enter는 줄 바꿈으로 유지한다(IME 조합과 충돌 방지). 제품 onSend에 원문을
44
+ - `MessageComposer`: 기존 TextArea와 Button을 조합한다. 글·첨부·추가 콘텐츠가 모두 없거나 pending·disabled이면
45
+ 전송하지 않는다. Enter는 기본 줄바꿈이며 데스크톱 채팅의 Enter 전송은 공개 `submitMode="send"`로 선택한다.
46
+ Web의 Shift+Enter·IME 조합 중 Enter는 전송하지 않는다. 제품별 키 처리로 이 보호를 우회하지 않는다.
47
+ 세부 플랫폼 동작은 [MessageComposer 사용 지침](usage/components/message-composer.md)을 따른다. 제품 onSend에 원문을
43
48
  전달하며 초안 초기화는 서버 영수증을 받은 제품만 수행한다. 1~5줄은 짧은 작성창이 타임라인을
44
49
  밀어내지 않도록 정한 기본값이며 더 긴 입력은 TextArea 안에서 스크롤한다.
45
50
  - `ChatScreen`: `composer`와 타임라인을 분리한다. 기본 `scroll="content"`로 FlatList 등
@@ -66,7 +71,9 @@ Web host는 실제 남은 높이를 제공해야 한다(예: flex route의 `heig
66
71
  `height:100dvh`). Native host는 safe area와 탭/상단 navigation inset을 먼저 제외한다.
67
72
  HJM이 기종별 높이나 중첩 safe area를 추측하지 않는다. Native ChatScreen은 기존
68
73
  `KeyboardAvoiding` 또는 제품 keyboard adapter 하나로 감싸며 둘을 동시에 적용하지 않는다.
69
- ready 이외의 채팅에는 작성창을 숨겨 접근 제한·초기 로딩에서 전송 조작이 노출되지 않게 한다.
74
+ 채팅 작성창은 `ready`와 `empty`에서 유지하고 `loading`·`error`·`restricted`에서 숨긴다.
75
+ 빈 대화에서도 첫 메시지를 작성할 수 있어야 하기 때문이다. 전송 실패는 기존 `ready` 또는 `empty`와
76
+ 초안을 유지한 채 `notice`로 알린다. [채팅 화면 지침](usage/screens/common-chat.md#첫-메시지부터-실패-복구까지)을 따른다.
70
77
 
71
78
  ## 사용 예시
72
79
 
@@ -6,7 +6,7 @@
6
6
  버튼 위치에서 시작해 구성·컴포넌트 지침으로 내려가고, 값은 토큰 지침에서 고른다.
7
7
  [선택 기준](SELECTION.md)으로 그대로 사용·공개 슬롯 확장·제품 전용 구성을 고른다. 설치 버전의 지침을 본다: `node_modules/@hjmds/design-contracts/docs/usage/`.
8
8
  제공 기준은 확인한 기본 API 버전이며 최초 출시 버전이 아니다. 구성·화면은 각 API 지침의 제공 버전을 확인한다.
9
- API 안정성은 1.21.0의 생성 manifest에서 읽은 정규(canonical) API 기준이다. 동반·선택형 확장은 개별 계약을 확인한다.
9
+ API 안정성은 1.24.0의 생성 manifest에서 읽은 정규(canonical) API 기준이다. 동반·선택형 확장은 개별 계약을 확인한다.
10
10
  Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품 반영을 의미하지 않는다.
11
11
  `분류`는 Storybook 제목 `<배포|실험>/<단계>/<분류>/<항목>`의 셋째 마디이고, 토큰·구성·화면은 Storybook 메뉴와 같은 순서다.
12
12
 
@@ -66,9 +66,11 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
66
66
  | [Combobox](components/combobox.md) | 입력 | 주어진 목록에서 하나를 고르는데 목록이 길어 입력으로 좁혀야 할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
67
67
  | [CommandPalette](components/command-palette.md) | 오버레이 | ⌘K 스타일로 앱 전체의 **행동**을 검색해 실행하는 모달에 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
68
68
  | [CommentThreadScreen](components/comment-thread-screen.md) | 화면/소통 | 게시물·콘텐츠 아래의 댓글 화면 한 장에 쓴다. | 1.17.1 | Web · Native | 개별 API 계약 참조 | 배포 |
69
+ | [CompositionLayout](components/composition-layout.md) | 화면/설정 | 카드·패널·화면 본문 안의 콘텐츠 묶음에 제목·배지·행동·안내·본문·하단 영역이 필요할 때 쓴다. | 1.23.0 | Web · Native | 개별 API 계약 참조 | 배포 |
69
70
  | [Container](components/container.md) | 레이아웃 | 화면 본문의 최대 폭과 좌우(논리 방향) 여백을 맞출 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
70
71
  | [ContentTransition](components/content-transition.md) | 시각 효과 | 같은 자리의 내용이 상태에 따라 바뀔 때(필터 결과 패널, 단계별 본문) 새 내용이 짧게 나타나도록 감싼다. | 1.12.1 | Web · Native | 개별 API 계약 참조 | 배포 |
71
72
  | [ContextMenu](components/context-menu.md) | 탐색 | Web에서 제품이 소유한 영역(카드·목록 행·캔버스)의 우클릭·길게 누르기·Shift+F10에 명령 목록을 띄울 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native unsupported | 배포 |
73
+ | [ConversationRow](components/conversation-row.md) | — | 대화 목록에서 상대, 마지막 메시지, 상대 시각, 안 읽음 점을 표시한다. | 1.24.0 | Web · Native | 개별 API 계약 참조 | 실험 |
72
74
  | [CounterBadge](components/counter-badge.md) | 데이터 표시 | 읽지 않은 알림·메시지·장바구니 수처럼 **셀 수 있는 개수**를 아이콘·행 옆에 작게 보일 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
73
75
  | [DataTable](components/data-table.md) | 데이터 표시 | 여러 행의 데이터를 열로 맞춰 훑고, 열 기준으로 정렬하거나 행을 골라 일괄 작업할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native beta | 배포 |
74
76
  | [DatePicker](components/date-picker.md) | 입력 | 폼·필터 자리에서 날짜 **하나**를 고를 때 쓴다(생년월일, 방문일, 시작일 필터). 평소에는 필드 트리거만 보이고, 누르면 Web은 필드에 붙은 팝오버, Native는 Sheet 안에 같은 달력 격자를 연다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
@@ -1,34 +1,47 @@
1
1
  # 화면과 구성 선택
2
2
 
3
- 검토일: 2026-10-13
3
+ 검토일: 2026-10-15
4
4
 
5
- 2026-10-13 설계 점검에서 화면 이름만으로 공통 화면을 강제하면 저장·스크롤·권한 흐름이
6
- 다른 제품까지 같은 틀에 맞출 수 있음을 확인했다. 화면 → 구성 → 컴포넌트 → 토큰 순으로
7
- 찾되, 아래 행동과 소유권이 맞는지 먼저 판단한다.
5
+ 2026-10-15 사용자 요청: 메시지·댓글에서 같은 HJM 부품을 사용해도 앱이 기본 배치와 상태 표시를
6
+ 다시 조립하면서 UI가 달라졌다. 이 선택 기준은 모든 화면·구성에 적용한다. **공통 구조는 HJM이
7
+ 완성해서 제공하고, 앱은 데이터·문구·브랜드·권한·추가 기능을 연결한다.**
8
8
 
9
9
  ## 선택 기준
10
10
 
11
11
  | 선택 | 조건 | 구현 경로 |
12
12
  | --- | --- | --- |
13
- | 그대로 사용 | 영역 순서, 저장 시점, 상태 교체, 스크롤 소유가 제품과 맞는다 | 공통 화면에 데이터·문구·콜백을 연결 |
14
- | 공개 슬롯으로 확장 | 공통 행동은 같고 헤더·본문 콘텐츠·보조 행동만 다르다 | header·notice·footer 등 해당 API의 공개 슬롯 사용 |
15
- | 제품 전용 구성 | 저장·탐색·권한 흐름 또는 가상화·스크롤 구조가 공개 API로 표현되지 않는다 | 기존 HJM 구성·컴포넌트로 제품에서 조합하고 차이를 기존 코드/작업 기록에 설명 |
16
-
17
- 공개 슬롯으로 해결할 수 있는 틀·상태 엔진은 복제하지 않는다. 제품 전용 구성도 semantic token,
18
- 접근성·입력·실패 복구 계약을 유지한다. 내부 selector나 제거된 style prop은 확장 경로가 아니다.
19
- 여러 제품이 공유하는 공백이면 공통 API의 의미 있는 축으로 검토한다. 별도 승인·ADR은 자동 요구하지 않는다.
20
-
21
- ## 선택 전에 확인할 것
22
-
23
- - **저장:** 즉시 저장인지 일괄 저장인지, pending의 소유자와 실패 시 마지막 확인값·초안이 어떻게 남는지.
24
- - **스크롤:** host와 본문 중 누가 스크롤하는지, 목록 가상화·페이지 추가·위치 복구가 필요한지.
25
- - **상태:** 최초 조회의 본문 교체와 저장/재조회 오류의 비차단 notice가 구분되는지.
26
- - **권한:** 접근 제한에서 작성·전송을 숨길지, 읽기와 쓰기 권한이 다른지.
27
- - **플랫폼:** Web 포커스와 Native 키보드·safe area를 누가 처리하는지. 같은 inset·스크롤을 중복 적용하지 않는다.
28
-
29
- SettingsScreen의 행별 즉시 저장은 행 콜백에 제품 mutation을 연결한다. 일괄 저장도 공개 footer와
30
- notice로 표현되면 같은 API를 사용한다. CommentThreadScreen의 데이터 구조나 스크롤이 대규모
31
- 가상화에 맞지 않으면 이름만 보고 강제하지 않고 ScreenLayout과 적합한 목록을 조합한다.
13
+ | 그대로 사용 | 제공하는 화면·구성이 제품의 목적에 맞는다 | HJM 공개 API에 데이터·문구·콜백 연결 |
14
+ | 공개 슬롯으로 확장 | 기본 흐름은 같고 제품 콘텐츠·추가 행동이 필요하다 | 정해진 슬롯에 콘텐츠를 공급하고 배치·상태 엔진은 HJM 유지 |
15
+ | 공통 API 보완 | 가상화·새 영역·동작을 기존 API로 표현할 수 없다 | HJM의 기존 구현을 확장하거나 공통 하위 구성을 공개한 뒤 앱에서 사용 |
16
+ | 제품 고유 기능 | 지도·편지·도구 실행 등 제품만의 도메인 경험이다 | HJM의 화면·구성 안 콘텐츠 슬롯에 연결. 제품 기능을 이유로 공통 헤더·입력·상태·배지를 복제하지 않음 |
17
+
18
+ ## 소유권
19
+
20
+ | HJM이 제공하는 것 | 앱이 공급하는 것 |
21
+ | --- | --- |
22
+ | 영역 순서, 간격·정렬·폭, 헤더·본문·footer, 반응형 배치 | 제목·문구·브랜드 설정·실제 콘텐츠 |
23
+ | 목록 항목, 아바타·시각·배지·반응·메뉴의 기본 자리 | 작성자·시각·배지 의미/값·반응 종류·메뉴 항목 |
24
+ | 초기 로딩·빈 상태·오류의 영역 중앙 표시, 비차단 안내 자리 | 조회 상태·오류 문구·재시도 콜백 |
25
+ | 작성창·답장 대상·첨부·전송 상태, 확장 영역 배치 | 초안·수신 결과·사진 권한/업로드·제품 도구 카드 |
26
+ | 펼침·전환·포커스·제스처·접근성 표현 | 권한 판정·라우팅·서버 요청·저장·도메인 규칙 |
27
+
28
+ 슬롯은 콘텐츠와 추가 기능의 연결점이다. header·footer·children이 ReactNode라는 이유로
29
+ 제품에서 동일한 기본 구조를 다시 만드는 허용 경로로 해석하지 않는다. 배지는 의미·값을 앱이
30
+ 결정하고 위치·간격·넘침 표현은 해당 HJM 구성과 Badge 계약을 따른다.
31
+
32
+ ## 구현과 확인
33
+
34
+ - 화면 → 구성 → 컴포넌트 → 토큰 순으로 찾고 기존 공개 API와 양 renderer를 먼저 확인한다.
35
+ - 기존 API가 부족하면 HJM 내부 구현을 분리·확장한다. 같은 구성에 대한 앱별 대체 엔진을 만들지 않는다.
36
+ 가상화 host는 제품이 연결할 수 있지만 댓글 행은 `CommentThreadRow`처럼 공통 표시를 재사용한다.
37
+ - 웹/RN은 플랫폼 adapter가 다르더라도 영역 순서·기본값·상태 의미를 공유한다. Native safe area·키보드와
38
+ Web host 높이는 외부 환경 입력이며 같은 inset·스크롤을 두 번 적용하지 않는다.
39
+ - 서버 실패·초안·권한은 제품 상태가 정본이다. HJM은 성공을 추정하거나 초안을 임의로 지우지 않는다.
40
+ - Showcase는 HJM 공개 구현에 fixture를 연결한다. fixture의 예시 데이터·지연·브랜드·제품 메뉴를
41
+ 운영 기능으로 복사하지 않는다. 공개되지 않은 예제 조합은 먼저 HJM API로 옮겨야 하며,
42
+ 문서가 있다는 이유로 재사용 구현이 완성됐다고 보고하지 않는다.
43
+ - 기존 소비 화면은 기능을 보존하며 이관한다. 설치 버전의 지원 여부를 확인하고 HJM 변경·제품 채택·
44
+ 화면 확인·게시를 구분한다. 새로운 전수 검사·승인 게이트를 추가하지 않는다.
32
45
 
33
46
  ## 상태와 버전 읽기
34
47
 
@@ -9,6 +9,11 @@
9
9
 
10
10
  ## 1. 공통
11
11
 
12
+ 화면·구성은 [소유권](SELECTION.md#소유권)에 맞춰 HJM 공개 구현과 앱의 데이터/확장 입력을 구분한다.
13
+ 기본 배치·상태·배지·상호작용을 제품이 재조립하도록 안내하지 않는다. 실행 예제만 있는 조합은
14
+ 공개 구현 여부를 명시하고, 예제를 앱에 복사하는 대신 기존 HJM API를 보완한다.
15
+
16
+
12
17
  - 파일 위치: `usage/<단계 폴더>/<영어 kebab-case>.md`. 컴포넌트는 공개 계약 이름의 kebab(`QRCode` → `qr-code`).
13
18
  - 제목 한 줄 `# <이름>`. 컴포넌트는 공개 계약 이름(`# Button`), 나머지는 Storybook의 마지막 이름(`# 간격`).
14
19
  - 제목 바로 아래 **머리말 목록**을 이 순서·이 키로 둔다. 값이 없으면 `없음`.
@@ -28,6 +28,7 @@
28
28
  | 이름 | 역할 | Web | Native |
29
29
  | --- | --- | --- | --- |
30
30
  | `CommentThreadScreen` | 화면 조합 | `/screen-flows` | `/screen-flows` |
31
+ | `CommentThreadRow` | 가상화 목록에서도 같은 댓글 행 재사용(1.23.0) | `/screen-flows` | `/screen-flows` |
31
32
  | `CommentThreadItem`(타입) | 댓글 한 개의 데이터 | `/screen-flows` | `/screen-flows` |
32
33
 
33
34
  루트 barrel에는 없다. optional native peer를 요구하지 않는다.
@@ -118,7 +119,7 @@ threadHeader도 댓글과 함께 숨겨지므로 항상 보여야 하는 안내
118
119
  `TypeError`가 난다. 답글의 답글은 표현하지 않으므로 제품이 최상위로 평탄화한다.
119
120
  - 모든 문구·시각·좋아요 수 라벨은 제품 i18n에서 만든다. `likeCountLabel`이 빈 문자열이면 그리지 않는다.
120
121
  - 좋아요 저장·길게 누르기·이모지 선택, 답글 권한(`canReply`/`replyDisabled`)은 제품 데이터로 정한다.
121
- - 기본 `scroll`은 `ScreenLayout`과 같은 `screen`이다. 댓글을 가상화 목록으로 그리면 직접 `scroll="content"`를 준다.
122
+ - 기본 `scroll`은 `ScreenLayout`과 같은 `screen`이다. 이 화면 자체는 가상화하지 않는다. 가상화가 필요하면 `ScreenLayout scroll="content"` 안 플랫폼 목록에 `CommentThreadRow`를 연결한다. `scroll="content"`만 바꾼다고 이 화면이 가상화 목록이 되지는 않는다.
122
123
 
123
124
  ## 플랫폼 차이
124
125
 
@@ -145,3 +146,5 @@ trigger로 공급한다. `actions`는 자유 노드 슬롯을 유지하고 메
145
146
 
146
147
  2026-10-12 Spint의 작성자 없는 개발 댓글에서 빈 머리글이 본문을 아래로 밀었다.
147
148
  `author`와 `timeLabel`이 모두 비면 머리글 행을 생략해 avatar와 본문 시작 높이를 맞춘다. Web·Native 동일하다.
149
+
150
+ `CommentThreadRow`는 `item`, `onLike`, `onReply`, `replyLabel`을 받으며 화면 내부도 같은 구현을 사용한다. 가상화 host는 항목 ID·페이지·스크롤을 연결하고 댓글 내부 간격·작성자·본문·시각·반응·메뉴 배치를 복제하지 않는다. `bodyText`가 있으면 작성자와 본문을 이어 쓰고 시각을 하단에, 없으면 작성자와 시각을 머리글에 표시한다.
@@ -0,0 +1,93 @@
1
+ # CompositionLayout
2
+
3
+ - 단계: 컴포넌트
4
+ - 상태: 배포
5
+ - 지원: Web · Native
6
+ - 적용: 1.23.0
7
+ - 검토일: 2026-10-15
8
+ - 근거: 양 renderer `src/screens.tsx`, [소유권](../SELECTION.md#소유권). 제품별 제목·배지·상태·하단 영역 재조립을 줄이기 위해 기존 Stack·상태 계약을 합성한다.
9
+ - 스토리북: `배포/화면/설정/알림 설정`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 카드·패널·화면 본문 안의 콘텐츠 묶음에 제목·배지·행동·안내·본문·하단 영역이 필요할 때 쓴다.
14
+ 전용 구성 API가 있으면 그것을 우선 사용한다. 단순 Stack은 배치 primitive이며 이 구성은 영역 순서와 상태 교체를 소유한다.
15
+
16
+ ## 쓰지 않을 때
17
+
18
+ | 상황 | 대신 쓸 것 |
19
+ | --- | --- |
20
+ | 전체 화면 높이와 고정 헤더/footer·스크롤 | [ScreenLayout](screen-layout.md) |
21
+ | 댓글·채팅 | [CommentThreadScreen](comment-thread-screen.md)·[ChatScreen](chat-screen.md) |
22
+ | 장식 표면 | [Surface](surface.md)를 바깥에 사용 |
23
+
24
+ ## 공개 이름과 import
25
+
26
+ | 이름 | 역할 | Web | Native |
27
+ | --- | --- | --- | --- |
28
+ | `CompositionLayout` | 구성 골격 | `/screens` | `/screens` |
29
+
30
+ ## 최소 사용 예
31
+
32
+ ```tsx
33
+ // Web
34
+ import { CompositionLayout } from "@hjmds/react/screens";
35
+ <CompositionLayout title={title} badge={badge} actions={actions}
36
+ notice={notice} state={state} stateAction={retry} footer={footer}>
37
+ {content}
38
+ </CompositionLayout>
39
+ ```
40
+
41
+ ```tsx
42
+ // Native
43
+ import { CompositionLayout } from "@hjmds/react-native/screens";
44
+ <CompositionLayout title={title} badge={badge} actions={actions}
45
+ notice={notice} state={state} stateAction={retry} footer={footer}>
46
+ {content}
47
+ </CompositionLayout>
48
+ ```
49
+
50
+ ## 축과 기본값
51
+
52
+ | prop | 값 | 기본값 | 설명 |
53
+ | --- | --- | --- | --- |
54
+ | title / description | string | 없음 | 제품 지역화 문구 |
55
+ | leading / badge / actions | ReactNode | 없음 | 앞 콘텐츠·제목 옆 배지·끝 행동. 자리와 간격은 HJM |
56
+ | notice | ReactNode | 없음 | 초안을 유지하는 비차단 안내 |
57
+ | loadingIndicator | ReactNode | 기본 Spinner | loading 상태의 제품 브랜드 그림. HJM이 제목·설명을 접근성 이름으로 전달하고 중앙 배치를 소유하며 ready/error에는 표시하지 않는다(1.24.0) |
58
+ | children | ReactNode | 없음 | 제품 도메인 콘텐츠 또는 HJM 전용 구성 |
59
+ | footer | ReactNode | 없음 | 본문 다음 보조/주 행동; 고정은 바깥 ScreenLayout 소유 |
60
+ | state | ScreenContentState | ready | 본문 교체; loading은 Spinner만 표시 |
61
+ | stateAction | ReactNode | 없음 | 본문 상태와 중앙 정렬되는 복구 행동 |
62
+ | layoutStyle | HjmCompositionStyleProp | 없음 | 부모가 제공하는 영역 크기·배치 |
63
+
64
+ ## 배치
65
+
66
+ ```text
67
+ leading → 제목·배지 / 설명 → actions
68
+ notice
69
+ 본문 또는 중앙 상태·복구
70
+ footer
71
+ ```
72
+
73
+ | 항목 | 값 | 근거 |
74
+ | --- | --- | --- |
75
+ | 크기 | 부모의 가용 폭·높이 | 별도 고정 높이나 스크롤 없음 |
76
+ | 간격 | 영역 spacing.md 16, 제목 행 spacing.sm 12, 배지 spacing.xs 8, 제목/설명 spacing.xxs 4 | Stack props |
77
+ | 순서·정렬 | 머리글 → notice → 본문 → footer | renderer |
78
+ | 고정·스크롤 | 부모 HJM 화면이 소유, 자체 ScrollView 없음 | renderer |
79
+ | 좁은 폭·큰 글자 | 머리글·배지 줄바꿈, 본문 폭 축소 허용 | flexWrap·minWidth |
80
+
81
+ ## 꼭 지킬 것
82
+
83
+ - 데이터·권한·초안·저장·배지 의미는 제품에서 연결한다. 전송 성공을 추정하거나 초안을 자동으로 지우지 않는다.
84
+ - 본문 교체는 초기 조회에, 기존 입력을 유지하는 실패는 notice에 연결한다.
85
+ - 중앙 상태는 이 구성에 주어진 영역을 기준으로 한다. 화면 높이를 임의 여백으로 흉내 내지 않는다.
86
+ - 별도 스크롤을 중첩하지 않는다. 기본 구조를 children 안에서 다시 만들지 않는다.
87
+
88
+ ## 플랫폼 차이
89
+
90
+ | 항목 | Web | Native |
91
+ | --- | --- | --- |
92
+ | 의미 | Heading·alert/status | header·accessibilityLiveRegion |
93
+ | 환경 | 부모 화면의 높이 | 부모 화면의 safe area·키보드 |
@@ -0,0 +1,93 @@
1
+ # ConversationRow
2
+
3
+ - 단계: 컴포넌트
4
+ - 상태: 실험
5
+ - 지원: Web · Native
6
+ - 적용: 1.24.0
7
+ - 검토일: 2026-10-15
8
+ - 근거: `src/conversation-row.ts`의 `conversationRowRecipe`
9
+ - 스토리북: 별도 예제 미등록
10
+
11
+ ## 언제 쓰나
12
+
13
+ 대화 목록에서 상대, 마지막 메시지, 상대 시각, 안 읽음 점을 표시한다. 2026-10-15 전사 적용 중
14
+ 제품마다 복제한 DM 행의 배치가 달라져 공개 구성으로 분리했다. 일반 설정 행은 `ListRow`를 쓴다.
15
+ 이 구성은 큰 아바타와 한 줄 미리보기·시각, 읽지 않은 대화 강조를 보존한다.
16
+
17
+ ## 쓰지 않을 때
18
+
19
+ | 상황 | 대신 쓸 것 |
20
+ | --- | --- |
21
+ | 본문 메시지 | `ChatMessage` |
22
+ | 일반 목록 | `ListRow` |
23
+ | 댓글 | `CommentThreadRow` |
24
+
25
+ ## 공개 이름과 import
26
+
27
+ | 이름 | 역할 | Web | Native |
28
+ | --- | --- | --- | --- |
29
+ | `ConversationRow` | 대화 미리보기 | `@hjmds/react/conversation-row` | `@hjmds/react-native/conversation-row` |
30
+
31
+ ## 최소 사용 예
32
+
33
+ ```tsx
34
+ // Web
35
+ import { ConversationRow } from "@hjmds/react/conversation-row";
36
+
37
+ <Link href={destination} aria-label={accessibleSummary}>
38
+ <ConversationRow title={name} preview={lastMessage} metadata={relativeTime}
39
+ avatar={avatar} unread={unreadCount > 0} />
40
+ </Link>
41
+ ```
42
+
43
+ ```tsx
44
+ // Native
45
+ import { ConversationRow } from "@hjmds/react-native/conversation-row";
46
+ <Pressable accessibilityRole="link" accessibilityLabel={accessibleSummary} onPress={openConversation}>
47
+ <ConversationRow title={name} preview={lastMessage} metadata={relativeTime}
48
+ avatar={avatar} unread={unreadCount > 0} />
49
+ </Pressable>
50
+ ```
51
+
52
+ ## 축과 기본값
53
+
54
+ | prop | 값 | 기본값 | 설명 |
55
+ | --- | --- | --- | --- |
56
+ | title / preview / metadata | string | 필수 | 상대·미리보기·현지화 시각 |
57
+ | avatar | ReactNode | 필수 | 제품 아바타 |
58
+ | unread | boolean | false | 읽지 않은 대화 강조 |
59
+
60
+ 앱은 라우팅, 접근 가능한 전체 이름, 데이터만 연결한다. 행의 내용은 host 이름과 중복 낭독되지 않는다.
61
+
62
+ ## 배치
63
+
64
+ | 항목 | 값 | 근거 |
65
+ | --- | --- | --- |
66
+ | 크기 | 아바타 56, 읽지 않음 점 10 | conversationRowRecipe |
67
+ | 간격 | 가로 sm, 세로 10 | 같은 recipe |
68
+ | 순서·정렬 | 아바타 → 제목/미리보기·시각 → 읽지 않음 점 | 양 renderer |
69
+ | 고정·스크롤 | 바깥 목록 host 소유 | 별도 scroller 없음 |
70
+ | 좁은 폭·큰 글자 | 가용 폭에서 미리보기 말줄임 | 양 renderer |
71
+
72
+ 메시지가 길어도 시각과 읽지 않음 점은 유지하고 미리보기만 말줄임한다. 앱은 별도 행 padding을 넣지 않는다.
73
+
74
+ ## 꼭 지킬 것
75
+
76
+ 전체 행의 현지화 접근성 이름과 라우팅 host를 함께 제공한다. HJM 내부 색·간격을 제품 CSS로 덮지 않는다.
77
+
78
+ ## 상태와 상호작용
79
+
80
+ 읽지 않음은 제목·미리보기를 강조하고 끝에 점을 표시한다. 클릭·키보드·내부 라우팅은
81
+ Next/Expo Link host에 남긴다. 시각 구성은 별도 button이나 link를 생성하지 않는다.
82
+
83
+ ## 접근성
84
+
85
+ host는 상대·미리보기·시각·읽지 않은 수가 포함된 현지화 이름을 제공한다. 내용은 장식으로 숨겨 중복 낭독을 피한다.
86
+
87
+ ## 제품 연결
88
+
89
+ 앱은 아바타 원본, 미리보기 생성, 시각 형식과 라우팅·읽음 정책을 결정한다. HJM에는 서버 데이터나 제품별 Link를 넣지 않는다.
90
+
91
+ ## 관련 지침
92
+
93
+ [선택 기준](../SELECTION.md), [ListRow](list-row.md), [ChatScreen](chat-screen.md).
@@ -135,3 +135,10 @@ Native 원형 leading의 `full`은 Provider token에서 읽지만 고정 원형
135
135
  예상하면 `description`을 제공한다(자리표시 중 문구 자체는 표시하지 않는다). loading 동안 행과
136
136
  trailingAction은 실행할 수 없으며 실제 슬롯 내용도 마운트하지 않는다. 여러 로딩 행 중 첫 행에만
137
137
  `loadingLabel`을 주고 나머지는 접근성 트리에서 숨긴다. 검색 화면도 임의 Skeleton 조립 대신 이 계약을 쓴다.
138
+
139
+ ### 독립 행동과 라우터 연결 (미게시, 1.23.0 이후)
140
+
141
+ Web도 Native와 같이 `trailingAction`을 지원한다. 이 슬롯은 행의 link/button 바깥에 놓이므로
142
+ 팔로우·삭제 같은 행동을 링크에 중첩하지 않는다. `renderLink`는 HJM이 만든 anchor props를
143
+ 프레임워크 Link에 전달하는 연결점이다. 배치·제목·앞/뒤 슬롯은 HJM이 계속 만든다.
144
+ 예: `renderLink={props => <Link {...props} href={props.href!} />}`.
@@ -70,6 +70,7 @@ import { ScreenLayout } from "@hjmds/react-native/screens";
70
70
  | prop | 값 | 기본값 | 설명 |
71
71
  | --- | --- | --- | --- |
72
72
  | `title` | `string` | 필수 | 화면 제목. `header`를 주면 접근성 이름으로만 쓴다 |
73
+ | `titleBadge` | `ReactNode` | 없음 | 기본 제목 바로 옆 배지. 값·의미는 제품, 간격·정렬은 HJM. `header` 교체 또는 제목 숨김이면 그리지 않는다. 1.23.0 추가 API |
73
74
  | `header` | `ReactNode` | 없음 | 기존 navigator 헤더 유지용. 주면 기본 헤더(`leading`·제목·`description`·`actions`)를 그리지 않는다 |
74
75
  | `description` | `string` | 없음 | 제목 아래 muted 설명 |
75
76
  | `leading` · `actions` | `ReactNode` | 없음 | 제목 앞(뒤로 가기)·뒤(도구) 슬롯 |
@@ -78,12 +79,14 @@ import { ScreenLayout } from "@hjmds/react-native/screens";
78
79
  | `state` | `{ kind: "ready" }` · `{ kind: "loading" \| "empty" \| "error" \| "restricted"; title; description? }` | `{ kind: "ready" }` | ready가 아니면 children을 그리지 않고 상태와 `stateAction`을 가운데에 놓는다. 로딩은 Spinner 하나만 보이고 `title`·`description`은 Spinner의 접근성 이름이 된다 |
79
80
  | `stateAction` | `ReactNode` | 없음 | 상태 안내 아래 행동(재시도 등) |
80
81
  | `scroll` | `"screen"` · `"content"` | `"screen"` | `content`는 본문 자식(가상화 목록)이 스크롤을 소유한다. 상태 교체 중에는 `screen`으로 돌아간다 |
82
+ | `contentAlignment` | `"start"` · `"center"` | `"start"` | ready 본문을 사용 영역 중앙에 배치. 긴 본문은 위가 잘리지 않게 스크롤한다. 로그인/제품 안내를 가짜 query state로 바꾸지 않는다(1.24.0) |
83
+ | `surface` | `"default"` · `"transparent"` | `"default"` | 투명은 제품 host의 배경·wallpaper를 보존. 배경색 style 우회 대신 사용(1.24.0) |
81
84
  | `contentInset` | `"default"` · `"none"` | `"default"` | `none`은 헤더·notice·본문·footer padding을 0으로(Native는 footer 위 테두리도 뺀다) |
82
85
  | `presentation` | `"dashboard"` · `"editorial"` · `"landscape"` | 가장 가까운 profile.screens.overview / 없으면 기존 골격 | [디자인 프로필](../../design-profile.md)의 제공 범위를 따른다. 명시값이 프로필을 이긴다. 헤더 배치·읽기 폭만 바꾸고 상태·입력·행동 슬롯은 유지한다 |
83
86
  | `as` (Web) | `"main"` · `"section"` | `"main"` | 제품 셸에 이미 `<main>`이 있으면 `section` |
84
87
  | `layoutStyle` | `HjmCompositionStyleProp` | 없음 | 화면 루트 배치 전용(예: 분할 화면의 `flex`·`width`). ScreenLayout 위에 만든 화면은 모두 이 prop을 루트까지 넘긴다 |
85
88
  | `className` (Web) | `string` | 없음 | 식별·배치 보조. 색·여백을 덮지 않는다 |
86
- | `testID` · `scrollRef` · `scrollProps` (Native) | `string` · `Ref<ScrollView>` · `refreshControl`·`keyboardDismissMode`·스크롤 막대 표시 | 없음 | ScrollView가 있을 때(`scroll="screen"` 또는 상태 교체 중)만 `scrollRef`·`scrollProps`가 연결된다 |
89
+ | `testID` · `scrollRef` · `scrollProps` (Native) | `string` · `Ref<ScrollView>` · `refreshControl`·`keyboardDismissMode`·스크롤 막대 표시·`onScroll`·`onScrollBeginDrag`·`onScrollEndDrag`·`scrollEventThrottle`·`alwaysBounceVertical`·`automaticallyAdjustKeyboardInsets` | 없음 | ScrollView가 있을 때(`scroll="screen"` 또는 상태 교체 중)만 `scrollRef`·`scrollProps`가 연결된다 |
87
90
 
88
91
  ## 배치
89
92
 
@@ -161,3 +161,10 @@ Web은 항목과 바깥 선택 영역의 크기를 함께 관찰한다. 2026-10-
161
161
  - 조작 가능 영역·글자는 움직이거나 확대하지 않는다. 새 사용 예제는 [선택 배경 이동](../compositions/selection-motion.md)이다.
162
162
 
163
163
  2026-10-07 큰 글자 UI 확인에서 Web 2배 글자 줄은 40px인데 pill 배경은 28px였다. Native처럼 세로 padding에도 pills.inset(spacing.xs=8px)을 적용해 배경이 전체 줄을 감싸게 했다. 고정 높이를 늘리는 대신 줄 높이를 따라가므로 3배·줄바꿈도 유지한다.
164
+
165
+ ### Text presentation (미게시, 1.23.0 이후)
166
+
167
+ `presentation="text"`는 캡슐 없이 라디오 항목과 짧은 선택 표시를 그린다. `textEmphasis="strong" | "regular"`
168
+ 기본 strong이며 조용한 관계 목록은 regular다. 기존 `items/value/onValueChange`와 키보드·disabled 계약을 유지한다.
169
+ 글자 탭의 radio 의미를 제품에서 재구현하던 문제를 없애기 위해 추가했다(2026-10-15 BurnTok 전사 이관).
170
+ 선택 표시는 16×2, 목표 높이는 44, 항목 간격은 md이며 text 모드는 slide artwork를 사용하지 않는다.
@@ -72,6 +72,7 @@ import { TopBar, TopBarAction } from "@hjmds/react-native/top-bar";
72
72
  | `headingLevel` | 1~6 | 1 | Web만. 페이지 구조에 맞춘다 |
73
73
  | `onTitleClick`(Web) | `(event: MouseEvent<HTMLButtonElement>) => void` | — | 제목을 버튼으로 만든다 |
74
74
  | `onTitlePress`(Native) | `(event: GestureResponderEvent) => void` | — | 제목을 버튼으로 만든다 |
75
+ | `subtitle` | `string` | — | 제목 아래 보조 문맥. 제목이 필요하며 배치·색·글자 규격은 HJM이 소유(1.24.0) |
75
76
  | `titleLeading` | `ReactNode` | — | 제목 앞 작은 시각 요소 |
76
77
  | `leading` / `actions`(별칭 `trailing`) | `ReactNode` | — | 둘 중 한 이름만 쓴다 |
77
78
  | `layoutStyle` | `HjmCompositionStyleProp` | — | 루트 배치. Web·Native 모두 |
@@ -4,12 +4,14 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.14.0
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: 공개 API를 사용하는 `showcase/*/reference-adoption-previews.tsx`
9
9
  - 스토리북: `배포/구성/피드백과 복구/버튼 완료 피드백`
10
10
 
11
11
  승급: 2026-10-07 사용자 승인, [검토 결과](../../../../../docs/qa/2026-10-07-experiment-promotion-release.md). Storybook 분류이며 제품 적용 증거는 별도다.
12
12
 
13
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 구성 API가 없는 콘텐츠 묶음은 [CompositionLayout](../components/composition-layout.md)의 제목·배지·행동·본문·상태·footer 슬롯을 사용한다.
14
+
13
15
  ## 언제 쓰나
14
16
 
15
17
  입력을 유지하며 저장의 진행·성공·재시도 가능 실패를 보여 줄 때 쓴다. 화면 wrapper 없이 아래 공개 컴포넌트와 `resolveSaveFeedback`을 조합한다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [공통 실행과 실패 복구](../../action-session.md), `src/action-session.ts`, `showcase/web/src/patterns/action-recovery-previews.tsx`, `showcase/native/src/action-recovery-previews.tsx`. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/구성/공통 동작/즉시 반영과 복구`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/구성/피드백과 복구/즉시 반영과 복구`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 구성 API가 없는 콘텐츠 묶음은 [CompositionLayout](../components/composition-layout.md)의 제목·배지·행동·본문·상태·footer 슬롯을 사용한다.
12
+
11
13
  ## 언제 쓰나
12
14
 
13
15
  북마크·좋아요·알림 켜기처럼 되돌려도 피해가 없는 저위험 토글을 누르는 즉시 화면에 반영하고, 서버가 실패하면 직전 확인 값으로 되돌릴 때 쓴다.
@@ -28,7 +30,7 @@
28
30
  | `Button` 다시 반영 | error일 때만 나타나는 보조 행동, `tone="secondary"` | [Button](../components/button.md) |
29
31
  | `Text` | 상태 문구. Web `role="status"`, Native 접근성 알림 | [Text](../components/text.md) |
30
32
  | `Stack` | 세로 쌓기 `gap="md"` | [Stack](../components/stack.md) |
31
- | `Container` · `ScrollView` | 바깥 틀. 제품 화면이 소유한다 | [Container](../components/container.md), [화면 여백](../tokens/layout.md) |
33
+ | `Container` · `ScrollView` | 바깥 틀. HJM 화면 골격이 소유한다 | [Container](../components/container.md), [화면 여백](../tokens/layout.md) |
32
34
 
33
35
  스토리의 `tone="ghost"` "서버에서 새 상태 불러오기"는 `reset`이 지난 응답을 버리는 것을 보여 주는 데모 버튼이다.
34
36
  제품에서는 이 동작을 버튼이 아니라 재조회·계정 변경 시점에 연결한다. "다음 요청 실패시키기"와 350ms 지연도 데모 전용이다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [공통 실행과 실패 복구](../../action-session.md), `src/action-session.ts`, `showcase/web/src/patterns/action-recovery-previews.tsx`, `showcase/native/src/action-recovery-previews.tsx`. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/구성/공통 동작/저장과 재시도`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/구성/피드백과 복구/저장과 재시도`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 구성 API가 없는 콘텐츠 묶음은 [CompositionLayout](../components/composition-layout.md)의 제목·배지·행동·본문·상태·footer 슬롯을 사용한다.
12
+
11
13
  ## 언제 쓰나
12
14
 
13
15
  입력한 내용을 서버에 저장하는 폼 한 덩어리에서 저장 중 중복 실행을 막고, 실패하면 입력을 지우지 않은 채 제출했던 값 그대로 다시 보낼 때 쓴다.
@@ -30,7 +32,7 @@
30
32
  | `Button` 다시 저장 | error일 때만 나타나는 보조 행동, `tone="secondary"` | [Button](../components/button.md) |
31
33
  | `Text` | 상태 문구(진행·성공·실패). Web `role="status"`, Native 접근성 알림. 확정 값 줄(선택) | [Text](../components/text.md) |
32
34
  | `Stack` | 세로 쌓기 `gap="md"` | [Stack](../components/stack.md) |
33
- | `Container` · `ScrollView` | 바깥 틀. 제품 화면이 소유한다 | [Container](../components/container.md), [화면 여백](../tokens/layout.md) |
35
+ | `Container` · `ScrollView` | 바깥 틀. HJM 화면 골격이 소유한다 | [Container](../components/container.md), [화면 여백](../tokens/layout.md) |
34
36
 
35
37
  스토리의 "다음 요청 실패시키기"(secondary 토글)와 350ms 지연은 데모 전용이다. 제품에 넣지 않는다.
36
38