@hjmds/design-contracts 1.17.2 → 1.19.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 (274) hide show
  1. package/dist/action-session.d.ts +16 -0
  2. package/dist/action-session.d.ts.map +1 -1
  3. package/dist/action-session.js +18 -0
  4. package/dist/action-session.js.map +1 -1
  5. package/dist/activity-chip.d.ts +127 -0
  6. package/dist/activity-chip.d.ts.map +1 -0
  7. package/dist/activity-chip.js +65 -0
  8. package/dist/activity-chip.js.map +1 -0
  9. package/dist/base-recipes.d.ts +1 -1
  10. package/dist/base-recipes.d.ts.map +1 -1
  11. package/dist/base-recipes.js +2 -1
  12. package/dist/base-recipes.js.map +1 -1
  13. package/dist/behaviors.d.ts +9 -6
  14. package/dist/behaviors.d.ts.map +1 -1
  15. package/dist/behaviors.js +5 -3
  16. package/dist/behaviors.js.map +1 -1
  17. package/dist/bottom-navigation.d.ts +16 -0
  18. package/dist/bottom-navigation.d.ts.map +1 -1
  19. package/dist/bottom-navigation.js +18 -0
  20. package/dist/bottom-navigation.js.map +1 -1
  21. package/dist/card.d.ts +4 -0
  22. package/dist/card.d.ts.map +1 -1
  23. package/dist/card.js +4 -1
  24. package/dist/card.js.map +1 -1
  25. package/dist/carousel.d.ts +3 -0
  26. package/dist/carousel.d.ts.map +1 -1
  27. package/dist/carousel.js.map +1 -1
  28. package/dist/catalog.d.ts +80 -9
  29. package/dist/catalog.d.ts.map +1 -1
  30. package/dist/component-recipes.d.ts +74 -3
  31. package/dist/component-recipes.d.ts.map +1 -1
  32. package/dist/component-recipes.js +38 -6
  33. package/dist/component-recipes.js.map +1 -1
  34. package/dist/content-transition.d.ts +1 -1
  35. package/dist/content-transition.d.ts.map +1 -1
  36. package/dist/content-transition.js +2 -2
  37. package/dist/content-transition.js.map +1 -1
  38. package/dist/field-group.d.ts +14 -0
  39. package/dist/field-group.d.ts.map +1 -1
  40. package/dist/field-group.js +9 -0
  41. package/dist/field-group.js.map +1 -1
  42. package/dist/form.d.ts.map +1 -1
  43. package/dist/form.js +10 -3
  44. package/dist/form.js.map +1 -1
  45. package/dist/icon-button-recipe.d.ts +1 -5
  46. package/dist/icon-button-recipe.d.ts.map +1 -1
  47. package/dist/icon-button-recipe.js +2 -1
  48. package/dist/icon-button-recipe.js.map +1 -1
  49. package/dist/index.d.ts +3 -2
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +2 -0
  52. package/dist/index.js.map +1 -1
  53. package/dist/screen-patterns.d.ts +10 -0
  54. package/dist/screen-patterns.d.ts.map +1 -1
  55. package/dist/screen-patterns.js +17 -3
  56. package/dist/screen-patterns.js.map +1 -1
  57. package/dist/toast-liquid.d.ts +1 -1
  58. package/dist/toast-liquid.d.ts.map +1 -1
  59. package/dist/toast-liquid.js +3 -2
  60. package/dist/toast-liquid.js.map +1 -1
  61. package/dist/toast.d.ts +29 -2
  62. package/dist/toast.d.ts.map +1 -1
  63. package/dist/toast.js +59 -3
  64. package/dist/toast.js.map +1 -1
  65. package/dist/version.d.ts +1 -1
  66. package/dist/version.js +1 -1
  67. package/dist/version.js.map +1 -1
  68. package/docs/action-session.md +2 -0
  69. package/docs/activity-chip.md +34 -0
  70. package/docs/asset.md +7 -0
  71. package/docs/bottom-navigation.md +16 -1
  72. package/docs/brand-boundary.md +3 -3
  73. package/docs/collection-rail.md +4 -0
  74. package/docs/compound-controls.md +8 -3
  75. package/docs/consumer-policy.md +39 -120
  76. package/docs/generated/component-maturity.md +1 -1
  77. package/docs/generated/renderer-evidence.json +3 -3
  78. package/docs/generated/renderer-evidence.md +1 -1
  79. package/docs/generated/showcase-manifest.json +1 -1
  80. package/docs/gravity-letters.md +2 -0
  81. package/docs/grid-reveal.md +2 -0
  82. package/docs/layout.md +2 -2
  83. package/docs/load-more.md +5 -0
  84. package/docs/migration-native-legacy-removal.md +30 -17
  85. package/docs/optional-adapters.md +4 -0
  86. package/docs/screen-patterns.md +23 -1
  87. package/docs/toast.md +64 -2
  88. package/docs/usage/README.md +248 -243
  89. package/docs/usage/SELECTION.md +43 -0
  90. package/docs/usage/STANDARD.md +22 -10
  91. package/docs/usage/components/accordion.md +5 -3
  92. package/docs/usage/components/activity-chip.md +109 -0
  93. package/docs/usage/components/agreement.md +6 -9
  94. package/docs/usage/components/alert-dialog.md +12 -6
  95. package/docs/usage/components/asset.md +10 -4
  96. package/docs/usage/components/auth-provider-button.md +2 -2
  97. package/docs/usage/components/avatar.md +8 -6
  98. package/docs/usage/components/badge.md +4 -4
  99. package/docs/usage/components/bottom-cta.md +4 -4
  100. package/docs/usage/components/bottom-info.md +2 -2
  101. package/docs/usage/components/bottom-navigation.md +18 -6
  102. package/docs/usage/components/button.md +6 -3
  103. package/docs/usage/components/card.md +28 -7
  104. package/docs/usage/components/carousel.md +12 -4
  105. package/docs/usage/components/chat-message.md +10 -2
  106. package/docs/usage/components/chat-screen.md +5 -5
  107. package/docs/usage/components/checkbox-group.md +1 -1
  108. package/docs/usage/components/checkbox.md +2 -2
  109. package/docs/usage/components/chip.md +2 -2
  110. package/docs/usage/components/code-block.md +3 -4
  111. package/docs/usage/components/collapsible.md +6 -4
  112. package/docs/usage/components/combobox.md +2 -2
  113. package/docs/usage/components/command-palette.md +1 -1
  114. package/docs/usage/components/comment-thread-screen.md +7 -4
  115. package/docs/usage/components/container.md +1 -1
  116. package/docs/usage/components/content-transition.md +3 -2
  117. package/docs/usage/components/context-menu.md +2 -2
  118. package/docs/usage/components/counter-badge.md +3 -3
  119. package/docs/usage/components/data-table.md +1 -1
  120. package/docs/usage/components/date-picker.md +4 -4
  121. package/docs/usage/components/description-list.md +3 -3
  122. package/docs/usage/components/design-system-provider.md +2 -2
  123. package/docs/usage/components/dialog.md +14 -3
  124. package/docs/usage/components/divider.md +2 -2
  125. package/docs/usage/components/editor-screen.md +5 -3
  126. package/docs/usage/components/effect-surface.md +1 -1
  127. package/docs/usage/components/empty-state.md +6 -6
  128. package/docs/usage/components/field.md +5 -3
  129. package/docs/usage/components/file-picker.md +6 -3
  130. package/docs/usage/components/form.md +1 -1
  131. package/docs/usage/components/heading.md +3 -3
  132. package/docs/usage/components/icon-button.md +6 -3
  133. package/docs/usage/components/icon.md +1 -1
  134. package/docs/usage/components/image.md +5 -4
  135. package/docs/usage/components/keyboard-dock.md +1 -1
  136. package/docs/usage/components/layout.md +1 -1
  137. package/docs/usage/components/link.md +2 -2
  138. package/docs/usage/components/list-detail-screen.md +6 -6
  139. package/docs/usage/components/list-row.md +13 -4
  140. package/docs/usage/components/list.md +5 -4
  141. package/docs/usage/components/masonry.md +2 -1
  142. package/docs/usage/components/media-selection-screen.md +11 -5
  143. package/docs/usage/components/mentions.md +2 -2
  144. package/docs/usage/components/menu.md +3 -2
  145. package/docs/usage/components/menubar.md +2 -2
  146. package/docs/usage/components/message-composer.md +3 -3
  147. package/docs/usage/components/moderation-screen.md +3 -2
  148. package/docs/usage/components/notice.md +2 -2
  149. package/docs/usage/components/notification-inbox-screen.md +1 -1
  150. package/docs/usage/components/notification-item.md +2 -2
  151. package/docs/usage/components/number-field.md +3 -3
  152. package/docs/usage/components/otp-field.md +1 -1
  153. package/docs/usage/components/overview-screen.md +1 -1
  154. package/docs/usage/components/permission-screen.md +1 -1
  155. package/docs/usage/components/photo-source-sheet.md +1 -1
  156. package/docs/usage/components/popover.md +2 -2
  157. package/docs/usage/components/profile-screen.md +1 -1
  158. package/docs/usage/components/progress.md +2 -2
  159. package/docs/usage/components/qr-code.md +2 -2
  160. package/docs/usage/components/radio-group.md +1 -2
  161. package/docs/usage/components/radio.md +1 -2
  162. package/docs/usage/components/result.md +1 -1
  163. package/docs/usage/components/saved-items-screen.md +4 -2
  164. package/docs/usage/components/screen-layout.md +4 -3
  165. package/docs/usage/components/search-field.md +1 -1
  166. package/docs/usage/components/search-screen.md +4 -4
  167. package/docs/usage/components/section.md +3 -3
  168. package/docs/usage/components/segmented-control.md +4 -4
  169. package/docs/usage/components/select.md +2 -3
  170. package/docs/usage/components/settings-screen.md +18 -56
  171. package/docs/usage/components/sheet.md +10 -5
  172. package/docs/usage/components/sidebar.md +1 -1
  173. package/docs/usage/components/skeleton.md +1 -1
  174. package/docs/usage/components/skip-nav.md +1 -1
  175. package/docs/usage/components/slider.md +2 -2
  176. package/docs/usage/components/spinner.md +2 -2
  177. package/docs/usage/components/splitter.md +1 -1
  178. package/docs/usage/components/statistic.md +5 -4
  179. package/docs/usage/components/steps.md +2 -2
  180. package/docs/usage/components/surface.md +2 -3
  181. package/docs/usage/components/switch.md +1 -1
  182. package/docs/usage/components/tabs.md +4 -4
  183. package/docs/usage/components/tag.md +2 -2
  184. package/docs/usage/components/tags-input.md +4 -4
  185. package/docs/usage/components/text-area.md +5 -3
  186. package/docs/usage/components/text.md +85 -4
  187. package/docs/usage/components/timeline.md +2 -2
  188. package/docs/usage/components/toast.md +41 -13
  189. package/docs/usage/components/toggle-group.md +3 -3
  190. package/docs/usage/components/tooltip.md +1 -1
  191. package/docs/usage/components/top-bar.md +5 -4
  192. package/docs/usage/components/top.md +3 -3
  193. package/docs/usage/components/transfer-list.md +5 -4
  194. package/docs/usage/components/upload-item.md +3 -3
  195. package/docs/usage/components/virtual-list.md +1 -0
  196. package/docs/usage/compositions/action-feedback.md +42 -6
  197. package/docs/usage/compositions/action-recovery-optimistic.md +6 -1
  198. package/docs/usage/compositions/action-recovery-save.md +19 -23
  199. package/docs/usage/compositions/action-recovery-undo.md +6 -1
  200. package/docs/usage/compositions/activity-status.md +168 -0
  201. package/docs/usage/compositions/collection-detail.md +2 -0
  202. package/docs/usage/compositions/command-records.md +15 -5
  203. package/docs/usage/compositions/common-message.md +4 -2
  204. package/docs/usage/compositions/common-notification.md +5 -3
  205. package/docs/usage/compositions/compound-controls.md +13 -5
  206. package/docs/usage/compositions/content-transition-comparison.md +2 -2
  207. package/docs/usage/compositions/date-entry.md +10 -5
  208. package/docs/usage/compositions/date-time-selection.md +3 -3
  209. package/docs/usage/compositions/design-profile-comparison.md +7 -5
  210. package/docs/usage/compositions/disclosure.md +1 -1
  211. package/docs/usage/compositions/document-resource.md +10 -3
  212. package/docs/usage/compositions/expo-interactions.md +3 -2
  213. package/docs/usage/compositions/family-drawer.md +9 -4
  214. package/docs/usage/compositions/field-group.md +7 -1
  215. package/docs/usage/compositions/floating-action-button.md +18 -18
  216. package/docs/usage/compositions/illustrated-outcome.md +3 -2
  217. package/docs/usage/compositions/input-sheet.md +6 -2
  218. package/docs/usage/compositions/interaction-adapters.md +1 -1
  219. package/docs/usage/compositions/interaction-flow-apply.md +6 -3
  220. package/docs/usage/compositions/interaction-flow-draft.md +5 -2
  221. package/docs/usage/compositions/interaction-flow-search.md +5 -2
  222. package/docs/usage/compositions/live-list.md +7 -5
  223. package/docs/usage/compositions/navigation-bar-collection.md +8 -7
  224. package/docs/usage/compositions/optional-adapters.md +1 -1
  225. package/docs/usage/compositions/origin-dialog.md +5 -5
  226. package/docs/usage/compositions/paper-surface.md +1 -1
  227. package/docs/usage/compositions/photo-source.md +1 -1
  228. package/docs/usage/compositions/purpose-input-comment.md +5 -3
  229. package/docs/usage/compositions/purpose-input-message.md +5 -3
  230. package/docs/usage/compositions/reference-first.md +63 -42
  231. package/docs/usage/compositions/reference-review.md +60 -50
  232. package/docs/usage/compositions/reference-settings.md +60 -50
  233. package/docs/usage/compositions/selection-scope.md +48 -35
  234. package/docs/usage/compositions/stea-order-progress.md +1 -0
  235. package/docs/usage/compositions/stea-otp-verify.md +5 -3
  236. package/docs/usage/compositions/stea-pixel-empty.md +3 -3
  237. package/docs/usage/compositions/time-selection.md +6 -5
  238. package/docs/usage/compositions/upload-recovery.md +10 -1
  239. package/docs/usage/compositions/video-dialog.md +10 -3
  240. package/docs/usage/compositions/web-additions.md +5 -5
  241. package/docs/usage/compositions/web-navigation.md +1 -1
  242. package/docs/usage/screens/common-chat.md +21 -8
  243. package/docs/usage/screens/common-comments.md +13 -1
  244. package/docs/usage/screens/common-inbox.md +19 -6
  245. package/docs/usage/screens/common-login.md +19 -7
  246. package/docs/usage/screens/common-profile.md +22 -9
  247. package/docs/usage/screens/common-saved.md +16 -2
  248. package/docs/usage/screens/common-search.md +24 -4
  249. package/docs/usage/screens/common-settings.md +22 -50
  250. package/docs/usage/screens/common-shell.md +14 -2
  251. package/docs/usage/screens/dashboard.md +52 -109
  252. package/docs/usage/screens/discovery-gallery.md +57 -148
  253. package/docs/usage/screens/flow-collection.md +38 -9
  254. package/docs/usage/screens/flow-editor.md +25 -7
  255. package/docs/usage/screens/flow-media.md +23 -7
  256. package/docs/usage/screens/flow-moderation.md +15 -2
  257. package/docs/usage/screens/flow-onboarding.md +3 -3
  258. package/docs/usage/screens/flow-permission.md +17 -3
  259. package/docs/usage/screens/landing.md +37 -20
  260. package/docs/usage/screens/mockup-studio.md +22 -7
  261. package/docs/usage/screens/notification-settings.md +18 -4
  262. package/docs/usage/screens/product-bento.md +18 -9
  263. package/docs/usage/screens/reference-comparison.md +29 -10
  264. package/docs/usage/tokens/color.md +6 -3
  265. package/docs/usage/tokens/elevation-opacity.md +4 -2
  266. package/docs/usage/tokens/font-roles.md +2 -2
  267. package/docs/usage/tokens/layers.md +3 -3
  268. package/docs/usage/tokens/motion.md +2 -2
  269. package/docs/usage/tokens/radius.md +4 -2
  270. package/docs/usage/tokens/spacing.md +3 -3
  271. package/docs/usage/tokens/theme-studio.md +2 -2
  272. package/docs/usage/tokens/typography-studio.md +14 -8
  273. package/docs/usage/tokens/typography.md +15 -5
  274. package/package.json +7 -1
