@hjmds/design-contracts 1.18.0 → 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.
- package/dist/action-session.d.ts +16 -0
- package/dist/action-session.d.ts.map +1 -1
- package/dist/action-session.js +18 -0
- package/dist/action-session.js.map +1 -1
- package/dist/behaviors.d.ts +7 -6
- package/dist/behaviors.d.ts.map +1 -1
- package/dist/behaviors.js +4 -3
- package/dist/behaviors.js.map +1 -1
- package/dist/bottom-navigation.d.ts +4 -0
- package/dist/bottom-navigation.d.ts.map +1 -1
- package/dist/bottom-navigation.js +11 -0
- package/dist/bottom-navigation.js.map +1 -1
- package/dist/carousel.d.ts +3 -0
- package/dist/carousel.d.ts.map +1 -1
- package/dist/carousel.js.map +1 -1
- package/dist/catalog.d.ts +42 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/component-recipes.d.ts +42 -0
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +14 -1
- package/dist/component-recipes.js.map +1 -1
- package/dist/field-group.d.ts +14 -0
- package/dist/field-group.d.ts.map +1 -1
- package/dist/field-group.js +9 -0
- package/dist/field-group.js.map +1 -1
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +10 -3
- package/dist/form.js.map +1 -1
- package/dist/screen-patterns.d.ts +4 -0
- package/dist/screen-patterns.d.ts.map +1 -1
- package/dist/screen-patterns.js +12 -3
- package/dist/screen-patterns.js.map +1 -1
- package/dist/toast.d.ts +10 -1
- package/dist/toast.d.ts.map +1 -1
- package/dist/toast.js +43 -2
- package/dist/toast.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/action-session.md +2 -0
- package/docs/asset.md +7 -0
- package/docs/bottom-navigation.md +11 -0
- package/docs/brand-boundary.md +3 -3
- package/docs/collection-rail.md +4 -0
- package/docs/compound-controls.md +8 -3
- package/docs/consumer-policy.md +39 -120
- package/docs/generated/component-maturity.md +1 -1
- package/docs/generated/renderer-evidence.json +3 -3
- package/docs/generated/renderer-evidence.md +1 -1
- package/docs/generated/showcase-manifest.json +1 -1
- package/docs/gravity-letters.md +2 -0
- package/docs/grid-reveal.md +2 -0
- package/docs/layout.md +2 -2
- package/docs/load-more.md +5 -0
- package/docs/migration-native-legacy-removal.md +30 -17
- package/docs/optional-adapters.md +4 -0
- package/docs/screen-patterns.md +23 -1
- package/docs/toast.md +41 -2
- package/docs/usage/README.md +248 -245
- package/docs/usage/SELECTION.md +43 -0
- package/docs/usage/STANDARD.md +22 -10
- package/docs/usage/components/accordion.md +5 -3
- package/docs/usage/components/agreement.md +6 -9
- package/docs/usage/components/alert-dialog.md +12 -6
- package/docs/usage/components/asset.md +10 -4
- package/docs/usage/components/auth-provider-button.md +2 -2
- package/docs/usage/components/avatar.md +8 -6
- package/docs/usage/components/badge.md +4 -4
- package/docs/usage/components/bottom-cta.md +4 -4
- package/docs/usage/components/bottom-info.md +2 -2
- package/docs/usage/components/bottom-navigation.md +16 -5
- package/docs/usage/components/button.md +2 -2
- package/docs/usage/components/card.md +1 -1
- package/docs/usage/components/carousel.md +12 -4
- package/docs/usage/components/chat-message.md +6 -2
- package/docs/usage/components/chat-screen.md +5 -5
- package/docs/usage/components/checkbox-group.md +1 -1
- package/docs/usage/components/checkbox.md +2 -2
- package/docs/usage/components/chip.md +2 -2
- package/docs/usage/components/code-block.md +3 -4
- package/docs/usage/components/collapsible.md +6 -4
- package/docs/usage/components/combobox.md +2 -2
- package/docs/usage/components/command-palette.md +1 -1
- package/docs/usage/components/comment-thread-screen.md +7 -4
- package/docs/usage/components/container.md +1 -1
- package/docs/usage/components/content-transition.md +2 -2
- package/docs/usage/components/context-menu.md +2 -2
- package/docs/usage/components/counter-badge.md +3 -3
- package/docs/usage/components/data-table.md +1 -1
- package/docs/usage/components/date-picker.md +4 -4
- package/docs/usage/components/description-list.md +3 -3
- package/docs/usage/components/design-system-provider.md +2 -2
- package/docs/usage/components/dialog.md +14 -3
- package/docs/usage/components/divider.md +2 -2
- package/docs/usage/components/editor-screen.md +5 -3
- package/docs/usage/components/effect-surface.md +1 -1
- package/docs/usage/components/empty-state.md +6 -6
- package/docs/usage/components/field.md +5 -3
- package/docs/usage/components/file-picker.md +6 -3
- package/docs/usage/components/form.md +1 -1
- package/docs/usage/components/heading.md +3 -3
- package/docs/usage/components/icon-button.md +3 -3
- package/docs/usage/components/icon.md +1 -1
- package/docs/usage/components/image.md +5 -4
- package/docs/usage/components/keyboard-dock.md +1 -1
- package/docs/usage/components/layout.md +1 -1
- package/docs/usage/components/link.md +2 -2
- package/docs/usage/components/list-detail-screen.md +6 -6
- package/docs/usage/components/list-row.md +13 -4
- package/docs/usage/components/list.md +5 -4
- package/docs/usage/components/masonry.md +2 -1
- package/docs/usage/components/media-selection-screen.md +11 -5
- package/docs/usage/components/mentions.md +2 -2
- package/docs/usage/components/menu.md +3 -2
- package/docs/usage/components/menubar.md +2 -2
- package/docs/usage/components/message-composer.md +3 -3
- package/docs/usage/components/moderation-screen.md +3 -2
- package/docs/usage/components/notice.md +2 -2
- package/docs/usage/components/notification-inbox-screen.md +1 -1
- package/docs/usage/components/notification-item.md +2 -2
- package/docs/usage/components/number-field.md +3 -3
- package/docs/usage/components/otp-field.md +1 -1
- package/docs/usage/components/overview-screen.md +1 -1
- package/docs/usage/components/permission-screen.md +1 -1
- package/docs/usage/components/photo-source-sheet.md +1 -1
- package/docs/usage/components/popover.md +2 -2
- package/docs/usage/components/profile-screen.md +1 -1
- package/docs/usage/components/progress.md +2 -2
- package/docs/usage/components/qr-code.md +2 -2
- package/docs/usage/components/radio-group.md +1 -2
- package/docs/usage/components/radio.md +1 -2
- package/docs/usage/components/result.md +1 -1
- package/docs/usage/components/saved-items-screen.md +4 -2
- package/docs/usage/components/screen-layout.md +4 -3
- package/docs/usage/components/search-field.md +1 -1
- package/docs/usage/components/search-screen.md +4 -4
- package/docs/usage/components/section.md +3 -3
- package/docs/usage/components/segmented-control.md +4 -4
- package/docs/usage/components/select.md +2 -3
- package/docs/usage/components/settings-screen.md +11 -49
- package/docs/usage/components/sheet.md +10 -5
- package/docs/usage/components/sidebar.md +1 -1
- package/docs/usage/components/skeleton.md +1 -1
- package/docs/usage/components/skip-nav.md +1 -1
- package/docs/usage/components/slider.md +2 -2
- package/docs/usage/components/spinner.md +2 -2
- package/docs/usage/components/splitter.md +1 -1
- package/docs/usage/components/statistic.md +5 -4
- package/docs/usage/components/steps.md +2 -2
- package/docs/usage/components/surface.md +2 -3
- package/docs/usage/components/switch.md +1 -1
- package/docs/usage/components/tabs.md +4 -4
- package/docs/usage/components/tag.md +2 -2
- package/docs/usage/components/tags-input.md +4 -4
- package/docs/usage/components/text-area.md +2 -3
- package/docs/usage/components/text.md +85 -4
- package/docs/usage/components/timeline.md +2 -2
- package/docs/usage/components/toast.md +17 -7
- package/docs/usage/components/toggle-group.md +3 -3
- package/docs/usage/components/tooltip.md +1 -1
- package/docs/usage/components/top-bar.md +5 -4
- package/docs/usage/components/top.md +3 -3
- package/docs/usage/components/transfer-list.md +5 -4
- package/docs/usage/components/upload-item.md +3 -3
- package/docs/usage/components/virtual-list.md +1 -0
- package/docs/usage/compositions/action-feedback.md +42 -6
- package/docs/usage/compositions/action-recovery-optimistic.md +6 -1
- package/docs/usage/compositions/action-recovery-save.md +19 -23
- package/docs/usage/compositions/action-recovery-undo.md +6 -1
- package/docs/usage/compositions/activity-status.md +11 -5
- package/docs/usage/compositions/collection-detail.md +2 -0
- package/docs/usage/compositions/command-records.md +15 -5
- package/docs/usage/compositions/common-message.md +4 -2
- package/docs/usage/compositions/common-notification.md +5 -3
- package/docs/usage/compositions/compound-controls.md +13 -5
- package/docs/usage/compositions/content-transition-comparison.md +2 -2
- package/docs/usage/compositions/date-entry.md +10 -5
- package/docs/usage/compositions/date-time-selection.md +3 -3
- package/docs/usage/compositions/design-profile-comparison.md +7 -5
- package/docs/usage/compositions/disclosure.md +1 -1
- package/docs/usage/compositions/document-resource.md +10 -3
- package/docs/usage/compositions/expo-interactions.md +3 -2
- package/docs/usage/compositions/family-drawer.md +9 -4
- package/docs/usage/compositions/field-group.md +7 -1
- package/docs/usage/compositions/floating-action-button.md +18 -18
- package/docs/usage/compositions/illustrated-outcome.md +3 -2
- package/docs/usage/compositions/input-sheet.md +6 -2
- package/docs/usage/compositions/interaction-adapters.md +1 -1
- package/docs/usage/compositions/interaction-flow-apply.md +6 -3
- package/docs/usage/compositions/interaction-flow-draft.md +5 -2
- package/docs/usage/compositions/interaction-flow-search.md +5 -2
- package/docs/usage/compositions/live-list.md +7 -5
- package/docs/usage/compositions/navigation-bar-collection.md +8 -7
- package/docs/usage/compositions/optional-adapters.md +1 -1
- package/docs/usage/compositions/origin-dialog.md +5 -5
- package/docs/usage/compositions/paper-surface.md +1 -1
- package/docs/usage/compositions/photo-source.md +1 -1
- package/docs/usage/compositions/purpose-input-comment.md +5 -3
- package/docs/usage/compositions/purpose-input-message.md +5 -3
- package/docs/usage/compositions/reference-first.md +63 -42
- package/docs/usage/compositions/reference-review.md +60 -50
- package/docs/usage/compositions/reference-settings.md +60 -50
- package/docs/usage/compositions/selection-scope.md +48 -35
- package/docs/usage/compositions/stea-order-progress.md +1 -0
- package/docs/usage/compositions/stea-otp-verify.md +5 -3
- package/docs/usage/compositions/stea-pixel-empty.md +3 -3
- package/docs/usage/compositions/time-selection.md +6 -5
- package/docs/usage/compositions/upload-recovery.md +10 -1
- package/docs/usage/compositions/video-dialog.md +10 -3
- package/docs/usage/compositions/web-additions.md +5 -5
- package/docs/usage/compositions/web-navigation.md +1 -1
- package/docs/usage/screens/common-chat.md +21 -8
- package/docs/usage/screens/common-comments.md +13 -1
- package/docs/usage/screens/common-inbox.md +19 -6
- package/docs/usage/screens/common-login.md +19 -7
- package/docs/usage/screens/common-profile.md +22 -9
- package/docs/usage/screens/common-saved.md +16 -2
- package/docs/usage/screens/common-search.md +24 -4
- package/docs/usage/screens/common-settings.md +22 -50
- package/docs/usage/screens/common-shell.md +14 -2
- package/docs/usage/screens/dashboard.md +52 -109
- package/docs/usage/screens/discovery-gallery.md +57 -148
- package/docs/usage/screens/flow-collection.md +38 -9
- package/docs/usage/screens/flow-editor.md +25 -7
- package/docs/usage/screens/flow-media.md +23 -7
- package/docs/usage/screens/flow-moderation.md +15 -2
- package/docs/usage/screens/flow-onboarding.md +3 -3
- package/docs/usage/screens/flow-permission.md +17 -3
- package/docs/usage/screens/landing.md +37 -20
- package/docs/usage/screens/mockup-studio.md +22 -7
- package/docs/usage/screens/notification-settings.md +18 -4
- package/docs/usage/screens/product-bento.md +18 -9
- package/docs/usage/screens/reference-comparison.md +29 -10
- package/docs/usage/tokens/color.md +6 -3
- package/docs/usage/tokens/elevation-opacity.md +4 -2
- package/docs/usage/tokens/font-roles.md +2 -2
- package/docs/usage/tokens/layers.md +3 -3
- package/docs/usage/tokens/motion.md +2 -2
- package/docs/usage/tokens/radius.md +4 -2
- package/docs/usage/tokens/spacing.md +3 -3
- package/docs/usage/tokens/theme-studio.md +2 -2
- package/docs/usage/tokens/typography-studio.md +14 -8
- package/docs/usage/tokens/typography.md +15 -5
- package/package.json +1 -1
|
@@ -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`은
|
|
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.
|
|
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`은
|
|
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)은
|
|
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
|
|
93
|
+
- Native `style`은 제거되었다. 배치는 `layoutStyle`, 제목 위계는 `level`로 지정한다.
|
|
94
94
|
|
|
95
95
|
2026-10-07 추가: 제목은 프로필의 optional `tokens.fontFamily.display`를 사용하고 생략 시 현재 ui를 따른다.
|
|
96
|
-
문서 semanticLevel·크기·굵기는 유지한다. [서체 역할](../tokens/font-roles.md), 1.
|
|
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`로 배치한다.
|
|
83
|
+
| Web `layoutStyle`(NotificationBell) | `HjmCompositionStyleProp` | — | 루트 배치. 기본 `alignSelf: "flex-start"`보다 우선하지만 `width`는 무시된다(루트는 항상 `max-content`). 떠 있는 배지가 루트의 끝 모서리에 붙어 있어 루트가 넓어지면 아이콘에서 떨어지기 때문이다. margin·`alignSelf`로 배치한다. 1.13.0 변경 |
|
|
84
84
|
|
|
85
85
|
## 배치
|
|
86
86
|
|
|
@@ -117,13 +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`은
|
|
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
|
|
126
|
+
- Native `IconButton`의 `style`은 제거되었다. 배치는 `layoutStyle`, 외형은 `tone`·`size`·`shape`로 지정한다.
|
|
127
127
|
|
|
128
128
|
2026-10-10 사용자 요청: Button과 같은 선택 표현을 쓴다. 선택은 bg 바탕·contentBrand 아이콘으로
|
|
129
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`은
|
|
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 전용,
|
|
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`다(
|
|
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 확장은
|
|
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#
|
|
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
|
-
-
|
|
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) | — | — |
|
|
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`은
|
|
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
|
-
- 적용:
|
|
7
|
-
- 검토일: 2026-10-
|
|
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
|
|
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이 아니다). 목록/상세 전환과 상관없이 같은 루트에
|
|
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에만 적용된다.
|
|
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
|
|
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`
|
|
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) | — | — |
|
|
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`은
|
|
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`은
|
|
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`은
|
|
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`은
|
|
200
|
+
| 배치 | `layoutStyle`(+ `className`) | `layoutStyle`(`style`은 제거된 속성) |
|
|
200
201
|
|
|
201
202
|
2026-10-07 Utilverse 항목 삭제 채택을 위해 `renderItemAction`을 추가했다. 체크와 삭제의 초점·누름을 분리하고 큰 글자 라벨 폭을 보존하도록 행동을 다음 줄에 둔다. `renderCollection`의 `renderItem`에도 포함된다. 삭제 저장·실패·되돌리기는 제품이 처리한다.
|
|
202
203
|
|
|
@@ -88,6 +88,7 @@ import { Masonry } from "@hjmds/react-native/masonry";
|
|
|
88
88
|
- `width`가 0 이하이거나 열 폭이 0 이하, `getItemHeight`가 양의 유한수가 아니면 `TypeError`가 난다. 항목이 비어
|
|
89
89
|
`empty`만 그릴 때도 기하 검사를 먼저 하므로 같다. 측정 전(폭 0)에는 Masonry를 렌더하지 않는다.
|
|
90
90
|
- 화면 폭이 바뀌면 `width`를 다시 넘긴다.
|
|
91
|
+
- 빈 목록도 `width`를 유지하며, `layoutStyle`로 할당한 영역 안에서 안내를 가로·세로 중앙에 놓는다. 높이를 지정하지 않은 본문에서는 안내의 내용 높이를 사용한다.
|
|
91
92
|
|
|
92
93
|
## 플랫폼 차이
|
|
93
94
|
|
|
@@ -100,7 +101,7 @@ import { Masonry } from "@hjmds/react-native/masonry";
|
|
|
100
101
|
## 함정
|
|
101
102
|
|
|
102
103
|
- 모든 항목을 한 번에 그린다. 항목이 많은 무한 피드에서는 렌더 비용이 쌓인다.
|
|
103
|
-
- 항목이 없으면 `empty`만 그리고 목록 role이 붙지 않는다. Web 빈 분기는 `role="group"` + `label` 이름이다(
|
|
104
|
+
- 항목이 없으면 `empty`만 그리고 목록 role이 붙지 않는다. Web 빈 분기는 `role="group"` + `label` 이름이다(1.13.0 변경.
|
|
104
105
|
1.12.1은 role 없는 `<div aria-label>`이라 이름이 노출되지 않았다). 빈 상태 문구는 `empty` 안에 보이는 글로 둔다.
|
|
105
106
|
- 다음 페이지는 목록 다음 형제로 [LoadMore](load-more.md)를 둔다. Native에서 FlatList 밖(ScrollView 안 Masonry)이면
|
|
106
107
|
`onEndReached`가 없으므로 `mode="manual"`로 둔다. Web `automatic`은 sentinel로 동작하지만 비가상화라 항목이
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
- 단계: 컴포넌트
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용:
|
|
7
|
-
- 검토일: 2026-10-
|
|
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
|
|
|
@@ -44,7 +44,7 @@ import { MediaSelectionScreen } from "@hjmds/react/screen-flows";
|
|
|
44
44
|
items={picked.map((p) => ({ descriptor: { id: p.id, name: p.fileName, state: p.upload },
|
|
45
45
|
preview: <img src={p.uri} alt="" width={p.width} height={p.height} /> }))}
|
|
46
46
|
add={{ label: t("media.add"), onAction: openPicker }}
|
|
47
|
-
done={{ label: t("media.done"), onAction: submit, disabled: uploading }}
|
|
47
|
+
done={{ label: t("media.done"), onAction: submit, disabled: uploading || !canComplete }}
|
|
48
48
|
labels={{ pending: t("upload.pending"), uploading: t("upload.uploading"),
|
|
49
49
|
success: t("upload.success"), cancel: t("upload.cancel"), retry: t("upload.retry") }}
|
|
50
50
|
actionLabels={{ remove: t("media.remove"), moveUp: t("media.moveUp"), moveDown: t("media.moveDown") }}
|
|
@@ -66,7 +66,7 @@ import { Image } from "react-native";
|
|
|
66
66
|
items={picked.map((p) => ({ descriptor: { id: p.id, name: p.fileName, state: p.upload },
|
|
67
67
|
preview: <Image src={p.uri} width={p.width} height={p.height} /> }))}
|
|
68
68
|
add={{ label: t("media.add"), onAction: openPicker }}
|
|
69
|
-
done={{ label: t("media.done"), onAction: submit, disabled: uploading }}
|
|
69
|
+
done={{ label: t("media.done"), onAction: submit, disabled: uploading || !canComplete }}
|
|
70
70
|
labels={{ pending: t("upload.pending"), uploading: t("upload.uploading"),
|
|
71
71
|
success: t("upload.success"), cancel: t("upload.cancel"), retry: t("upload.retry") }}
|
|
72
72
|
actionLabels={{ remove: t("media.remove"), moveUp: t("media.moveUp"), moveDown: t("media.moveDown") }}
|
|
@@ -104,7 +104,7 @@ Web의 `<img alt="">`는 장식용 미리보기다. 사진 이름은 `removeLabe
|
|
|
104
104
|
| 간격 | 화면 padding `spacing.md` 16; 격자 간격 `spacing.md` 16; 항목 안 미리보기–UploadItem–버튼 줄 `spacing.xs` 8; 버튼 사이 `spacing.xxs` 4; footer 요약–완료 `spacing.sm` 12 | Web·Native `MediaSelectionScreen` |
|
|
105
105
|
| 순서·정렬 | 헤더(제목 → 추가) → 격자(항목마다 미리보기 → UploadItem → 위로·아래로·삭제) → footer(`selectionSummary` → 완료) | 렌더 순서 |
|
|
106
106
|
| 고정·스크롤 | 헤더·footer 고정, 격자는 본문 스크롤(`scroll` 기본 `"screen"`) | `ScreenLayout` |
|
|
107
|
-
| 좁은
|
|
107
|
+
| 좁은 폭 | 좁은 폭에서도 2열 유지; 항목 버튼 줄은 줄바꿈(`flexWrap: "wrap"`)해 버튼을 자르지 않는다 | `screen-flows.tsx` |
|
|
108
108
|
|
|
109
109
|
## 꼭 지킬 것
|
|
110
110
|
|
|
@@ -117,3 +117,9 @@ Web의 `<img alt="">`는 장식용 미리보기다. 사진 이름은 `removeLabe
|
|
|
117
117
|
|
|
118
118
|
- `actions`를 넘겨도 `add` 버튼이 그 자리를 덮는다. 다른 상단 행동은 `leading`이나 `notice`로 둔다.
|
|
119
119
|
- `library`를 주면 `items` 격자와 업로드 상태 표시는 그려지지 않는다. 선택 결과는 `selectionSummary`로 보여 준다.
|
|
120
|
+
|
|
121
|
+
## 삭제·순서 변경 후 Web 키보드 초점
|
|
122
|
+
|
|
123
|
+
기본 격자는 제품이 `items` 변경을 반영한 뒤 초점을 복구한다. 현재 사진을 제거하면 같은 위치의 다음 사진 제거 버튼, 마지막이면 이전 사진 제거 버튼으로 이어진다. 모두 제거하면 활성 `add`로 이동하고, 추가도 잠겼으면 본문에 남는다. 마지막 제거와 동시에 `state.kind="empty"`로 바꿔도 같은 규칙이다. 이동 후 첫·마지막 위치의 버튼이 비활성화되면 같은 사진의 사용 가능한 행동으로 이어진다. 살아 있는 미리보기 입력과 값은 key인 `descriptor.id`를 따라 유지한다. 이동된 초점이 본문 밖으로 가려지면 `scroll="screen"`의 본문만 필요한 만큼 스크롤한다.
|
|
124
|
+
|
|
125
|
+
2026-10-13 실행 검수에서 삭제·경계 이동 후 키보드 문맥이 끊겨 추가한 동작이다. 외부 컨트롤로 이동하거나 명시적으로 blur한 초점은 가져오지 않는다. `library`의 초점·선택과 Native OS 접근성 초점은 이 Web DOM 복구 범위에 포함되지 않는다. `done` 허용 조건과 요청 취소·늦은 응답 처리는 계속 제품이 소유한다.
|
|
@@ -79,7 +79,7 @@ Native 후보 목록의 모서리는 `comboboxRecipe.popover.radius` 역할을 P
|
|
|
79
79
|
| 항목 | 값 | 근거 |
|
|
80
80
|
| --- | --- | --- |
|
|
81
81
|
| 크기 | 후보 한 줄 최소 높이 44(`control.minTouchTarget`). Web 목록 최대 높이 `14rem`, 입력 폭에 맞춤 | `.hjm-mentions__option`·`__list`, Native `minHeight: 44` |
|
|
82
|
-
| 간격 | 라벨·입력·설명·오류 사이 `spacing.xs` 8. Web 목록은 입력과 8, 화면 가장자리 8, 안쪽 8, radius `radius.md` 12 — 모두 Combobox 목록과 같은 `comboboxRecipe.popover`(sideOffset·collisionPadding·padding `spacing.xs`). Native 목록 안쪽 8(같은 recipe)·항목 사이 4, radius `radius.md` 12. 후보 좌우 여백 8. 2026-10-06까지 Web 가장자리 16·안쪽 4, Native 안쪽 4였다(1.
|
|
82
|
+
| 간격 | 라벨·입력·설명·오류 사이 `spacing.xs` 8. Web 목록은 입력과 8, 화면 가장자리 8, 안쪽 8, radius `radius.md` 12 — 모두 Combobox 목록과 같은 `comboboxRecipe.popover`(sideOffset·collisionPadding·padding `spacing.xs`). Native 목록 안쪽 8(같은 recipe)·항목 사이 4, radius `radius.md` 12. 후보 좌우 여백 8. 2026-10-06까지 Web 가장자리 16·안쪽 4, Native 안쪽 4였다(1.13.0 변경) | `comboboxRecipe.popover`, `.hjm-mentions__*`, `react-native/src/mentions.tsx` |
|
|
83
83
|
| 순서·정렬 | 후보 목록은 입력 **바로 아래**. Web은 아래 공간이 모자라면 위로 뒤집는다. Web 후보는 `label` 옆에 `description`, 좁으면 줄바꿈 | `useAnchoredPopup`, `.hjm-mentions__option` |
|
|
84
84
|
| 고정·스크롤 | Web 목록은 떠 있는 portal(z-index `layer.dropdown` 400)이고 넘치면 목록 안 스크롤. Native 목록은 문서 흐름 안에 끼어들어 아래 내용을 민다 | `src/mentions.tsx`(Web·Native) |
|
|
85
85
|
| 좁은 폭·큰 글자 | 키보드 위 입력이면 Native 목록이 가려질 수 있으니 [KeyboardFormScrollView](keyboard-form-scroll-view.md) 안에 둔다 | — |
|
|
@@ -112,7 +112,7 @@ Web Native
|
|
|
112
112
|
| 후보 표시 | 입력 아래 떠 있는 listbox(portal) | 입력 아래 문서 흐름 안 목록 |
|
|
113
113
|
| 키보드 | ↑↓ 이동, Enter 확정, Esc 닫기(입력은 유지) | 해당 없음(누름으로 확정) |
|
|
114
114
|
| 기본 후보 렌더 | `label` + `description` | `label`만 |
|
|
115
|
-
| 추가 prop | `className`, ref(`HTMLTextAreaElement`). `style`은 안쪽 textarea에 붙는다 | `listStyle`은
|
|
115
|
+
| 추가 prop | `className`, ref(`HTMLTextAreaElement`). `style`은 안쪽 textarea에 붙는다 | `listStyle`은 제거된 속성(후보 목록의 외형은 recipe가 소유한다) |
|
|
116
116
|
|
|
117
117
|
## 함정
|
|
118
118
|
|
|
@@ -89,7 +89,7 @@ Native popover와 항목 모서리는 Provider token에서 recipe 역할을 해
|
|
|
89
89
|
| 항목 | 값 | 근거 |
|
|
90
90
|
| --- | --- | --- |
|
|
91
91
|
| 크기 | 항목 높이 `comfortable` 56(`layout.rowHeight.singleLine`) · `compact` 44(`control.minTouchTarget`). Web 표면 폭 `13.75rem`~`min(24rem, 90vw)`, 높이 최대 `100dvh − 2 × spacing.md`. Native 표면 폭 100%·최대 520, 높이 최대 75% | `menuRecipe.density`, `.hjm-menu__content`, `react-native/src/navigation.tsx` |
|
|
92
|
-
| 간격 | 항목 좌우 `spacing.sm` 12, leading·문구·단축키 사이 12. 섹션 제목 좌우 12 · 위아래 `spacing.xs` 8. Web: 트리거와 8(`menuRecipe.sideOffset`), 화면 가장자리 8(`collisionPadding`), 안쪽 `spacing.xs` 8(`menuRecipe.surface.padding`; 2026-10-06까지 4, 1.
|
|
92
|
+
| 간격 | 항목 좌우 `spacing.sm` 12, leading·문구·단축키 사이 12. 섹션 제목 좌우 12 · 위아래 `spacing.xs` 8. Web: 트리거와 8(`menuRecipe.sideOffset`), 화면 가장자리 8(`collisionPadding`), 안쪽 `spacing.xs` 8(`menuRecipe.surface.padding`; 2026-10-06까지 4, 1.13.0 변경), 표면·항목 radius `radius.md` 12, 구분선 위아래 4 · 좌우 8. Native: 바깥 여백·안쪽 `spacing.md` 16, 제목·목록·닫기 사이 12, radius `radius.lg` 16 | `collectionItemContract`, `menuRecipe.sectionLabel`·`surface`, `.hjm-menu__*`, `useAnchoredPopup` |
|
|
93
93
|
| 순서·정렬 | 일반 행동을 위에, `danger` 행동은 맨 아래. Web `align="start"`(기본)는 트리거 시작 끝, `end`는 끝 끝에 맞춘다(행 끝 ⋯은 `end`). Native는 위→아래 제목 → 항목 → 닫기 버튼(`secondary`) | `menuRecipe`, `src/overlays.tsx` |
|
|
94
94
|
| 고정·스크롤 | Web: 트리거에 붙는 portal(z-index `layer.dropdown` 400), 아래가 모자라면 위로 뒤집고 가로는 화면 안으로 민다. 넘치면 표면 안 스크롤. Native: `Modal`로 화면 **가운데**, scrim을 누르면 닫히고 항목 목록만 스크롤 | `useAnchoredPopup`, `react-native/src/navigation.tsx`(`Menu`) |
|
|
95
95
|
| 좁은 폭·큰 글자 | Web 폭 상한 `90vw`, 항목 문구는 줄바꿈된다. Native 높이 상한 75%를 넘으면 목록이 스크롤된다 | `.hjm-menu__content`, `maxHeight: "75%"` |
|
|
@@ -114,6 +114,7 @@ Web (트리거 아래, 앵커) Native (Modal, 화면 가운데)
|
|
|
114
114
|
- 항목 id는 비어 있지 않고 유일해야 하며, 활성 항목이 하나 이상 있어야 한다. 어기면 렌더 중 던진다.
|
|
115
115
|
- Web에서 `label`이 문자열이 아닌 항목은 typeahead용 `textValue`를 준다. 없으면 던진다.
|
|
116
116
|
- 다른 모달·시트·화면 이동을 여는 action은 `onActionAfterDismiss`로 실행한다. 이 콜백은 메뉴가 실제로 닫힌 뒤(Native는 Modal teardown 뒤)에만 실행되므로 두 표면이 겹치지 않는다.
|
|
117
|
+
- Native에서 teardown 완료 전에 재열거나 owner를 교체하면 이전에 대기하던 after-dismiss action은 폐기된다. 비동기 action의 요청 오류는 제품 콜백 안에서 처리하고 제품 오류 UI에 연결한다. 메뉴는 Promise 실패에 따른 오류 화면이나 자동 재시도를 만들지 않는다.
|
|
117
118
|
- 항목 문구·아이콘(제품 소유)은 i18n 키와 제품 아이콘으로 넣고, 색은 `tone`으로만 바꾼다.
|
|
118
119
|
- `MorphingMenu`는 action 전용이다. 선택·async가 필요하면 `Menu`를 쓴다. reduce-motion·RTL에서는 스스로 `Menu`로 돌아간다.
|
|
119
120
|
|
|
@@ -128,4 +129,4 @@ Web (트리거 아래, 앵커) Native (Modal, 화면 가운데)
|
|
|
128
129
|
| 상태 | `disabled` | `disabled`, `readOnly`(+`readOnlyLabel`), `busy`, `onRetry`/`retryLabel` |
|
|
129
130
|
| 표면 | 트리거에 붙는 portal(`portalContainer`) | `Modal` |
|
|
130
131
|
| 항목 label | `ReactNode`(+`textValue`) | `string` |
|
|
131
|
-
| 배치 | `className`, `layoutStyle` | `layoutStyle`(`style`은
|
|
132
|
+
| 배치 | `className`, `layoutStyle` | `layoutStyle`(`style`은 제거된 속성) |
|
|
@@ -93,7 +93,7 @@ Native 예는 없다(renderer 없음).
|
|
|
93
93
|
- `className`·`layoutStyle`은 배치에만 쓴다. 색·높이를 덮지 않는다.
|
|
94
94
|
|
|
95
95
|
2026-10-07 Web 전체 회귀 중 키보드로 옮긴 항목이 첫 항목으로 돌아가는 문제를 재현했다.
|
|
96
|
-
|
|
96
|
+
1.15.0 변경 수정은 패널의 늦은 mouseenter가 키보드 선택을 덮지 않고 실제 마우스 이동에
|
|
97
97
|
맞춰 활성 항목을 바꾼다. 메뉴 막대 라벨의 hover 전환·클릭·비활성 건너뛰기는 유지한다.
|
|
98
98
|
고정 지연이나 키보드 검사 재시도로 가리는 대신 입력 의도를 구분했다.
|
|
99
99
|
[검증 기록](../../../../../docs/qa/2026-10-07-command-records.md).
|
|
@@ -102,4 +102,4 @@ Native 예는 없다(renderer 없음).
|
|
|
102
102
|
|
|
103
103
|
2026-10-07 소비 감사에서 Web 라벨의 숫자 recipe 모서리가 프로필을 우회했다. 기존 `radius.sm` 역할을 가까운 Provider의 CSS 변수로 읽고 변수 없는 독립 사용은 recipe 8을 유지한다. 열린 메뉴·roving focus·비활성 건너뛰기·행동과 Web 전용 범위는 바꾸지 않는다.
|
|
104
104
|
|
|
105
|
-
프로필 연결은 1.
|
|
105
|
+
프로필 연결은 1.16.0 변경이며, 기본 배포 계약과 실제 Native 기기 검증은 구분한다.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- 단계: 컴포넌트
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용:
|
|
6
|
+
- 적용: 1.17.1
|
|
7
7
|
- 검토일: 2026-10-07
|
|
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
|
- 스토리북: `배포/구성/입력과 작성/메시지 작성`, `배포/구성/입력과 작성/댓글 작성`, `배포/화면/소통/채팅`
|
|
@@ -95,7 +95,7 @@ import { Image } from "react-native";
|
|
|
95
95
|
| `attachmentAction` | `{ label, icon, onPress(), disabled? }` | 없음 | 첨부 버튼(ghost `IconButton`) |
|
|
96
96
|
| `attachments` | `readonly { id, removeLabel, preview, disabled? }[]` | `[]` | 첨부 미리보기. 있으면 `onRemoveAttachment` 필수 |
|
|
97
97
|
| `onRemoveAttachment` | `(id: string) => void` | 없음 | 첨부 제거 |
|
|
98
|
-
| Web `layoutStyle` | `HjmCompositionStyleProp` | 없음 | 작성창 루트 배치(margin·width·flex 등).
|
|
98
|
+
| Web `layoutStyle` | `HjmCompositionStyleProp` | 없음 | 작성창 루트 배치(margin·width·flex 등). 1.13.0 변경 |
|
|
99
99
|
|
|
100
100
|
전송 가능 조건: `disabled`·`pending`·`sendDisabled`가 아니고, 글(공백 제외)·첨부·`additionalContent` 중 하나가 있을 때(`canSubmitMessage`).
|
|
101
101
|
|
|
@@ -104,7 +104,7 @@ import { Image } from "react-native";
|
|
|
104
104
|
| 항목 | 값 | 근거 |
|
|
105
105
|
| --- | --- | --- |
|
|
106
106
|
| 크기 | 입력 `composerMinLines` 1 ~ `composerMaxLines` 5줄(넘으면 입력 안에서 스크롤), `shape="large"`; 첨부 썸네일 `attachmentSize` 80(radius `radius.md`); 제거 버튼은 `IconButton size="small"` 36 + 사방 hit 4(실제 타깃 44), 그 안 보이는 원 `attachmentRemoveSize` 24; circle 전송은 `IconButton size="small"` 36 | `screen-patterns.ts`, `icon-button-recipe.ts` `sizes.small`, Web `styles.css`, Native `MessageComposer` |
|
|
107
|
-
| 간격 | 세로 묶음(context·답장 대상·첨부·입력 줄) 사이와 입력–텍스트 전송 버튼 사이 `screenPatternRecipe.itemGap`(`spacing.sm` 12, 두 플랫폼. 2026-10-06까지 Web은 8, 1.
|
|
107
|
+
| 간격 | 세로 묶음(context·답장 대상·첨부·입력 줄) 사이와 입력–텍스트 전송 버튼 사이 `screenPatternRecipe.itemGap`(`spacing.sm` 12, 두 플랫폼. 2026-10-06까지 Web은 8, 1.13.0 변경); 답장 대상 줄·첨부 사이 `spacing.sm` 12; circle 전송은 입력 글자와 `spacing.sm` 12, 오른쪽 끝 8 | `screenPatternRecipe.itemGap`, Web `.hjm-message-composer`·`__row`(`--hjm-message-composer-gap`), Native `Stack gap="sm"`·`itemGap` |
|
|
108
108
|
| 순서·정렬 | `context` → 답장 대상(작성자·발췌 → 취소) → 첨부 썸네일(→ 첨부 버튼) → 입력·전송 한 줄, 전송은 입력 아래쪽(`flex-end`)에 맞춘다. 입력 줄 안은 `leadingAction` → 글자 → (아이콘 모드) 전송/첨부 | 렌더 순서, Web `forms.tsx` `hjm-field__leading` |
|
|
109
109
|
| 고정·스크롤 | 자체 고정 없음: 화면 composer 슬롯(footer)에 넣는다; 첨부는 가로 스크롤; 키보드 adapter는 host 하나만 | ChatScreen·CommentThreadScreen `composer` |
|
|
110
110
|
| 좁은 폭·큰 글자 | 입력이 남은 폭을 채우고(`flex: 1`, 최소 폭 0) 전송 버튼은 줄지 않는다; 큰 글자에서도 5줄 상한 뒤 입력 안 스크롤로 기록을 가리지 않는다 | Web `.hjm-message-composer__row`, Native `layoutStyle={{ flex: 1 }}` |
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
- 단계: 컴포넌트
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용:
|
|
7
|
-
- 검토일: 2026-10-
|
|
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
|
|
|
@@ -111,3 +111,4 @@ import { ModerationScreen } from "@hjmds/react-native/screen-flows";
|
|
|
111
111
|
- 차단 버튼의 `block.action.onAction`은 호출되지 않는다. 버튼은 확인 대화상자만 열고, 실제 차단은 `confirmation.onConfirm`에서 한다.
|
|
112
112
|
- 서버 신고·권한·차단 mutation, 완료 후 이동은 제품 소유다. 사유 목록과 정책 문구도 제품이 정한다.
|
|
113
113
|
- 확인 대화상자의 성공 후 닫힘·실패 문구는 [AlertDialog](alert-dialog.md) 계약을 따른다.
|
|
114
|
+
- `block`을 제거하면 열린 확인 세션도 종료한다. 나중에 차단 행동을 다시 공급해도 사용자가 버튼을 새로 눌러야 확인창이 열린다. 2026-10-13 사라진 행동의 확인 상태가 부활하던 경로를 수정했다. 대상이 바뀌는 제품은 대상 id를 화면 key/초안·요청 수명에 함께 연결한다.
|
|
@@ -95,7 +95,7 @@ import { Notice } from "@hjmds/react-native/feedback";
|
|
|
95
95
|
## 꼭 지킬 것
|
|
96
96
|
|
|
97
97
|
- 제목·설명·행동 문구는 i18n 키로 넣는다. 아이콘은 제품 소유 자산이고 색은 tone이 정한다.
|
|
98
|
-
- 배치는 `layoutStyle`로만 한다. 색·padding·radius를 `style`/`className`으로 덮지 않는다. Native `style`은
|
|
98
|
+
- 배치는 `layoutStyle`로만 한다. 색·padding·radius를 `style`/`className`으로 덮지 않는다. Native `style`은 제거된 속성이다.
|
|
99
99
|
- 같은 화면에 같은 내용의 Notice와 Toast를 동시에 띄우지 않는다.
|
|
100
100
|
|
|
101
101
|
## 플랫폼 차이
|
|
@@ -105,7 +105,7 @@ import { Notice } from "@hjmds/react-native/feedback";
|
|
|
105
105
|
| 발표 | 항상 live region. `danger`는 `role="alert"`+assertive, 나머지는 `role="status"`+polite | `announcement`: `none`(기본) · `polite` · `assertive`. 지정해야 발표한다 |
|
|
106
106
|
| title/description 타입 | `ReactNode` | `string` |
|
|
107
107
|
| 아이콘 렌더 함수 | 없음 | `renderIcon` |
|
|
108
|
-
| 배치 | `className`, `layoutStyle`(HTML `style`도 받음) | `layoutStyle`(`style`은
|
|
108
|
+
| 배치 | `className`, `layoutStyle`(HTML `style`도 받음) | `layoutStyle`(`style`은 제거된 속성) |
|
|
109
109
|
|
|
110
110
|
## 함정
|
|
111
111
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- 단계: 컴포넌트
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용:
|
|
6
|
+
- 적용: 1.17.1
|
|
7
7
|
- 검토일: 2026-10-06
|
|
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
|
- 스토리북: `배포/화면/소통/알림함`
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- 단계: 컴포넌트
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용:
|
|
6
|
+
- 적용: 1.17.1
|
|
7
7
|
- 검토일: 2026-10-06
|
|
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
|
- 스토리북: `배포/구성/정보 표시/알림 항목`, `배포/화면/소통/알림함`
|
|
@@ -95,4 +95,4 @@ import { NotificationItem } from "@hjmds/react-native/screens";
|
|
|
95
95
|
| 상태·시각 줄 | 설명 아래 caption `Text` | `description`과 줄바꿈(`\n`)으로 합친 한 문자열 |
|
|
96
96
|
| 이동 | `href`, `onClick` | `onPress` |
|
|
97
97
|
| 추가 슬롯 | `className`, `layoutStyle` | `titleMetadata`, `trailingAction`, `trailingText`, `layoutStyle` |
|
|
98
|
-
| 제목 굵기 조정 | `Text emphasis` | 렌더러 비공개 입력(`hjmTitleEmphasis`)으로 안 읽음 700(`fontWeight.bold`)·읽음 400(`fontWeight.regular`).
|
|
98
|
+
| 제목 굵기 조정 | `Text emphasis` | 렌더러 비공개 입력(`hjmTitleEmphasis`)으로 안 읽음 700(`fontWeight.bold`)·읽음 400(`fontWeight.regular`). 제거된 `ListRow.titleStyle`을 사용하지 않는다. `NotificationItemProps`에는 `titleStyle`이 없다(1.13.0 변경) |
|
|
@@ -120,7 +120,7 @@ Native는 제품 프로필의 `tokens.fontFamily.ui`를 실제 텍스트/입력
|
|
|
120
120
|
- 단위·통화·소수 자릿수 표시는 만들지 않는다. 보조기기용 문구가 필요하면 `getValueText`로 제품이 포맷한다.
|
|
121
121
|
- 제어형과 비제어형을 렌더 사이에 바꾸지 않는다(`value`를 넣었다 뺐다 하면 던진다).
|
|
122
122
|
- `min ≥ max`, `step ≤ 0`, 범위 밖 `value`는 던진다. DurationField도 범위 밖·정수 아닌 초를 던진다.
|
|
123
|
-
- 색·높이를 덮지 않는다. 배치는 `layoutStyle`로 한다. Native `inputStyle`·`containerStyle`은
|
|
123
|
+
- 색·높이를 덮지 않는다. 배치는 `layoutStyle`로 한다. Native `inputStyle`·`containerStyle`은 제거된 속성 — 높이·글꼴은 `size`, 배치는 `layoutStyle`.
|
|
124
124
|
|
|
125
125
|
## 플랫폼 차이
|
|
126
126
|
|
|
@@ -129,5 +129,5 @@ Native는 제품 프로필의 `tokens.fontFamily.ui`를 실제 텍스트/입력
|
|
|
129
129
|
| 역할 | `spinbutton`, `type="text"` | 입력 + 증감 button, accessibility action |
|
|
130
130
|
| label·description·error 타입 | `ReactNode` | `string` |
|
|
131
131
|
| 추가 이름 | 없음 | `accessibilityLabel`, `accessibilityHint` |
|
|
132
|
-
| 클래스·스타일 | `className`, `inputClassName`, `layoutStyle`(필드 전체). `style`은 안쪽 input | `layoutStyle`(필드 전체). `inputStyle`·`containerStyle`은
|
|
133
|
-
| DurationField 배치 | `className`, `layoutStyle`(fieldset) | `layoutStyle`(
|
|
132
|
+
| 클래스·스타일 | `className`, `inputClassName`, `layoutStyle`(필드 전체). `style`은 안쪽 input | `layoutStyle`(필드 전체). `inputStyle`·`containerStyle`은 제거된 속성 |
|
|
133
|
+
| DurationField 배치 | `className`, `layoutStyle`(fieldset) | `layoutStyle`(1.13.0 변경, 1.12.1은 감싸는 View) |
|
|
@@ -97,5 +97,5 @@ import { OtpField } from "@hjmds/react-native/otp-field";
|
|
|
97
97
|
| 항목 | Web | Native |
|
|
98
98
|
| --- | --- | --- |
|
|
99
99
|
| 이름 | `label` | `label` 또는 `accessibilityLabel`만 |
|
|
100
|
-
| 칸 스타일 통로 | 없음 | `slotStyle`, `slotTextStyle`은
|
|
100
|
+
| 칸 스타일 통로 | 없음 | `slotStyle`, `slotTextStyle`은 제거된 속성. 외형은 `size`·`presentation` |
|
|
101
101
|
| busy 표현 | read-only + `aria-busy` | `editable={false}` + `accessibilityState.busy` |
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
같은 데이터와 기능을 유지하면서 테마별 행·카드·격자와 도구 배치를 선택하는 목록 화면에 쓴다.
|
|
14
14
|
|
|
15
|
-
기본 공개 API는 npm 1.15.0에 있다.
|
|
15
|
+
기본 공개 API는 npm 1.15.0에 있다. 글자 역할·종이 줄 간격·토큰 소비 개선은 1.16.0 변경이다. 실제 설치 버전과 기기 검증은 별도로 확인한다.
|
|
16
16
|
|
|
17
17
|
## 쓰지 않을 때
|
|
18
18
|
|