@@ -91,7 +91,7 @@ import { DatePicker } from "@hjmds/react-native/date-picker";
91
91
  | `renderCellContent` | `(cell: ResolvedCalendarDateCell) => ReactNode` | — | 날짜 아래 보조 표시 |
92
92
  | Web `onNavigateBeyondGrid` | `(detail: { date, intent, overflow: "before" \| "after" }, focusDate: (date: string) => void) => void` | — | 키보드가 격자 밖으로 나갈 때 |
93
93
  | Native `safeAreaInsets` | Sheet `safeAreaInsets` | Provider 값 | — |
94
- | `layoutStyle` | 배치 전용 style | — | 루트 배치. Native `style`은 deprecated |
94
+ | `layoutStyle` | 배치 전용 style | — | 루트 배치. Native `style`은 제거된 속성 |
95
95
 
96
96
  ## 배치
97
97
 
@@ -99,7 +99,7 @@ Native trigger 모서리는 `datePickerRecipe.frame.radius` 역할을 Provider
99
99
 
100
100
  | 항목 | 값 | 근거 |
101
101
  | --- | --- | --- |
102
- | 크기 | 트리거 높이: `medium` 44 · `large` 52(`datePickerRecipe.sizes`, 두 플랫폼), 좌우 여백 16 · 20. 2026-10-06까지 렌더 값이 Web 44·56, Native 48·56이었다(1.12.1 이후 미게시). 지우기 버튼 44×44. Web 팝오버 폭 `min(22.5rem, 100vw − 2rem)`(최대 360). 날짜 셀 44, 7열 | `datePickerRecipe.sizes`, `packages/react/src/styles.css` `.hjm-date-picker*`, `packages/react-native/src/date-picker.tsx`, `src/calendar.ts` |
102
+ | 크기 | 트리거 높이: `medium` 44 · `large` 52(`datePickerRecipe.sizes`, 두 플랫폼), 좌우 여백 16 · 20. 2026-10-06까지 렌더 값이 Web 44·56, Native 48·56이었다(1.13.0 변경). 지우기 버튼 44×44. Web 팝오버 폭 `min(22.5rem, 100vw − 2rem)`(최대 360). 날짜 셀 44, 7열 | `datePickerRecipe.sizes`, `packages/react/src/styles.css` `.hjm-date-picker*`, `packages/react-native/src/date-picker.tsx`, `src/calendar.ts` |
103
103
  | 간격 | 라벨·트리거·설명·오류 사이 Web `spacing.xs` 8, Native 6(렌더러 고정값). 트리거 가로 여백 `medium` `spacing.md` 16, `large` `spacing.lg` 20. 팝오버는 트리거 아래 `spacing.xs` 8, 안쪽 여백 `spacing.sm` 12 | 같은 파일 |
104
104
  | 순서·정렬 | 폼 안에서 다른 필드와 같은 폭으로 세로로 쌓는다. 위에서 라벨 → 트리거 → 설명 → 오류. 지우기 버튼(값이 있을 때)은 Web은 트리거 안 끝에 겹치고(트리거가 끝 여백 3rem을 비움), Native는 트리거 바깥 끝에 최소 터치 영역으로 붙는다. Web 팝오버는 시작 쪽 정렬 | `packages/react/src/styles.css`, `packages/react-native/src/date-picker.tsx` |
105
105
  | 고정·스크롤 | Web 팝오버는 필드에 붙어 층 800에 뜬다. Native 달력은 화면 아래에서 올라오는 [Sheet](sheet.md)이며 하단 안전 영역은 Provider `safeAreaInsets`를 쓴다 | 같은 파일 |
@@ -125,7 +125,7 @@ Web Native
125
125
  - 모든 문구(라벨·placeholder·`clearLabel`·`closeLabel`·월 이동 라벨·셀 이름)는 i18n 키로 만든다.
126
126
  셀 이름(`composeAccessibleName`)의 어순·문법은 제품이 소유한다.
127
127
  - 달을 넘기면 제품이 새 달의 `cells`와 `monthLabel`을 다시 만들어 넘긴다. 달을 바꿔도 선택값은 바뀌지 않는다.
128
- - 배치는 `layoutStyle`로 한다(Web·Native). 트리거 색·높이를 덮지 않는다. Native `style`은 deprecated — `layoutStyle` 또는 `size`로 옮긴다.
128
+ - 배치는 `layoutStyle`로 한다(Web·Native). 트리거 색·높이를 덮지 않는다. Native `style`은 제거된 속성 — `layoutStyle` 또는 `size`로 옮긴다.
129
129
 
130
130
  ## 플랫폼 차이
131
131
 
@@ -135,7 +135,7 @@ Web Native
135
135
  | `description`·`error` | `ReactNode` | `string` |
136
136
  | 격자 밖 이동 알림 `onNavigateBeyondGrid` | 있음 | 없음 |
137
137
  | 하단 안전 영역 `safeAreaInsets` | 없음 | 있음(기본은 Provider 값) |
138
- | 배치 | `layoutStyle`(그 밖에 `className`) | `layoutStyle`(`style`은 deprecated) |
138
+ | 배치 | `layoutStyle`(그 밖에 `className`) | `layoutStyle`(`style`은 제거된 속성) |
139
139
 
140
140
  ## 함정
141
141
 
@@ -66,7 +66,7 @@ import { DescriptionList } from "@hjmds/react-native/data-display";
66
66
  | 항목 | `{ id, label: string, value: string }` | — (필수) | 셋 다 빈 문자열이면 안 된다. 항목이 없거나 `id`가 겹치면 예외가 난다 |
67
67
  | Native `label` | `string` | — (필수) | 목록 전체의 접근성 이름 |
68
68
  | Native `availableWidth` | 양수 | 측정값 | 주면 측정보다 우선 |
69
- | `layoutStyle` | 배치 전용 style | — | 루트 배치. Native `style`·`itemStyle`은 deprecated |
69
+ | `layoutStyle` | 배치 전용 style | — | 루트 배치. Native `style`·`itemStyle`은 제거된 속성 |
70
70
 
71
71
  콜백 prop은 없다.
72
72
 
@@ -86,7 +86,7 @@ import { DescriptionList } from "@hjmds/react-native/data-display";
86
86
  - 큰 글자 대응으로 화면에서 `fontScale`을 보고 `columns={1}`을 넘기지 않는다. 접힘은 resolver가 한다
87
87
  ([계약](../../description-list.md#큰-글자에서의-전환--resolver가-소유)).
88
88
  - 라벨은 i18n 키로 넣는다. Native는 목록 전체의 접근성 이름 `label`이 필수다.
89
- - 배치는 `layoutStyle`로만 한다(Web·Native). 색·글자 스타일을 덮지 않는다. Native `style`·`itemStyle`은 deprecated — 배치는 `layoutStyle`, 열 배치는 `descriptor.columns`로 옮긴다.
89
+ - 배치는 `layoutStyle`로만 한다(Web·Native). 색·글자 스타일을 덮지 않는다. Native `style`·`itemStyle`은 제거된 속성 — 배치는 `layoutStyle`, 열 배치는 `descriptor.columns`로 옮긴다.
90
90
 
91
91
  ## 플랫폼 차이
92
92
 
@@ -95,7 +95,7 @@ import { DescriptionList } from "@hjmds/react-native/data-display";
95
95
  | 데이터 전달 | `items`, `columns` 평면 prop | `descriptor={{ items, columns }}` |
96
96
  | 목록 이름 | 없음(`dl`/`dt`/`dd` 의미 구조) | `label` 필수, `accessibilityRole="list"` |
97
97
  | 폭 측정 | 요소 폭 측정 | `availableWidth`가 우선, 없으면 `onLayout` 측정, 첫 프레임은 창 폭 |
98
- | 배치 prop | `layoutStyle`(`style`과 합친다) | `layoutStyle`(`style`·`itemStyle`은 deprecated) |
98
+ | 배치 prop | `layoutStyle`(`style`과 합친다) | `layoutStyle`(`style`·`itemStyle`은 제거된 속성) |
99
99
 
100
100
  ## 함정
101
101
 
@@ -86,9 +86,9 @@ expect(checkBrandPaletteContrast(PRODUCT_BRAND_PALETTE)).toEqual({ light: [], da
86
86
  | `reducedMotion` | `true` · `false` | `false` | — |
87
87
  | `minimumVisualTarget` | `true` · `false` | `false` | — |
88
88
  | `brandPalette` | `{ light?, dark? }` | — | 각각 `ThemeColors` 17개 key 중 필요한 것만 넘긴다(부분 병합). 상태 강조색은 덮을 수 없다. 중첩 Provider는 가장 가까운 상위의 값을 물려받는다 |
89
- | `designProfile` | `HjmDesignProfile` | 가장 가까운 상위 프로필 또는 없음 | 기본 프로필은1.15.0 게시. `hjmDesignPresets` 또는 `defineHjmDesignProfile` 결과만 넣는다. 후속 글자 역할·질감/구성 개선은 아직 미게시다. [프로필 계약](../../design-profile.md)의 토큰·질감·전환·구성·화면 기본값을 상속한다 |
89
+ | `designProfile` | `HjmDesignProfile` | 가장 가까운 상위 프로필 또는 없음 | 기본 프로필·표면 질감은 1.15.0 추가. `hjmDesignPresets` 또는 `defineHjmDesignProfile` 결과만 넣는다. 글자 역할·관련 구성 개선은 1.16.0 변경. [프로필 계약](../../design-profile.md)의 토큰·질감·전환·구성·화면 기본값을 상속한다 |
90
90
  | `value` | `DesignSystemProviderValue`(`resolveDesignSystemProviderValue` 결과) | — | 테스트·스토리·임베딩용. 환경 prop·`brandPalette`·`designProfile`과 함께 쓸 수 없고(타입이 막는다), 주면 OS theme·모션 관찰과 상위 `brandPalette` 상속이 멈춘다 |
91
- | `surfaceEffects`(Native) | `{ renderBackdrop?, insetShadows? }` | 상위 host 또는 없음 | 미게시 실험. 실제 blur host·inset capability를 한 번 등록하며 `{}`로 하위 영역을 해제. [Surface 질감 계약](../../design-profile.md#surfacecard의-유리클레이-질감) |
91
+ | `surfaceEffects`(Native) | `{ renderBackdrop?, insetShadows? }` | 상위 host 또는 없음 | 1.15.0 실험 API. 실제 blur host·inset capability를 한 번 등록하며 `{}`로 하위 영역을 해제. [Surface 질감 계약](../../design-profile.md#surfacecard의-유리클레이-질감) |
92
92
  | `safeAreaInsets`(Native) | `{ top?, right?, bottom?, left? }`(pt) | `{}` | 보통 `useSafeAreaInsets()` 결과. 중첩 Provider는 가장 가까운 상위 값을 물려받는다 |
93
93
 
94
94
  - 제품 상태를 바꾸는 이벤트 prop은 없다. Native의 선택형 `surfaceEffects.renderBackdrop`은 장식 host만 공급한다. 해석된 값은 Web `useHjmTheme()`, Native `useHjmNativeTheme()`로 읽는다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-07
7
+ - 검토일: 2026-10-13
8
8
  - 근거: [Dialog](../../dialog.md), [명령형 오버레이](../../overlay-stack.md), recipe `dialogRecipe`(`src/component-recipes.ts`)
9
9
  - 스토리북: `배포/컴포넌트/오버레이/대화상자`
10
10
 
@@ -49,6 +49,7 @@ import { Dialog } from "@hjmds/react/overlays";
49
49
  <Dialog
50
50
  open={open}
51
51
  onOpenChange={(next) => setOpen(next)}
52
+ returnFocusRef={openButtonRef}
52
53
  title={t("album.rename.title")}
53
54
  description={t("album.rename.description")}
54
55
  closeLabel={t("common.close")}
@@ -64,6 +65,11 @@ import { Dialog } from "@hjmds/react/overlays";
64
65
  </Dialog>
65
66
  ```
66
67
 
68
+ Web에서 바깥 버튼으로 `open`을 제어할 때는 그 버튼의 ref를 `returnFocusRef`로 연결한다.
69
+ 위 `openButtonRef`는 `useRef<HTMLButtonElement>(null)`로 만들고 여는 버튼의 `ref`에도 준다.
70
+ macOS WebKit은 버튼 클릭이 초점을 옮기지 않을 수 있어, 열기 직전 `activeElement`만으로는
71
+ 닫힌 뒤 호출 위치를 복구할 수 없다(2026-10-13 브라우저 대조). `trigger` 슬롯을 사용하면 이 연결은 내부에서 처리한다.
72
+
67
73
  ```tsx
68
74
  // Native
69
75
  import { Dialog } from "@hjmds/react-native/overlays";
@@ -89,6 +95,11 @@ Native `save`는 저장 Promise를 반환한다. Dialog가 완료를 기다리
89
95
  이벤트 밖에서 시작한 요청은 외부 `busy`로 연결한다. 이전 즉시 닫힘 대응 예제가 현재 Promise 계약과 충돌해
90
96
  2026-10-06 독립 재구현 검증에서 교정했다.
91
97
 
98
+ Native Dialog는 화면 하단에 붙은 키보드가 나타나면 그 위의 사용 가능한 영역에 가운데 정렬한다.
99
+ 입력 본문은 내부에서 스크롤하고 제목·닫기·저장 행동은 그 밖에 유지한다. 별도 KeyboardAvoidingView를
100
+ 중첩하지 않는다. iOS 입력 예제에서 저장 버튼이 키보드에 가려진 재현을 바탕으로 Sheet와 같은 좌표 계산을
101
+ 공유한다(2026-10-13). Android가 이미 줄인 모달 높이는 다시 빼지 않으며 떠 있는 키보드는 이 여백 계산에서 제외한다.
102
+
92
103
  ```tsx
93
104
  // Web 명령형: 앱 루트에 <OverlayStackProvider>를 두고
94
105
  import { useDialog } from "@hjmds/react/overlay-stack";
@@ -111,8 +122,8 @@ await handle.closed; // portal 제거·초점 복구 뒤. 다음 오버레이는
111
122
  | `onOpenChange` | `(open: boolean, detail: { reason }) => void` | — | Web `reason`: `"trigger"`·`"close-action"`·`"escape"`·`"outside"`. Native: `"close-action"`·`"back"`·`"outside"`. `busy`이거나 `dismissible={false}`면 닫기 요청을 보내지 않는다 |
112
123
  | `onDismissComplete`(Web) | `(detail: { reason: "close-action" \| "escape" \| "outside" \| "programmatic" }) => void` | — | 한 번 열릴 때마다 한 번, portal 제거·초점 복구 뒤. 다음 오버레이는 여기서 연다 |
113
124
  | `trigger`(Web) | `ReactElement`(버튼 요소) | — | 누르면 `reason: "trigger"`로 열린다 |
114
- | `primaryAction`·`secondaryAction`(Native) | `{ label, onPress: () => void \| Promise<void>, tone?, disabled?, accessibilityHint? }` | — | 동기 함수는 같은 누름에서 닫힌다. Promise를 반환하면 기다리는 동안 누른 버튼이 loading이 되고 행동·닫기·back·바깥 닫기가 모두 막히며, 성공하면 `close-action`으로 닫힌다. 던지거나 거절되면 열린 채 행동이 다시 켜지고 `onActionError`를 호출한다. 외부 `busy`도 반복 행동·닫기를 막는다. 미게시(1.12.1 이후) — 1.12.1은 Promise를 무시하고 바로 닫았다 |
115
- | `onActionError`(Native) | `(error: unknown) => void` | — | action 실패를 본문 오류·재시도로 연결한다. 오류를 처리하는 동안 초안을 보존한다. 없으면 개발 빌드에서 실패마다 `console.error`를 남긴다(중복 제거 없음). 미게시(1.12.1 이후) |
125
+ | `primaryAction`·`secondaryAction`(Native) | `{ label, onPress: () => void \| Promise<void>, tone?, disabled?, accessibilityHint? }` | — | 동기 함수는 같은 누름에서 닫힌다. Promise를 반환하면 기다리는 동안 누른 버튼이 loading이 되고 행동·닫기·back·바깥 닫기가 모두 막히며, 성공하면 `close-action`으로 닫힌다. 던지거나 거절되면 열린 채 행동이 다시 켜지고 `onActionError`를 호출한다. 외부 `busy`도 반복 행동·닫기를 막는다. 1.13.0 변경 — 1.12.1은 Promise를 무시하고 바로 닫았다 |
126
+ | `onActionError`(Native) | `(error: unknown) => void` | — | action 실패를 본문 오류·재시도로 연결한다. 오류를 처리하는 동안 초안을 보존한다. 없으면 개발 빌드에서 실패마다 `console.error`를 남긴다(중복 제거 없음). 1.13.0 변경 |
116
127
  | `children`(Native) | `ReactNode` | — | 고정된 제목 줄 아래 스크롤 본문(`ScrollView`) 안에 들어간다. `FlatList`·`SectionList`를 넣지 않는다 |
117
128
  | `contentStyle`(Native) | 배치 key만(`HjmCompositionStyleProp`) | — | 대화상자 상자의 배치. 높이는 `size`로 |
118
129
  | `useDialog()`(Web) | `(request) => { closed: Promise<void>, close(): void }` | — | request는 `open`·`defaultOpen`·`onOpenChange`·`trigger`를 뺀 DialogProps. `OverlayStackProvider` 안에서만 |
@@ -54,7 +54,7 @@ import { Divider } from "@hjmds/react-native/data-display";
54
54
  | `orientation` | `horizontal` · `vertical` | `horizontal` | 가로선·세로선 |
55
55
  | `inset` | Web `none` · `start` · `both` / Native 숫자(pt, 토큰 값으로 넘긴다) | Web `none`(0) / Native 0 | Web `start`·`both`는 `spacing.md`. Native는 가로선이면 좌우, 세로선이면 위아래 여백이 된다 |
56
56
  | Web `decorative` | `boolean` | `false` | `true`면 `role="presentation"`·`aria-hidden` |
57
- | `layoutStyle` | 배치 전용 style | — | 루트 배치. Native `style`은 deprecated |
57
+ | `layoutStyle` | 배치 전용 style | — | 루트 배치. Native `style`은 제거된 속성 |
58
58
 
59
59
  콜백 prop은 없다.
60
60
 
@@ -71,7 +71,7 @@ import { Divider } from "@hjmds/react-native/data-display";
71
71
  ## 꼭 지킬 것
72
72
 
73
73
  - 색·두께를 `style`/`className`으로 바꾸지 않는다. 구분선 색은 제품 테마의 `border` key로 바뀐다.
74
- 배치는 `layoutStyle`로 한다. Native `style`은 deprecated — `layoutStyle` 또는 `orientation`·`inset`으로 옮긴다.
74
+ 배치는 `layoutStyle`로 한다. Native `style`은 제거된 속성 — `layoutStyle` 또는 `orientation`·`inset`으로 옮긴다.
75
75
  - Web에서 의미가 없는 장식선은 `decorative`로 표시한다(`role="presentation"`, `aria-hidden`).
76
76
  기본은 `role="separator"`다.
77
77
 
@@ -3,8 +3,8 @@
3
3
  - 단계: 컴포넌트
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
- - 적용: 미게시(1.12.1 이후)
7
- - 검토일: 2026-10-06
6
+ - 적용: 1.17.1
7
+ - 검토일: 2026-10-13
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md), Web·Native `src/screens.tsx`·`src/screen-flows.tsx`; 기존 개별 지침을 새 규격으로 통합. 예제 스토리는 2026-10-06 사용자 승인으로 스토리북 배포([승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정)). 스토리북 배포는 API 게시가 아니다(`적용` 참고)
9
9
  - 스토리북: `배포/화면/콘텐츠/작성과 수정`, `배포/화면/계정/프로필`
10
10
 
@@ -92,7 +92,7 @@ import { TextField } from "@hjmds/react-native/inputs";
92
92
  | prop | 값 | 기본값 | 설명 |
93
93
  | --- | --- | --- | --- |
94
94
  | `dirty` | `boolean` | 필수 | `true`에서 닫기를 누르면 `discard` 확인창을 띄우고, 확인하면 `cancel.onAction`을 부른다. `false`면 바로 부른다 |
95
- | `submit` | `ScreenFlowAction`(`{ label, onAction(), disabled?, pending? }`) | 필수 | `pending`이면 loading·비활성. 그동안 닫기 버튼도 비활성 |
95
+ | `submit` | `ScreenFlowAction`(`{ label, onAction(), disabled?, pending? }`) | 필수 | `pending`이면 loading·비활성. 그동안 닫기 버튼도 비활성. 본문 `state`가 `ready`가 아니면 저장 비활성 |
96
96
  | `cancel` | `ScreenFlowAction` | 필수 | `leading` 자리의 ghost 닫기 버튼 |
97
97
  | `discard` | AlertDialog confirm 요청에서 `onConfirm`을 뺀 것 + `fallbackErrorMessage: string` | 필수 | `onConfirm`은 EditorScreen이 채운다 |
98
98
  | `submitPlacement` | `"footer"` · `"header"` | `"footer"` | `header`면 저장이 상단 `actions` 자리로 가고 footer에는 `draftStatus`만 남는다 |
@@ -115,6 +115,8 @@ import { TextField } from "@hjmds/react-native/inputs";
115
115
  - 모든 문구(`label`, `discard`의 제목·설명·버튼)는 i18n 키로 넣는다. 컴포넌트는 기본 문구를 갖지 않는다.
116
116
  - `discard`는 `fallbackErrorMessage`까지 필수 타입이다. `onConfirm`은 EditorScreen이 채우므로 넘기지 않는다.
117
117
  - 확인 후 초안을 되돌리는 일은 `cancel.onAction` 안에서 제품이 한다.
118
+ - 저장 시작 또는 `cancel.disabled`/`cancel.pending` 전환은 이미 열린 초안 버리기 확인도 종료한다. 잠금이 풀렸다고 확인창이 다시 열리지는 않는다. 2026-10-13 이전 확인창으로 저장 중 이탈 잠금을 우회할 수 있던 경로를 수정했다.
119
+ - 최초 조회·오류·권한 제한 등 본문 대체 상태에서는 저장을 잠근다. 새 문서의 빈 초안은 `state="empty"`가 아니라 `ready`와 빈 입력으로 표현하고 제품 validation은 `submit.disabled`에 연결한다.
118
120
  - 라우터 뒤로가기·Android back·브라우저 이탈은 이 컴포넌트가 막지 않는다. 제품이 `dirty`로 guard를 건다.
119
121
  - 저장 성공 판정·오류 표시는 제품 상태(`notice`, Toast 등)로 한다.
120
122
 
@@ -62,7 +62,7 @@ import { EffectSurface } from "@hjmds/react-native/effect-surface";
62
62
 
63
63
  | prop | 값 | 기본값 | 설명 |
64
64
  | --- | --- | --- | --- |
65
- | `descriptor.layers` | `mesh` · `glow` · `grain` · `noise` · `ruled` | `["mesh"]` | 서로 다른 1~4개. ruled는 1.15.0 이후 미게시 실험 |
65
+ | `descriptor.layers` | `mesh` · `glow` · `grain` · `noise` · `ruled` | `["mesh"]` | 서로 다른 1~4개. ruled는 1.16.0 추가 실험 |
66
66
  | `descriptor.ruledSpacing` | 유한한 8~128 host units | `24` | ruled의 1unit 선 간격. 텍스트 baseline과 독립적이며 모션 밖 정적 레이어 |
67
67
  | `descriptor.intensity` | 0~1 | `0.22` | — |
68
68
  | `descriptor.period` | 2~120초 | `12` | — |
@@ -67,7 +67,7 @@ import { EmptyState } from "@hjmds/react-native/feedback";
67
67
  | `description` | Web `ReactNode` · Native `string` | — | — |
68
68
  | `action` | `ReactNode`(Button 하나) | — | 콜백은 Button의 `onClick`·`onPress`가 갖는다 |
69
69
  | Web `icon` · Native `illustration` | `ReactNode` | — | 장식, 접근성에서 숨김 |
70
- | `layoutStyle` | 배치 전용 style | — | 루트 배치. Native `style`·`illustrationStyle`·`titleStyle`·`descriptionStyle`·`actionStyle`은 deprecated |
70
+ | `layoutStyle` | 배치 전용 style | — | 루트 배치. Native `style`·`illustrationStyle`·`titleStyle`·`descriptionStyle`·`actionStyle`은 제거된 속성 |
71
71
 
72
72
  EmptyState 자체에는 콜백 prop이 없다. 2026-10-07 기존 예제의 생성 버튼에 동작이 없음을 확인해
73
73
  양 기본 스토리를 예시 생성 → 검색 0건 → 검색어 지우기 → 목록 복구로 연결했다. 이 데이터는
@@ -77,7 +77,7 @@ EmptyState 자체에는 콜백 prop이 없다. 2026-10-07 기존 예제의 생
77
77
 
78
78
  | 항목 | 값 | 근거 |
79
79
  | --- | --- | --- |
80
- | 크기 | 그림 슬롯은 `glyph.lg`(28) 정사각형이다(Native `illustration` 상자 고정 크기). 세로 여백: `compact` `spacing.xl`(24). `regular` `spacing.xxxl`(40, 두 플랫폼) | `component-recipes.ts` `emptyStateRecipe`, `styles.css` `.hjm-empty-state` |
80
+ | 크기 | Native 그림 슬롯은 최소 `glyph.lg`(28) 폭·높이를 확보하고 큰 일러스트의 자체 크기를 존중한다. Web 그림 슬롯에는 고정 크기가 없다. 세로 여백: `compact` `spacing.xl`(24). `regular` `spacing.xxxl`(40, 두 플랫폼) | `component-recipes.ts` `emptyStateRecipe`, `styles.css` `.hjm-empty-state` |
81
81
  | 간격 | 항목 사이 간격 `spacing.xs`(8), 좌우 여백 `spacing.xl`(24) | `component-recipes.ts` `emptyStateRecipe` |
82
82
  | 순서·정렬 | 위→아래 순서는 그림 → 제목 → 설명 → 행동이고 가운데 정렬이다. 행동은 Button 하나(`tone="secondary"` 또는 `primary`)를 내용 폭만큼 둔다 | `react-native/src/feedback.tsx` `EmptyState` |
83
83
  | 고정·스크롤 | 비어 있는 목록·검색 결과 영역 **안**에 둔다. 위 TopBar·검색칸·필터는 그대로 남기고 그 아래 내용 영역만 EmptyState로 바꾼다. Native `regular`는 남은 높이를 채우고(`flexGrow: 1`) 세로 가운데에 놓는다. `align="upper"`는 위:아래 빈 공간을 1:3으로 나눠 내용이 위쪽에 온다. 화면 하단 고정 행동이 필요하면 [BottomCTA](bottom-cta.md)로 따로 둔다 | `react-native/src/feedback.tsx` `EmptyState` |
@@ -107,16 +107,16 @@ EmptyState 자체에는 콜백 prop이 없다. 2026-10-07 기존 예제의 생
107
107
  - 제목·설명·행동 문구는 i18n 키로 넣는다. "검색 0건"과 "아직 만든 것 없음"은 다른 문구로 구분한다.
108
108
  - 다음 행동이 있으면 `action`에 Button 하나를 둔다. 아이콘·일러스트는 장식이라 접근성에서 숨겨진다.
109
109
  - Native는 `title`·`description`·`accessibilityLabel` 중 하나는 있어야 한다. 모두 없으면 `TypeError`를 던진다.
110
- - 일러스트·아이콘 자산은 제품 소유다. 색은 recipe가 정하므로 슬롯 색을 덮지 않는다.
110
+ - 일러스트·아이콘 자산과 자체 크기는 제품 소유다. 큰 그림은 `illustration` 노드 안에서 크기를 지정한다. EmptyState의 제목·설명 색은 recipe가 정하며 제거된 `illustrationStyle`로 프레임을 덮지 않는다.
111
111
  - 배치는 `layoutStyle`로 한다. Native 슬롯 style(`style`·`illustrationStyle`·`titleStyle`·`descriptionStyle`·`actionStyle`)은
112
- deprecated(개발 모드 1회 경고, 다음 major 제거)다 — 배치는 `layoutStyle`, 외형은 `density`·`align`으로 옮긴다.
112
+ 제거된 속성이다 — 배치는 `layoutStyle`, 외형은 `density`·`align`으로 옮긴다.
113
113
 
114
114
  ## 플랫폼 차이
115
115
 
116
116
  | 항목 | Web | Native |
117
117
  | --- | --- | --- |
118
- | 그림 슬롯 | `icon`(ReactNode) | `illustration`(ReactNode, 아이콘 크기 상자) |
118
+ | 그림 슬롯 | `icon`(ReactNode) | `illustration`(ReactNode, 최소 아이콘 크기 + 콘텐츠 자체 크기) |
119
119
  | `title` | 필수, ReactNode | 선택, string |
120
120
  | `description` | ReactNode | string |
121
121
  | 정렬·알림 | 없음 | `align`, `announcement`, `accessibilityLabel`, `titleRole` |
122
- | 배치 | `layoutStyle`(그 밖에 `className`과 div 속성) | `layoutStyle`(슬롯 style 다섯 개는 deprecated) |
122
+ | 배치 | `layoutStyle`(그 밖에 `className`과 div 속성) | `layoutStyle`(슬롯 style 다섯 개는 제거된 속성) |
@@ -30,6 +30,8 @@
30
30
  | `Field` | 기본 — 커스텀 컨트롤용 틀 | `@hjmds/react`, `/forms` | `@hjmds/react-native`, `/forms` |
31
31
  | `TextField` | 동반 — 한 줄 텍스트 입력 | `@hjmds/react`, `/forms` | `@hjmds/react-native`, `/inputs` |
32
32
  | `GestureSheetInput` | 확장 — GestureSheet 안의 키보드 추적 입력 | 없음 | `/sheet-gesture` |
33
+ | `DateEntry` | 확장 — 날짜 조각 직접 입력([구성 지침](../compositions/date-entry.md)) | `/date-entry` | `/date-entry` |
34
+ | `FieldGroup` | 확장 — 관련 입력의 그룹 안내·오류([구성 지침](../compositions/field-group.md)) | `/field-group` | `/field-group` |
33
35
 
34
36
  ## 최소 사용 예
35
37
 
@@ -130,7 +132,7 @@ TextField와 같은 테두리·글꼴·placeholder 색을 입힌다. 라벨·도
130
132
  않는다(TS2375). 위 예처럼 조건부 spread로 넘긴다. Web은 `ReactNode`라 `undefined`를 그대로 넘겨도 된다.
131
133
  - Web `Field`는 `controlId`가 필수이고, Native `Field`에는 `controlId`가 없다. 공용 코드에서 같은 props 객체를 넘기지 않는다.
132
134
 
133
- ### 날짜 조각 직접 입력 (실험·미게시)
135
+ ### 날짜 조각 직접 입력 (1.14.0 추가·실험)
134
136
 
135
137
  알고 있는 날짜를 년·월·일로 직접 편집하려면 `DateEntry`를 사용한다.
136
138
  Web `@hjmds/react/date-entry`, Native `@hjmds/react-native/date-entry`의 Field 확장이다.
@@ -143,11 +145,11 @@ Web `@hjmds/react/date-entry`, Native `@hjmds/react-native/date-entry`의 Field
143
145
 
144
146
  Web TextField의 `aria-invalid={true}`는 외부 오류 ID를 `aria-describedby`로 연결하는 경우에도
145
147
  오류 테두리를 표시한다. Native TextField/TextArea의 `invalid`는 인라인 error 문구 없이 오류
146
- 테두리를 표시하는 미게시 옵션이다. 같은 오류를 `accessibilityHint`로 연결하고 그룹 안내를
148
+ 테두리를 표시하는 옵션이다(1.14.0 추가). 같은 오류를 `accessibilityHint`로 연결하고 그룹 안내를
147
149
  별도로 보여 준다. error가 있으면 해당 문구가 hint보다 우선하고, hint가 없으면 description을 쓴다.
148
150
  2026-10-07 날짜 직접 입력의 큰 글자 오류가 세 번 반복된 관찰에 따라 이 경로를 추가했다.
149
151
 
150
- ### 관련 입력 그룹 (실험·미게시)
152
+ ### 관련 입력 그룹 (1.14.0 추가·실험)
151
153
 
152
154
  여러 입력이 한 질문에 답하면 `FieldGroup`을 사용한다. Form의 제출 경계와 구분하며
153
155
  `@hjmds/react/field-group`, `@hjmds/react-native/field-group`에서 제공한다.
@@ -70,7 +70,7 @@ import { FilePicker } from "@hjmds/react-native/file-picker";
70
70
  | Native `onPick` | `() => Promise<readonly FilePickerCandidate[] \| null>` | 필수 | 제품 adapter. 취소면 `null` |
71
71
  | Native `onPickError` | `(error: unknown) => void` | 필수 | `onPick`이 reject하면 이쪽으로만 온다 |
72
72
  | Web `getCandidateId` | `(file: File, index: number) => string` | `name:size:lastModified:index` | 후보 id 규칙 |
73
- | `disabled` | `true` · `false` | `false` | — |
73
+ | `disabled` | `true` · `false` | `false` | 선택 완료 시에도 잠금 상태면 결과를 반영하지 않는다 |
74
74
  | `hint`, `error` | 문구 | — | 선택 사항이다 |
75
75
 
76
76
  - `FilePickerCandidate`: `{ id, name, mimeType, sizeBytes }`(`mimeType`은 플랫폼이 못 정하면 `""`).
@@ -93,6 +93,9 @@ import { FilePicker } from "@hjmds/react-native/file-picker";
93
93
  만들고 색으로만 알리지 않는다.
94
94
  - `rejected`가 있어도 `accepted`는 그대로 쓴다. 한 장이 막혔다고 전체 선택을 버리지 않는다.
95
95
  - FilePicker는 선택 목록을 쌓지 않는다. 고른 파일 목록·업로드 상태는 제품 상태와 UploadItem이 소유한다.
96
+ - 취소(Web의 빈 선택, Native adapter의 `null`)는 기존 목록을 바꾸거나 `onSelect`를 호출하지 않는다. 같은 파일은 다시 선택할 수 있다.
97
+ - Native 결과는 완료 시점의 `descriptor`·`existingCount`로 판정하고 현재 콜백으로 전달한다. 대기 중 다른 파일이 추가·제거되면 `existingCount`를 계속 갱신한다.
98
+ - 파일 행 제거와 제거 후 초점 복귀는 목록을 소유한 제품이 처리한다. 제거 버튼을 FilePicker 내부에 가정하거나 선택 콜백을 목록 초기화 신호로 쓰지 않는다.
96
99
  - Native 실제 OS document/image picker 연결(`onPick`)은 제품 책임이다. 패키지는 이를 구현하거나
97
100
  기기에서 검증하지 않는다. 제품 릴리스 QA에서 확인한다.
98
101
 
@@ -106,7 +109,7 @@ import { FilePicker } from "@hjmds/react-native/file-picker";
106
109
  | 진행 중 잠금 | 없음 | `onPick` 대기 중 버튼 `busy`·비활성 |
107
110
  | `label`·`hint`·`error` 타입 | `ReactNode` | `string` |
108
111
  | 후보 id | `getCandidateId`(기본 `name:size:lastModified:index`) | adapter가 `id`를 채움 |
109
- | 배치 | `layoutStyle`(루트), `className`과 div 속성 | `layoutStyle`. `style`은 deprecated(개발 모드 1회 경고, 다음 major 제거) — `layoutStyle` 또는 recipe |
112
+ | 배치 | `layoutStyle`(루트), `className`과 div 속성 | `layoutStyle`. `style`은 제거된 속성 — `layoutStyle` 또는 recipe |
110
113
 
111
114
  ## 함정
112
115
 
@@ -117,4 +120,4 @@ import { FilePicker } from "@hjmds/react-native/file-picker";
117
120
 
118
121
  2026-10-07 소비 감사에서 Native 선택 버튼의 모서리 12가 프로필을 우회했다. Web 버튼의 기존 `radius.md`와 같은 역할을 Native도 가까운 프로필에서 읽으며, 프로필이 없으면 기존 12를 유지한다. 선택 어댑터·비활성/진행 상태·파일 검증은 바꾸지 않는다.
119
122
 
120
- 프로필 연결은 1.15.0 이후 미게시 변경이며, 기본 배포 계약과 실제 Native 기기 검증은 구분한다.
123
+ 프로필 연결은 1.16.0 변경이며, 기본 배포 계약과 실제 Native 기기 검증은 구분한다.
@@ -157,7 +157,7 @@ useEffect(() => { firstInvalidRef.current = nicknameError ? nicknameInputRef.cur
157
157
  | 실패 처리 | Promise reject를 삼킨다. 제품이 `formError`를 설정 | reject하면 `failed`로 바꾸고 Error 메시지 또는 `fallbackErrorMessage`를 alert로 표시 |
158
158
  | 첫 오류 필드로 이동 | 없음. 제품이 `onSubmit` 안에서 첫 무효 입력에 `.focus()`한다 | `firstInvalidFieldRef`: 버튼을 누른 순간 `current`를 읽는다. non-null이면 그 입력에 focus와 스크린 리더 focus를 옮기고 `onSubmit`·`onStatusChange` 없이 멈춘다(상태 그대로). ref는 `useEffect`에서 갱신하고, 순서 계산은 `resolveFirstInvalidFieldFocusTarget(fieldOrder, invalidIds)`를 쓴다 |
159
159
  | 접근성 이름 | `<form>` 속성(`aria-label` 등) | `label` 필수 |
160
- | 배치 | `className`, `layoutStyle`(form 속성 `style`도 받음) | `layoutStyle`. `style`은 deprecated(개발 모드 1회 경고, 다음 major 제거) — `layoutStyle` 또는 `density` |
160
+ | 배치 | `className`, `layoutStyle`(form 속성 `style`도 받음) | `layoutStyle`. `style`은 제거된 속성 — `layoutStyle` 또는 `density` |
161
161
  | 밖에서 제출 | `<form>`이므로 `type="submit"` 버튼·Enter로 된다 | `ref: Ref<FormHandle>`의 `submit()`으로 같은 validation·중복 제출 방지를 실행 |
162
162
 
163
163
  ## 함정
@@ -83,14 +83,14 @@ import { Heading } from "@hjmds/react-native/heading";
83
83
  | --- | --- | --- |
84
84
  | 출력 | `h1`~`h6` 요소 | `Text` + `accessibilityRole="header"` + `aria-level` |
85
85
  | 추가 prop | `HTMLAttributes`(`id`, `className`, `style` 등), ref | 없음 |
86
- | 배치 | `layoutStyle`. `style`도 합쳐진다(크기 변수는 덮이지 않음) | `layoutStyle`. `style`(TextStyle)은 deprecated(개발 모드 1회 경고, 다음 major 제거) — `layoutStyle` 또는 `level` |
86
+ | 배치 | `layoutStyle`. `style`도 합쳐진다(크기 변수는 덮이지 않음) | `layoutStyle`. `style`(TextStyle)은 제거된 속성 — `layoutStyle` 또는 `level` |
87
87
  | 글자 배율 | `--hjm-text-scale`로 확대 | Native `Text`의 글자 배율 |
88
88
 
89
89
  ## 함정
90
90
 
91
91
  - Web `style`은 이제 버려지지 않고 합쳐진다. 다만 크기·줄 높이·굵기 변수(`--hjm-heading-*`)는 recipe가 마지막에 덮으므로
92
92
  `style`로 크기를 바꿀 수 없다. 배치는 `layoutStyle`로 준다.
93
- - Native `style`(deprecated)은 아직 마지막에 합쳐져 색·크기까지 바뀐다. 쓰지 말고 `layoutStyle`·`level`로 옮긴다.
93
+ - Native `style`은 제거되었다. 배치는 `layoutStyle`, 제목 위계는 `level`로 지정한다.
94
94
 
95
95
  2026-10-07 추가: 제목은 프로필의 optional `tokens.fontFamily.display`를 사용하고 생략 시 현재 ui를 따른다.
96
- 문서 semanticLevel·크기·굵기는 유지한다. [서체 역할](../tokens/font-roles.md), 1.15.0 이후 미게시.
96
+ 문서 semanticLevel·크기·굵기는 유지한다. [서체 역할](../tokens/font-roles.md), 1.16.0 변경.
@@ -80,7 +80,7 @@ import { NotificationBell } from "@hjmds/react-native/notification-bell";
80
80
  | `onPress`(NotificationBell) | `() => void` | 필수 | Web·Native 모두 `onPress`다(Web도 `onClick`이 아니다) |
81
81
  | `active`(NotificationBell) | `true` · `false` | `true` | — |
82
82
  | `disabled`(NotificationBell) | `true` · `false` | `false` | — |
83
- | Web `layoutStyle`(NotificationBell) | `HjmCompositionStyleProp` | — | 루트 배치. 기본 `alignSelf: "flex-start"`보다 우선하지만 `width`는 무시된다(루트는 항상 `max-content`). 떠 있는 배지가 루트의 끝 모서리에 붙어 있어 루트가 넓어지면 아이콘에서 떨어지기 때문이다. margin·`alignSelf`로 배치한다. 미게시(1.12.1 이후) |
83
+ | Web `layoutStyle`(NotificationBell) | `HjmCompositionStyleProp` | — | 루트 배치. 기본 `alignSelf: "flex-start"`보다 우선하지만 `width`는 무시된다(루트는 항상 `max-content`). 떠 있는 배지가 루트의 끝 모서리에 붙어 있어 루트가 넓어지면 아이콘에서 떨어지기 때문이다. margin·`alignSelf`로 배치한다. 1.13.0 변경 |
84
84
 
85
85
  ## 배치
86
86
 
@@ -117,10 +117,13 @@ TopBar
117
117
  | 스피너 교체 | 없음 | `renderLoadingIndicator` |
118
118
  | 비활성이지만 포커스 유지 | `aria-disabled` | `loading` 기본 동작 |
119
119
  | NotificationBell 흔들림 정지 | 문서 숨김·reduced motion | AppState 비활성·reduced motion |
120
- | 배치 | `layoutStyle`(IconButton·NotificationBell, NotificationBell은 `width` 무시) | IconButton `layoutStyle`. `style`은 deprecated(개발 모드 1회 경고, 다음 major 제거) — `layoutStyle` 또는 `tone`·`size`·`shape`. NotificationBell은 배치 prop이 없어 감싸는 View로 배치한다 |
120
+ | 배치 | `layoutStyle`(IconButton·NotificationBell, NotificationBell은 `width` 무시) | IconButton `layoutStyle`. `style`은 제거된 속성 — `layoutStyle` 또는 `tone`·`size`·`shape`. NotificationBell은 배치 prop이 없어 감싸는 View로 배치한다 |
121
121
 
122
122
  ## 함정
123
123
 
124
124
  - children의 색은 버튼이 칠하지 않는다. HJM Icon은 자기 `tone` 색을 쓰므로, `primary`·`danger` 버튼 안에서는
125
125
  `tone="inverse"`를 준다. 다른 아이콘 라이브러리의 컴포넌트를 직접 넣으면(Native) 색을 제품이 넘겨야 한다.
126
- - Native `IconButton` `style`은 deprecated지만 다음 major 전까지 recipe 뒤에 합쳐져 지름·배경까지 바뀐다. 새 코드에서 쓰지 않는다.
126
+ - Native `IconButton`의 `style`은 제거되었다. 배치는 `layoutStyle`, 외형은 `tone`·`size`·`shape`로 지정한다.
127
+
128
+ 2026-10-10 사용자 요청: Button과 같은 선택 표현을 쓴다. 선택은 bg 바탕·contentBrand 아이콘으로
129
+ 표시하고 추가 선택 테두리는 없다. aria-pressed/Native selected와 키보드 focus outline은 유지한다.
@@ -96,7 +96,7 @@ const renderGlyph = createLucideGlyph({ search: Search, back: ArrowLeft });
96
96
  | glyph | 내장 SVG 경로, `renderGlyph`는 선택 | `renderGlyph` 필수(내장 그림 없음) |
97
97
  | 사용자 정의 이름 | `SemanticIconName`만 | `Icon<Name>` 제네릭으로 제품 이름 허용 |
98
98
  | 출력 | `<svg>`(장식이면 `aria-hidden`) | `View` 프레임(장식이면 `accessible={false}`) |
99
- | 배치 | `layoutStyle` | `layoutStyle`. `style`은 deprecated(개발 모드 1회 경고, 다음 major 제거) — `layoutStyle` 또는 descriptor `size`·`tone` |
99
+ | 배치 | `layoutStyle` | `layoutStyle`. `style`은 제거된 속성 — `layoutStyle` 또는 descriptor `size`·`tone` |
100
100
 
101
101
  ## 함정
102
102
 
@@ -84,10 +84,11 @@ import { ImageViewer } from "@hjmds/react-native/image-viewer";
84
84
  | ImageViewer `onClose` · `onIndexChange` | `() => void` · `(index: number) => void` | `onClose` 필수 | — |
85
85
  | ImageViewer `safeAreaInsets` | `{ top: number; bottom: number; left?: number; right?: number }` | 필수, 좌우 0 | 버튼·caption·상태 안내를 물리적 좌우 안전 영역 안에 배치. 사진은 전체 갤러리 폭 사용 |
86
86
  | ImageViewer `supportedOrientations` | RN Modal의 orientation 배열 | RN 기본값 | 제품 manifest·기기 회전 잠금 범위 안에서 허용. Modal은 fullScreen |
87
- | ImageViewer `renderImage` | `(props: ImageViewerImageRenderProps) => ReactNode` | RN Image | Native 전용, 미게시. `item`, 측정된 `width`·`height`, `onReady`·`onError`를 전달. 제품 이미지 host의 캐시·표시 이벤트를 연결 |
87
+ | ImageViewer `renderImage` | `(props: ImageViewerImageRenderProps) => ReactNode` | RN Image | Native 전용, 1.14.0 추가. `item`, 측정된 `width`·`height`, `onReady`·`onError`를 전달. 제품 이미지 host의 캐시·표시 이벤트를 연결 |
88
88
  | ImageViewer `onImageStatusChange` | `({ item, status }) => void` | 없음 | `loading`·`ready`·`error`. 마운트된 각 이미지 기준이며 비선택 페이지도 포함할 수 있음 |
89
89
 
90
90
  - 실패하면 중립 배경 위에 오류 기호를 그리고, 정보 이미지의 이름은 그대로 유지한다. `fallback`은 시각만 바꾼다.
91
+ - 같은 주소라도 Web `imageProps.srcSet`/요청 정책, Native `sourceAdapter`의 실제 source·헤더·cache 내용이 바뀌면 새 요청으로 보고 loading부터 시작한다. inline adapter 함수만 다시 만든 것은 재시도가 아니다. 같은 요청을 명시적으로 재시도하거나 adapter 구현만 교체하려면 `<Image key={attempt} ... />`처럼 요청 시도 키로 Image 전체를 다시 마운트한다. 실패한 요청의 늦은 콜백은 새 요청을 덮지 않는다.
91
92
 
92
93
  ## 배치
93
94
 
@@ -107,7 +108,7 @@ Native 이미지 프레임은 `imageRecipe.radius` 역할을 Provider의 `tokens
107
108
  - GridReveal의 `ready`는 `onLoadStatusChange`가 `loaded`일 때만 true, 오류·소스 변경·재시도 전에는 false로 돌린다.
108
109
  Native에서 화면이 가려진 채 mounted면 `active={false}`.
109
110
  - ImageViewer는 `open`으로 제어하고 닫히면 상태를 버린다. 라벨·`id`가 비거나 중복이면 `TypeError`.
110
- - ImageViewer는 불러오는 중(`loadingLabel`)과 실패(`errorLabel`)를 모두 알린다. 실패는 Android assertive live region, iOS는 `announceForAccessibility`다(미게시(1.12.1 이후). 1.12.1은 실패를 알리지 않았다).
111
+ - ImageViewer는 불러오는 중(`loadingLabel`)과 실패(`errorLabel`)를 모두 알린다. 실패는 Android assertive live region, iOS는 `announceForAccessibility`다(1.13.0 변경. 1.12.1은 실패를 알리지 않았다).
111
112
  - 원격 이미지 권한·URL 수명·캐시는 제품 소유다. HJM은 메타데이터를 가져오지 않는다.
112
113
 
113
114
  ### Native ImageViewer의 제품 이미지 호스트
@@ -140,7 +141,7 @@ import { ImageViewer } from "@hjmds/react-native/image-viewer";
140
141
  회전을 허용하는 제품은 `supportedOrientations={["portrait", "landscape"]}`와 갱신되는
141
142
  safeAreaInsets 네 방향을 제공한다. 앱 manifest가 portrait 고정이면 이 prop만으로 회전이 보장되지 않는다.
142
143
  화면 크기가 바뀌면 버튼을 제외한 남은 갤러리 영역을 다시 측정해 host에 전달한다.
143
- 아래 inspection 확장은 미게시이며, Expo 표시 확인·결과 승인 회귀와 Utilverse 채택은 별도 검증한다.
144
+ 아래 inspection 확장은 1.14.0 추가 API이며, Expo 표시 확인·결과 승인 회귀와 Utilverse 채택은 별도 검증한다.
144
145
 
145
146
  ### 결과 검사 크기 계산
146
147
 
@@ -157,7 +158,7 @@ const geometry = resolveImageInspectionGeometry(
157
158
  ); // width/height 804, panBounds x=201 y=175 (중앙에서 양방향)
158
159
  ```
159
160
 
160
- ### Native 결과 검사 보기 (미게시)
161
+ ### Native 결과 검사 보기 (1.14.0 추가)
161
162
 
162
163
  `ImageViewer`의 선택적 `inspection`은 위 계산을 사용한다. 모든 `items`에 실제 출력의
163
164
  양수 `width`·`height`를 제공한다. 일반 Gallery의 pinch/paging과 별개로 고정 배율의 결과를
@@ -65,7 +65,7 @@ const keyboardOpen = useKeyboardState((state) => state.isVisible);
65
65
  | `enabled` | `true` · `false` | `true` | `false`면 따라 움직이지 않는다 |
66
66
  | `clearance` | 0 이상 숫자(layout point) | `0` | 키보드 위 추가 간격. 음수나 유한하지 않은 값은 `TypeError` |
67
67
  | `children` | ReactNode(필수) | — | 하단 행동. 안쪽 여백·안전 영역은 호스트가 준다 |
68
- | `style` | `StyleProp<ViewStyle>` | — | 감싸는 sticky view의 host 스타일. 플랫폼 host라 1.13 deprecated 대상이 아니다([이관 문서](../../migration-native-legacy-removal.md#113-deprecated-시각-style-제거는-다음-major)). 배치 값만 넣는다 |
68
+ | `style` | `StyleProp<ViewStyle>` | — | 감싸는 sticky view의 host 스타일. 플랫폼 host라 1.13 deprecated 대상이 아니다([이관 문서](../../migration-native-legacy-removal.md#2026-10-13-잔여-시각-style-제거)). 배치 값만 넣는다 |
69
69
 
70
70
  - 이벤트·콜백 prop은 없다. 키보드 열림 여부가 필요하면 peer의 `useKeyboardState((state) => state.isVisible)`로 읽는다.
71
71
  - 내부에서 `KeyboardStickyView`에 `offset={{ closed: 0, opened: -clearance }}`를 넘긴다.
@@ -113,5 +113,5 @@ Web (persistent sidebar) Native
113
113
 
114
114
  ## 함정
115
115
 
116
- - [계약 문서](../../layout.md)는 Native `skipLinkLabel`이 deprecated로 남아 있다고 적지만 1.12.1 Native `LayoutProps`에는 없다.
116
+ - `skipLinkLabel`은 Web 전용이다. Native `LayoutProps`에는 없으므로 플랫폼 공통 props에 넣지 않는다.
117
117
  - Native persistent 사이드바는 옆으로 놓이지 않고 세로로 쌓인다. 넓은 화면 2단 배치가 필요하면 제품이 main 안에서 구성한다.
@@ -79,7 +79,7 @@ const renderGlyph = createLucideGlyph({ chevronEnd: ChevronRight }); // 제품
79
79
  | `renderIcon`(Native) | `(props: { name, size, color, strokeWidth }) => ReactNode` | — | semantic name → glyph 경계. 보통 `createLucideGlyph`. 크기·색은 HJM이 넘긴 값을 그대로 쓴다 |
80
80
  | `renderAnchor`(Web) | `(props: LinkRenderProps) => ReactElement` | — | 받은 props(`href`·`ref`·`className`·`data-*`·`aria-disabled`·`onClick`·`children`·병합된 `style`)를 framework Link에 그대로 넘긴다 |
81
81
  | `layoutStyle` | 배치 key만 | — | 양쪽 있음. Web은 `renderAnchor`에도 병합된 `style`로 전달된다 |
82
- | `style`(Native) | — | — | **deprecated**(1.13, 개발 모드 1회 경고, 다음 major 제거) — `layoutStyle`을 쓴다 |
82
+ | `style`(Native) | — | — | 제거된 속성 — `layoutStyle`을 쓴다 |
83
83
 
84
84
  - Native는 tone·variant가 없다. 항상 brand 색 · 밑줄 · `bodyLarge` 글자 · 최소 터치 target이다.
85
85
 
@@ -118,6 +118,6 @@ const renderGlyph = createLucideGlyph({ chevronEnd: ChevronRight }); // 제품
118
118
  - Native에서 descriptor 아이콘을 주고 `renderIcon`을 빼면 개발 모드에서 한 번 경고하고 아이콘을 그리지 않는다(throw하지 않음).
119
119
  descriptor 아이콘과 `leading`/`trailing` node를 함께 주면 descriptor가 그 자리를 갖고 경고가 난다. 1.12까지는 Native가
120
120
  descriptor 아이콘을 그리지 않았다.
121
- - Native `style`은 deprecated다. 아직 recipe 뒤에 합쳐져 시각 값이 통과하므로 배치는 `layoutStyle`로만 준다.
121
+ - Native `style`은 제거되었다. 배치는 `layoutStyle`로 지정하고 brand 색·밑줄·글자 크기는 `linkRecipe`를 따른다. Native에는 `tone`·`size` prop이 없다.
122
122
  - 현재 Native 스토리(`showcase/native/src/component-examples.tsx` LinkExample)는 descriptor 아이콘·`renderIcon` 예가 없고
123
123
  문구가 i18n 키가 아니다. 아이콘 경로는 위 최소 사용 예로 확인한다.
@@ -3,8 +3,8 @@
3
3
  - 단계: 컴포넌트
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
- - 적용: 미게시(1.12.1 이후)
7
- - 검토일: 2026-10-06
6
+ - 적용: 1.17.1
7
+ - 검토일: 2026-10-13
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md), Web·Native `src/screens.tsx`·`src/screen-flows.tsx`; 기존 개별 지침을 새 규격으로 통합. 예제 스토리는 2026-10-06 사용자 승인으로 스토리북 배포([승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정)). 스토리북 배포는 API 게시가 아니다(`적용` 참고)
9
9
  - 스토리북: `배포/화면/콘텐츠/목록과 상세`
10
10
 
@@ -64,11 +64,11 @@ import { ListDetailScreen } from "@hjmds/react-native/screen-flows";
64
64
  | prop | 값 | 기본값 | 설명 |
65
65
  | --- | --- | --- | --- |
66
66
  | `list` | `ReactNode` | 필수 | 목록 pane 본문. 상세가 열려도 mount를 유지한다 |
67
- | `detail` | `{ title: string; content: ReactNode }` | 없음 | 있으면 상세 pane을 보이고 목록 pane을 숨긴다. 닫힌 상태는 prop을 빼서 표현한다(`undefined`를 넘기지 않는다) |
67
+ | `detail` | `{ title; content; state?; stateAction?; notice? }` | 없음 | 있으면 상세 pane을 보이고 목록 pane을 숨긴다. 닫힌 상태는 prop을 빼서 표현한다(`undefined`를 넘기지 않는다) |
68
68
  | `back` | `ScreenFlowAction`(`{ label, onAction(), disabled?, pending? }`) | 필수 | 상세 pane `leading` 자리의 ghost 버튼 |
69
69
  | `refresh` | `ScreenFlowAction` | 없음 | 목록 pane `actions` 자리의 ghost 버튼. 주면 `actions`를 덮는다 |
70
70
  | `loadMore` | `ScreenFlowAction` | 없음 | 목록 pane footer의 ghost 버튼 |
71
- | `layoutStyle` | `HjmCompositionStyleProp` | 없음 | Web·Native 모두 목록·상세 pane을 함께 담는 **바깥 틀**에 적용한다(안쪽 목록 ScreenLayout이 아니다). 목록/상세 전환과 상관없이 같은 루트에 남는다. 미게시(1.12.1 이후) |
71
+ | `layoutStyle` | `HjmCompositionStyleProp` | 없음 | Web·Native 모두 목록·상세 pane을 함께 담는 **바깥 틀**에 적용한다(안쪽 목록 ScreenLayout이 아니다). 목록/상세 전환과 상관없이 같은 루트에 남는다(1.13.0) |
72
72
  | 나머지 | `ScreenLayout`과 같음(`children`·`footer` 제외) | — | 목록 pane에만 적용된다(`title` 필수, `state`, `scroll` 등) |
73
73
 
74
74
  ## 배치
@@ -84,7 +84,7 @@ import { ListDetailScreen } from "@hjmds/react-native/screen-flows";
84
84
  ## 꼭 지킬 것
85
85
 
86
86
  - 라벨은 모두 i18n 키로 넣는다.
87
- - `state`(로딩·빈·오류·제한)는 목록 pane에만 적용된다. 상세의 로딩·오류는 `detail.content` 안에서 표시한다.
87
+ - `state`(로딩·빈·오류·제한)는 목록 pane에만 적용된다. 상세 최초 조회·오류는 `detail.state`, 재시도는 `detail.stateAction`, 기존 상세를 유지하는 갱신 실패는 `detail.notice`로 전달한다. 미게시(1.18.0 이후)에 추가된 선택 prop이며 이전 설치 버전에서는 상세 content 안에 상태를 구성한다.
88
88
  - Web은 상세 진입 시 뒤로 버튼에 포커스를 옮기고 복귀 시 목록의 원래 요소로 돌린다. 요소가 삭제되었으면 목록 본문으로 이동한다.
89
89
  처음부터 `detail`이 있는 채로 mount되면(딥링크) 사용자 전환이 아니므로 포커스를 옮기지 않는다. 그때 포커스는 제품 router가 정한다.
90
90
  - 상세 열림 상태는 제품이 가진다. OS 뒤로가기·브라우저 뒤로가기와 `back`을 제품 router에서 함께 연결한다.
@@ -99,5 +99,5 @@ import { ListDetailScreen } from "@hjmds/react-native/screen-flows";
99
99
  ## 함정
100
100
 
101
101
  - `refresh`를 주면 ScreenLayout `actions`는 무시된다. 둘 다 필요하면 `refresh` 대신 `actions`에 직접 조합한다.
102
- - 상세 pane은 `title`과 `back`(leading)만 받는다. 상세 쪽 `description`·`actions`·footer 자리는 없다.
102
+ - 상세 pane은 `title`·`state`·`stateAction`·`notice`와 `back`(leading)을 받는다. 상세 쪽 `description`·`actions`·footer 자리는 없다.
103
103
  - `loadMore`는 상태 없는 버튼이다. 끝·오류·중복 요청 방지가 필요하면 LoadMore를 쓴다.
@@ -54,6 +54,8 @@ import { Avatar, ListRow } from "@hjmds/react-native/data-display";
54
54
  <ListRow title={member.name} description={member.role}
55
55
  leading={<Avatar name={member.name} decorative />} leadingShape="circle"
56
56
  trailingText={t("member.joined", { date })} onPress={() => openMember(member.id)} />
57
+
58
+ <ListRow title={t("account.delete")} tone="danger" onPress={openDeleteConfirmation} />
57
59
  ```
58
60
 
59
61
  ## 축과 기본값
@@ -62,15 +64,16 @@ import { Avatar, ListRow } from "@hjmds/react-native/data-display";
62
64
  | --- | --- | --- | --- |
63
65
  | `density` | `compact` · `comfortable` · `relaxed` · `spacious` | `comfortable` | 최소 높이는 설명 유무(한 줄·두 줄)와 함께 정해진다 |
64
66
  | `leadingShape` | `square` · `circle` | `square` | leading 프레임 크기는 recipe가 그린다 |
67
+ | `tone` | `default` · `danger` | `default` | 탈퇴 같은 파괴적 목적지 제목은 `danger`로 강조한다. 선택·잠금·밀도와 독립적이다 |
65
68
  | `selected` | `true` · `false` | `false` | — |
66
69
  | `disabled` | `true` · `false` | `false` | — |
67
70
  | `href`(Web) | 문자열 | — | 있으면 `<a>` 행 |
68
71
  | `onClick`(Web) | `(event: MouseEvent<HTMLElement>) => void` | — | 있으면 `<button>` 행 |
69
72
  | `onPress`(Native) | `(event: GestureResponderEvent) => void` | — | 있으면 누를 수 있는 행 |
70
- | `loading`·`loadingLabel`(Web) | `boolean` · 현지화 문자열 | `false` · — | 같은 슬롯 모양의 자리표시 행. `loadingLabel`이 상태로 읽힌다 |
73
+ | `loading`·`loadingLabel` | `boolean` · 현지화 문자열 | `false` · — | 같은 슬롯 모양의 자리표시 행. `loadingLabel`이 상태로 읽힌다 |
71
74
  | `trailingAction`(Native) | ReactNode | — | 행 명령 옆에 따로 그리는 별도 target(IconButton 등). 자기 `onPress`를 가진다 |
72
75
  | `layoutStyle` | 배치 key만 | — | 행 루트 배치. Native는 슬롯 배치용 `leadingStyle`·`contentStyle`·`titleRowStyle`·`trailingStyle`·`trailingActionStyle`(모두 배치 key만)도 받는다 |
73
- | `titleStyle`·`descriptionStyle`(Native) | — | — | **deprecated**(1.13, 개발 모드 1회 경고, 다음 major 제거) — `density`·`selected`, 글자는 listRowRecipe가 정한다 |
76
+ | `titleStyle`·`descriptionStyle`(Native) | — | — | 제거된 속성 — 배치는 `layoutStyle`, 제목의 위험색은 `tone="danger"`, 글자는 listRowRecipe가 정한다 |
74
77
 
75
78
  - 상호작용: Web은 `href`면 `<a>`(selected는 `aria-current="page"`), `onClick`이면 `<button>`(selected는 `aria-pressed`),
76
79
  둘 다 없으면 `<div>`. Native는 `onPress`가 있으면 누를 수 있는 행이 된다.
@@ -116,7 +119,7 @@ Native 원형 leading의 `full`은 Provider token에서 읽지만 고정 원형
116
119
  | 항목 | Web | Native |
117
120
  | --- | --- | --- |
118
121
  | 제목·설명 타입 | `ReactNode` | `string` |
119
- | 자리표시 행 | `loading` + `loadingLabel` | — |
122
+ | 자리표시 행 | `loading` + `loadingLabel` | `loading` + `loadingLabel` |
120
123
  | 링크 행 | `href` | 없음(`onPress`에서 router 호출) |
121
124
  | 제목 옆 메타·별도 뒤 행동·뒤 문구 | — | `titleMetadata`, `trailingAction`, `trailingText` |
122
125
  | 접근성 이름 조합 | 요소 내용 | `accessibilityLabel` 없으면 제목·`metadataLabel`·설명·`trailingLabel`을 이어 붙임 |
@@ -125,4 +128,10 @@ Native 원형 leading의 `full`은 Provider token에서 읽지만 고정 원형
125
128
  ## 함정
126
129
 
127
130
  - Web 로딩 행은 슬롯의 **존재**로 모양을 정하고 문구는 무시한다. 실제 행과 같은 슬롯을 넘겨야 높이가 맞는다.
128
- - Native `titleStyle`·`descriptionStyle`은 deprecated지만 아직 TextStyle 전체를 받아 색·굵기도 바뀐다. 새 코드에서 쓰지 않는다.
131
+ - Native `titleStyle`·`descriptionStyle`은 제거되었다. 파괴적 목적지의 제목 강조에는 양 플랫폼 공통 `tone="danger"`를 사용한다. `tone`은 확인 절차나 삭제 실행을 대신하지 않는다.
132
+
133
+
134
+ 로딩 행은 실제 ListRow의 density·한 줄/두 줄 최소 높이와 padding을 그대로 사용한다. 두 줄 결과를
135
+ 예상하면 `description`을 제공한다(자리표시 중 문구 자체는 표시하지 않는다). loading 동안 행과
136
+ trailingAction은 실행할 수 없으며 실제 슬롯 내용도 마운트하지 않는다. 여러 로딩 행 중 첫 행에만
137
+ `loadingLabel`을 주고 나머지는 접근성 트리에서 숨긴다. 검색 화면도 임의 Skeleton 조립 대신 이 계약을 쓴다.
@@ -19,7 +19,7 @@
19
19
  | 화면 형태 | 쓸 것 |
20
20
  | --- | --- |
21
21
  | 수십 개 이하의 행을 한 번에 그림(설정, 계정 메뉴, 검색 결과 한 페이지) | `List` + `ListRow` |
22
- | 유한 카드 여러 장의 독립 입력/행동을 함께 보여주는 가로 탐색 | `CollectionRail` (미게시 실험, 아래 가로 배치 지침) |
22
+ | 유한 카드 여러 장의 독립 입력/행동을 함께 보여주는 가로 탐색 | `CollectionRail` (1.16.0 추가·실험, 아래 가로 배치 지침) |
23
23
  | 행 한 줄의 제목·설명·앞뒤 슬롯 | [ListRow](list-row.md) |
24
24
  | 높이가 고정된 행 수백~수천 개를 창 안에서 스크롤 | [VirtualList](virtual-list.md) |
25
25
  | 이어지는 목록의 다음 페이지 요청 footer | [LoadMore](load-more.md) (List·VirtualList 아래에 둔다) |
@@ -42,6 +42,7 @@
42
42
  | --- | --- | --- | --- |
43
43
  | `List` | 기본 | `@hjmds/react`, `/display` | `@hjmds/react-native`, `/data-display` |
44
44
  | `TaskList` | 확장 — 완료 체크 목록(optional) | `/task-list` | `/task-list` |
45
+ | `CollectionRail` | 동반·대안 — 독립 행동을 가진 유한 가로 목록([구성 지침](../compositions/collection-detail.md)) | `/collection-rail` | `/collection-rail` |
45
46
 
46
47
  `TaskList`는 granular subpath로만 import 된다. 추가 peer는 없다. 순서 바꾸기를 합성하면 SortableCollection의 peer가 필요하다.
47
48
 
@@ -74,7 +75,7 @@ import { Text } from "@hjmds/react-native/primitives";
74
75
  | `label` | 현지화 문자열(필수) | — | 목록의 접근성 이름. 비면 `TypeError` |
75
76
  | `separator` | `indented` · `full` · `none` | `indented` | `indented`는 leading 슬롯 폭만큼 들여씀 |
76
77
  | `appearance` | `plain` · `grouped` | `plain` | `grouped`는 배경과 둥근 모서리로 한 덩어리 |
77
- | `layoutStyle` | 배치 key만 | — | 목록 루트 배치. Native `style`은 **deprecated**(1.13, 개발 모드 1회 경고, 다음 major 제거) — `layoutStyle`, 모양은 `appearance`·`separator` |
78
+ | `layoutStyle` | 배치 key만 | — | 목록 루트 배치. Native `style`은 제거된 속성 — `layoutStyle`, 모양은 `appearance`·`separator` |
78
79
  | TaskList `items` | `readonly TaskItem[]` — `{ id, label, completed, description?, disabled? }` | — | `id`·`label`이 비거나 `id`가 중복되거나 `completed`가 boolean이 아니면 `TypeError` |
79
80
  | TaskList `onCompletedChange` | `(id: string, completed: boolean) => void` | — | 필수. 체크 하나마다 한 번. 목록 순서·저장은 제품이 정한다 |
80
81
  | TaskList `disabled` | `true` · `false` | `false` | 전체 체크박스를 끈다 |
@@ -110,7 +111,7 @@ Native `grouped` 프레임은 Provider의 `tokens.radius.lg`를 읽는다. plain
110
111
 
111
112
  ### CollectionRail 가로 배치
112
113
 
113
- 새 `CollectionRail`은 미게시(1.15.0 이후) 실험 companion이다. 기존 List의 배포/호환 계약은 유지한다.
114
+ 새 `CollectionRail`은 1.16.0 추가 실험 companion이다. 기존 List의 배포/호환 계약은 유지한다.
114
115
 
115
116
  #### 언제 쓰나
116
117
 
@@ -196,7 +197,7 @@ import { CollectionRail } from "@hjmds/react/collection-rail";
196
197
  | --- | --- | --- |
197
198
  | 구조 | `role="list"` + 자식마다 `role="listitem"` | `accessibilityRole="list"` |
198
199
  | 구분선 | CSS(`data-separator`) | 행 사이 1px `View` |
199
- | 배치 | `layoutStyle`(+ `className`) | `layoutStyle`(`style`은 deprecated) |
200
+ | 배치 | `layoutStyle`(+ `className`) | `layoutStyle`(`style`은 제거된 속성) |
200
201
 
201
202
  2026-10-07 Utilverse 항목 삭제 채택을 위해 `renderItemAction`을 추가했다. 체크와 삭제의 초점·누름을 분리하고 큰 글자 라벨 폭을 보존하도록 행동을 다음 줄에 둔다. `renderCollection`의 `renderItem`에도 포함된다. 삭제 저장·실패·되돌리기는 제품이 처리한다.
202
203