@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.
- 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/activity-chip.d.ts +127 -0
- package/dist/activity-chip.d.ts.map +1 -0
- package/dist/activity-chip.js +65 -0
- package/dist/activity-chip.js.map +1 -0
- package/dist/base-recipes.d.ts +1 -1
- package/dist/base-recipes.d.ts.map +1 -1
- package/dist/base-recipes.js +2 -1
- package/dist/base-recipes.js.map +1 -1
- package/dist/behaviors.d.ts +9 -6
- package/dist/behaviors.d.ts.map +1 -1
- package/dist/behaviors.js +5 -3
- package/dist/behaviors.js.map +1 -1
- package/dist/bottom-navigation.d.ts +16 -0
- package/dist/bottom-navigation.d.ts.map +1 -1
- package/dist/bottom-navigation.js +18 -0
- package/dist/bottom-navigation.js.map +1 -1
- package/dist/card.d.ts +4 -0
- package/dist/card.d.ts.map +1 -1
- package/dist/card.js +4 -1
- package/dist/card.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 +80 -9
- package/dist/catalog.d.ts.map +1 -1
- package/dist/component-recipes.d.ts +74 -3
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +38 -6
- package/dist/component-recipes.js.map +1 -1
- package/dist/content-transition.d.ts +1 -1
- package/dist/content-transition.d.ts.map +1 -1
- package/dist/content-transition.js +2 -2
- package/dist/content-transition.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/icon-button-recipe.d.ts +1 -5
- package/dist/icon-button-recipe.d.ts.map +1 -1
- package/dist/icon-button-recipe.js +2 -1
- package/dist/icon-button-recipe.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/screen-patterns.d.ts +10 -0
- package/dist/screen-patterns.d.ts.map +1 -1
- package/dist/screen-patterns.js +17 -3
- package/dist/screen-patterns.js.map +1 -1
- package/dist/toast-liquid.d.ts +1 -1
- package/dist/toast-liquid.d.ts.map +1 -1
- package/dist/toast-liquid.js +3 -2
- package/dist/toast-liquid.js.map +1 -1
- package/dist/toast.d.ts +29 -2
- package/dist/toast.d.ts.map +1 -1
- package/dist/toast.js +59 -3
- 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/activity-chip.md +34 -0
- package/docs/asset.md +7 -0
- package/docs/bottom-navigation.md +16 -1
- 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 +64 -2
- package/docs/usage/README.md +248 -243
- 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/activity-chip.md +109 -0
- 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 +18 -6
- package/docs/usage/components/button.md +6 -3
- package/docs/usage/components/card.md +28 -7
- package/docs/usage/components/carousel.md +12 -4
- package/docs/usage/components/chat-message.md +10 -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 +3 -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 +6 -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 +18 -56
- 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 +5 -3
- package/docs/usage/components/text.md +85 -4
- package/docs/usage/components/timeline.md +2 -2
- package/docs/usage/components/toast.md +41 -13
- 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 +168 -0
- 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 +7 -1
|
@@ -84,6 +84,7 @@ Native 화면(ScrollView, 위아래 spacing.lg 20) > Container gutter 16(폭 < 6
|
|
|
84
84
|
| 완료 | 모든 단계 complete, 진행 문구 "모든 단계가 확정됐어요.", secondary 버튼 | 진행 문구가 읽힌다 |
|
|
85
85
|
|
|
86
86
|
- 요청 중 중복 요청은 상태 로직에서 무시한다. 버튼 `loading`만으로는 키보드 반복 입력을 막지 못한다.
|
|
87
|
+
- 예제 실패 스위치도 요청 중에는 잠그고 늦은 toggle callback을 reducer에서 무시한다. 응답을 기다리는 동안 스위치가 진행 중 요청의 결과를 뒤집던 실제 예제 검수에 따른 보완이다.
|
|
87
88
|
- 문구 키는 상태별 상수로 둔다. 상태 이름으로 키를 조립하지 않는다.
|
|
88
89
|
|
|
89
90
|
| 상태 | 진행 문구 키 | Notice 키 | 버튼 키 |
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
| `Card` | 제목("인증번호 확인")·설명을 가진 틀 | [Card](../components/card.md) |
|
|
22
22
|
| `ContentTransition` | 입력 폼 ↔ 성공 결과 전환(기본 `fade`). 입력 중엔 같은 subtree를 유지 | [ContentTransition](../components/content-transition.md) |
|
|
23
23
|
| `OtpField` | `length` 6 숫자 칸. 확인 중 `busy`, 잠김 `disabled`, 틀림·요청 실패 `error` | [OtpField](../components/otp-field.md) |
|
|
24
|
-
| 재전송 안내 문구 | 다시
|
|
24
|
+
| 재전송 안내 문구 | 예제는 "예제 인증번호를 다시 받았어요. 246810을 입력해 주세요.", 제품은 실제 재전송 완료·실패 문구(muted, live) | [Text](../components/text.md) |
|
|
25
25
|
| `Button` primary | 확인. 확인 중 `loading` | [Button](../components/button.md) |
|
|
26
26
|
| `Button` ghost | 인증번호 다시 받기. 대기 시간 동안 `disabled`, 라벨이 남은 초를 보여 줌. 재전송 요청 중 `loading` | [Button](../components/button.md) |
|
|
27
27
|
| `Result` `status="success"` | 성공 결과 + 행동 하나. Web은 `icon`을 비우면 CSS가 ✓를 그린다. Native는 기본 glyph가 없으니 `renderIcon`으로 제품 glyph를 넘긴다(비우면 빈 원) | [Result](../components/result.md) |
|
|
@@ -93,6 +93,7 @@ Native 화면(ScrollView keyboardShouldPersistTaps="handled", 위아래 spacing.
|
|
|
93
93
|
| 성공 | `Result` success, 행동 하나(제품의 다음 단계, 예 `otp.continue`). 스토리의 "다른 번호로 다시 해 보기"는 데모 초기화용 | Web: Result(`tabIndex={-1}`)로 포커스 이동 |
|
|
94
94
|
|
|
95
95
|
- 스토리의 정답 `246810`, 대기 10초(실서비스는 보통 30~60초), 지연 900ms는 데모 값이다. 시도 횟수·대기 시간·검증은 서버와 제품 소유다.
|
|
96
|
+
- 예제는 실제 문자를 보내거나 이전 번호를 폐기하지 않는다. 같은 데모 번호를 사용하는 구현에 맞춰 안내하고, 서버의 재전송 성공으로 표현하지 않는다.
|
|
96
97
|
- 문구 키는 상태별 상수로 둔다(아래 `otpErrorKey`). 상태 이름으로 키를 조립하면 상태 표와 키가 어긋난다.
|
|
97
98
|
|
|
98
99
|
| 상태 | 문구 키 | 변수 |
|
|
@@ -199,8 +200,9 @@ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
|
199
200
|
| --- | --- | --- |
|
|
200
201
|
| 제출 | `<form onSubmit>` + `type="submit"` 버튼(Enter 제출) | `onComplete`로 6자리 도달 시 자동 제출 + 확인 버튼 |
|
|
201
202
|
| 바깥 틀 | 제품 화면 레이아웃(문서 스크롤) | `ScrollView keyboardShouldPersistTaps="handled"` > `Container` |
|
|
202
|
-
| 재전송 후 포커스 | 입력으로 `focus()` |
|
|
203
|
-
| 성공 후 포커스 | `Result`(`tabIndex={-1}`)로
|
|
203
|
+
| 재전송 후 포커스 | 첫 번째·반복 재전송 모두 입력으로 `focus()` | 입력 ref의 `focus()` 호출 |
|
|
204
|
+
| 성공 후 포커스 | 사라지는 폼에 초점이 있었으면 `Result`(`tabIndex={-1}`)로 이동. 사용자가 옮긴 외부 초점은 유지 | 결과 행동을 각각 탐색할 수 있게 유지하며 iOS 성공 안내·Android polite live region 연결 |
|
|
205
|
+
| 성공 뒤 데모 초기화 | 새 입력으로 복귀 | 새 입력 ref의 `focus()` 호출 |
|
|
204
206
|
| 성공 아이콘 | `icon`을 비우면 CSS가 ✓ | `renderIcon`으로 제품 glyph(비우면 빈 원) |
|
|
205
207
|
| 상태 문구 | `role="status"` | `accessibilityLiveRegion="polite"` |
|
|
206
208
|
| busy 표현 | read-only + `aria-busy` | `editable={false}` + `accessibilityState.busy` |
|
|
@@ -47,7 +47,7 @@ Native 화면(ScrollView, 위아래 spacing.lg 20) > Container gutter 16(폭 < 6
|
|
|
47
47
|
| --- | --- | --- | --- |
|
|
48
48
|
| 바깥 틀 | Web: 제품 화면 레이아웃(문서 스크롤). Native: `ScrollView` > `Container` | 이 구성은 바깥 폭·여백을 정하지 않는다(Web 스토리는 Stack만 그린다). Native는 `ScrollView` 안 [Container](../components/container.md)에 둔다. 입력이 없어 키보드 처리는 없다. 안전 영역은 화면 골격(내비게이션 헤더·탭 바)이 맡는다 | Native ScrollView 위아래 `spacing.lg` 20, Container gutter 폭 600 미만 `compact` 16 · 이상 `regular` 20(`layout.pagePadding`) |
|
|
49
49
|
| 빈 상태 | EmptyState `density="regular"` | 본문 가운데 | 위아래 Web `spacing.xxl` 32 / Native `spacing.xxxl` 40, 좌우 `spacing.xl` 24, 슬롯 사이 `spacing.xs` 8 |
|
|
50
|
-
| 캐릭터 | Web `icon` / Native `illustration` | EmptyState 맨 위 | 96×96(12칸 × `spacing.xs` 8). Native
|
|
50
|
+
| 캐릭터 | Web `icon` / Native `illustration` | EmptyState 맨 위 | 96×96(12칸 × `spacing.xs` 8). Native도 그림 노드 자체의 96×96 크기를 사용한다 |
|
|
51
51
|
| 시작 행동 | Button `primary` | EmptyState `action` | 높이 44, 하나만 |
|
|
52
52
|
| 멈춤 제어 | Button `ghost` | EmptyState 아래, 꽉 찬 폭(`Stack` 기본 `align="stretch"`) | 묶음 사이 `spacing.md` 16 |
|
|
53
53
|
| 상태 문구 | Text muted | 맨 아래. Web은 빈 `role="status"`를 늘 마운트, Native는 문구가 있을 때만 | `spacing.md` 16 |
|
|
@@ -109,7 +109,7 @@ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
|
109
109
|
<ScrollView contentContainerStyle={{ paddingVertical: spacing.lg }}>
|
|
110
110
|
<Container gutter={gutter}>
|
|
111
111
|
<Stack gap="md">
|
|
112
|
-
<EmptyState illustration={<ProductMascot paused={paused} />}
|
|
112
|
+
<EmptyState illustration={<ProductMascot paused={paused} />}
|
|
113
113
|
title={t("records.empty.title")} description={t("records.empty.description")}
|
|
114
114
|
action={<Button loading={starting} onPress={startRecord}>{t("records.empty.start")}</Button>} />
|
|
115
115
|
<Button tone="ghost" onPress={() => setPaused((v) => !v)}>
|
|
@@ -127,7 +127,7 @@ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
|
127
127
|
|
|
128
128
|
| 항목 | Web | Native |
|
|
129
129
|
| --- | --- | --- |
|
|
130
|
-
| 그림 슬롯 | `icon` | `illustration`
|
|
130
|
+
| 그림 슬롯 | `icon` | `illustration`(최소 `glyph.lg` 크기만 보장하고 캐릭터 자체 크기를 존중한다) |
|
|
131
131
|
| 세로 여백 | `.hjm-empty-state` `spacing.xxl` 32 | recipe `spacing.xxxl` 40 |
|
|
132
132
|
| 일시정지 감지 | `document.hidden` | `AppState` |
|
|
133
133
|
| 상태 문구 자리 | 빈 `role="status"`를 늘 마운트 | 문구가 있을 때만 마운트 |
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
6
|
- 적용: 1.12.1
|
|
7
|
-
- 검토일: 2026-10-
|
|
7
|
+
- 검토일: 2026-10-13
|
|
8
8
|
- 근거: `showcase/web/src/patterns/TimeSelection.stories.tsx`, `showcase/native/src/TimeSelection.stories.tsx`, `showcase/native/src/pattern-status.tsx`, `showcase/shared/time-example.ts`, `src/foundations.ts`(`control`, `spacing`), `src/component-recipes.ts`(`stackRecipe`, `sectionRecipe`), `src/container.ts`
|
|
9
9
|
- 스토리북: `배포/구성/선택과 필터/시간 선택`
|
|
10
10
|
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
## 배치
|
|
31
31
|
|
|
32
32
|
```text
|
|
33
|
-
Native 화면(ScrollView, 위아래 spacing.lg 20) > Container gutter 16
|
|
33
|
+
Native 화면(ScrollView, 위아래 spacing.lg 20) > Container gutter compact 16
|
|
34
34
|
┌ Section(스크롤과 함께 흐름) ───────────┐
|
|
35
35
|
│ 제목 (Section title) │
|
|
36
36
|
│ 설명 (muted) │ 제목–설명 spacing.xxs 4
|
|
@@ -55,7 +55,7 @@ Native 화면(ScrollView, 위아래 spacing.lg 20) > Container gutter 16(폭 < 6
|
|
|
55
55
|
|
|
56
56
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
57
57
|
| --- | --- | --- | --- |
|
|
58
|
-
| 바깥 틀 | Web: 제품 화면 레이아웃(문서 스크롤). Native: `ScrollView` > `Container` |
|
|
58
|
+
| 바깥 틀 | Web: 제품 화면 레이아웃(문서 스크롤). Native: `ScrollView` > `Container` | Web 스토리도 Container gutter="compact" 안에 Section을 둔다. Native는 `ScrollView` 안 [Container](../components/container.md)에 둔다. Select는 키보드 대신 목록 표면(Web popover, Native modal sheet)을 열어 키보드 처리는 없다. 안전 영역은 화면 골격(내비게이션 헤더·탭 바)이 맡는다 | Native ScrollView 위아래 `spacing.lg` 20, 양 Story의 Container gutter는 `compact` 16이며 폭에 따라 자동 변경되지 않음 |
|
|
59
59
|
| 머리 | Section 제목·설명 | 흐름 맨 위, 스크롤과 함께 | 제목–설명 `spacing.xxs` 4, 머리–내용 `spacing.xs` 8 |
|
|
60
60
|
| 입력 | Select 시 → 분 | 머리 아래, 시가 먼저 | 필드 `control.fieldHeight` 44, 사이 `spacing.md` 16 |
|
|
61
61
|
| 상태 | 상태 문구 | 입력 바로 아래 | 위 `spacing.md` 16 |
|
|
@@ -162,8 +162,7 @@ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
|
162
162
|
| --- | --- | --- |
|
|
163
163
|
| 목록 표면 | 트리거에 붙은 listbox popover | modal sheet |
|
|
164
164
|
| 필수 문구 | `emptySelectionLabel`(선택 해제 항목) | `dismissLabel`(시트 닫기) |
|
|
165
|
-
| 상태 문구 낭독 | `role="status"` |
|
|
166
|
-
| 테마·글자 스토리 | 기본·어두운 테마·큰 글자(textScale 2) | 기본·어두운 테마·큰 글자(textScale 2) |
|
|
165
|
+
| 상태 문구 낭독 | `role="status"` | 공개 StatusText(Android live region + iOS 변경 알림) |
|
|
167
166
|
| 확정 Notice 발표 | 늘 live region | `announcement`를 지정해야 발표(기본 `none`) |
|
|
168
167
|
| 바깥 틀 | 제품 화면 레이아웃(문서 스크롤) | `ScrollView` > `Container` |
|
|
169
168
|
|
|
@@ -172,3 +171,5 @@ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
|
172
171
|
- 값을 바꾸면 이전 확정 결과(`result`)를 지운다. 남겨 두면 새로 고른 시각과 다른 확정 Notice가 보인다.
|
|
173
172
|
- 스토리의 확정은 로컬 상태만 바꾼다. 서버 저장의 진행·실패·재시도 검증을 대신하지 않는다.
|
|
174
173
|
- 2026-10-06 검수에서 예제도 Container·Section·Text와 Native 확정 Notice의 `announcement="polite"`를 사용하도록 맞췄다. 제목 의미와 iOS 알림을 개별 View/Text 스타일로 다시 만들지 않는다.
|
|
174
|
+
|
|
175
|
+
2026-10-13 실제 독립 Story 검수: 한쪽 값만 선택한 상태는 확정하지 못하고, 자정 00:00도 유효하다. 선택 해제/변경은 이전 확인 결과를 지우며 다시 고르기는 두 값과 결과를 함께 비운다. 기존 지침의 반응형 gutter 설명은 실제 양 Story의 고정 compact 값과 달라 바로잡았다. 위 코드 골격의 반응형 gutter는 제품이 선택하는 별도 예시다.
|
|
@@ -65,7 +65,7 @@ Button의 실행 콜백은 Web `onClick`, Native `onPress`로 연결한다. 위
|
|
|
65
65
|
| 항목 | Web | Native |
|
|
66
66
|
| --- | --- | --- |
|
|
67
67
|
| 배치/테마 | Stack과 HjmProvider | Stack과 HjmNativeProvider |
|
|
68
|
-
|
|
|
68
|
+
| 좁은 폭 | 줄바꿈·단일 내용 | 같은 순서, OS 화면 검증은 별도 |
|
|
69
69
|
|
|
70
70
|
### 예제의 제한과 초점 복구
|
|
71
71
|
|
|
@@ -78,3 +78,12 @@ Button의 실행 콜백은 Web `onClick`, Native `onPress`로 연결한다. 위
|
|
|
78
78
|
Native onPick은 예제 후보를 반환하는 fixture다. Native 시스템 사진 선택기 검증으로 세지 않는다.
|
|
79
79
|
서버 비전송 안내는 성공·거부 상태와 별도로 항상 표시한다. 실제 제품은 서버 확정·취소 시
|
|
80
80
|
늦게 도착하는 응답·전송 ticket의 소유권을 제품 업로드 계층에 연결해야 한다.
|
|
81
|
+
|
|
82
|
+
2026-10-13 실제 예제에서 중복 후보가 남은 슬롯을 차지하는 선택과 같은 렌더의 연속 드롭·선택이
|
|
83
|
+
3개 제한을 넘는 경로를 재현했다. 양쪽 예제는 `showcase/shared/reference-adoption.ts`의
|
|
84
|
+
`mergeUploadSelection`으로 현재 목록과 중복을 먼저 확인한 뒤 남은 개수를 판정한다. 개수 초과 후보만
|
|
85
|
+
다시 판정하며 파일 형식·크기 거부는 유지한다. 예제 사진 추가도 같은 제한을 따른다.
|
|
86
|
+
|
|
87
|
+
수동 응답 예제의 콜백은 해당 파일의 현재 상태 객체에 연결한다. 취소·재시도·제거 후 다시 선택하면
|
|
88
|
+
이전 상태의 성공·실패 콜백을 무시한다. 다른 파일을 추가하는 동작은 진행 중 파일의 응답을 무효화하지 않는다.
|
|
89
|
+
이는 예제 이벤트 수명 보호이며 실제 네트워크 요청 취소나 시스템 파일 선택기 검증을 뜻하지 않는다.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
- 지원: Web · Native
|
|
6
6
|
- 적용: 1.14.0
|
|
7
7
|
- 검토일: 2026-10-07
|
|
8
|
-
- 근거: `showcase/web/src/patterns/video-dialog-preview.tsx`, `showcase/native/src/video-dialog-preview.tsx`
|
|
8
|
+
- 근거: `showcase/web/src/patterns/video-dialog-preview.tsx`, `showcase/native/src/video-dialog-preview.tsx`·`video-dialog-host.ts`(Expo 로딩 경계)
|
|
9
9
|
- 스토리북: `배포/구성/정보 표시/영상 미리보기`
|
|
10
10
|
|
|
11
11
|
승급: 2026-10-07 사용자 승인, [검토 결과](../../../../../docs/qa/2026-10-07-experiment-promotion-release.md). Storybook 분류이며 제품 적용 증거는 별도다.
|
|
@@ -49,17 +49,18 @@ Dialog: [제목 닫기]
|
|
|
49
49
|
1. 제품이 open과 입력 초안을 소유한다. 닫기 후 초안을 지우지 않는다.
|
|
50
50
|
2. open일 때만 플레이어를 mount한다. 자동 재생하지 않고 사용자 행동으로 시작한다.
|
|
51
51
|
3. 브라우저/Native 미디어 이벤트에서 재생·정지·실패를 읽는다. 요청 성공으로 재생을 추정하지 않는다.
|
|
52
|
-
4. 실패 시 설명과 재시도를 유지한다. 재시도는 정상 source로 호스트를 새로 만들며 Web은 재생 버튼으로 초점을 옮긴다.
|
|
52
|
+
4. 실패 시 설명과 재시도를 유지한다. Web에서 사라질 재생 컨트롤이 초점을 갖고 있으면 재시도로 옮기고, 닫기 버튼 등 다른 초점은 유지한다. 재시도는 정상 source로 호스트를 새로 만들며 Web은 새 재생 버튼으로 초점을 옮긴다.
|
|
53
53
|
5. open=false 즉시 호스트를 unmount한다. 모달 퇴장 애니메이션이 끝날 때까지 기다리지 않는다.
|
|
54
54
|
6. Web은 pause 후 src를 제거하고 load하여 자원을 해제한다. Native useVideoPlayer는 unmount 시 player를 release한다.
|
|
55
55
|
7. 앱/문서가 비활성화되면 일시 정지한다. 복귀 시 자동 재생하지 않는다.
|
|
56
|
+
8. Web `play()`가 준비 중이면 Button `loading`으로 같은 요청을 막으면서 키보드 초점을 유지한다. DOM `disabled`로 Enter 조작의 초점을 지우지 않는다. 재생 여부는 미디어 이벤트로 표시하고, 정지·오류·닫기 뒤 늦은 promise 결과는 반영하지 않는다.
|
|
56
57
|
|
|
57
58
|
| 상태 | 모습 | 포커스·알림 |
|
|
58
59
|
| --- | --- | --- |
|
|
59
60
|
| 기본 | 열기 전 플레이어 없음, 메모 유지 | 열기 버튼 |
|
|
60
61
|
| 진행 중 | 실제 이벤트에 따른 일시 정지와 상태 | 재생 상태 알림 |
|
|
61
62
|
| 실패 | 설명·다시 시도, 원래 메모 유지 | 오류 알림, Web 재시도 후 재생 버튼 |
|
|
62
|
-
| 호스트 없음 | Native 지원 모듈 누락
|
|
63
|
+
| 호스트 없음 | Native 지원 모듈 누락 안내와 동등한 텍스트 설명, 닫기 가능 | 제목과 안내 |
|
|
63
64
|
| 닫힘 | 영상 호스트 제거 | 열기 버튼으로 초점 복귀 |
|
|
64
65
|
|
|
65
66
|
## 코드 골격
|
|
@@ -98,3 +99,9 @@ Web은 HTML video controls와 playsInline을 사용한다. Native 예제는 Expo
|
|
|
98
99
|
| 정리 | pause·src 제거·load | hook 소유 player 해제 |
|
|
99
100
|
| 지원 누락 | 미디어 오류와 재시도 | 모듈 누락 안내 |
|
|
100
101
|
| 설명 | 화면의 동등한 텍스트 | 화면의 동등한 텍스트 |
|
|
102
|
+
|
|
103
|
+
2026-10-13 예제 검수에서 Web의 연속 재생 요청과 오류 시 초점 유실, Native 모듈 누락 시 텍스트 설명 누락을
|
|
104
|
+
재현해 수정했다. Web은 Chromium·WebKit·Firefox에서 제어한 HTML 미디어 이벤트·promise·키보드 초점
|
|
105
|
+
8개 행동과, 미디어 모형을 제거한 번들 MP4의 메타데이터·실제 재생 시간 증가·일시 정지·닫기 시 자원 해제를
|
|
106
|
+
확인했다. Native 4개 행동은 Expo 호스트 모형의 이벤트·해제 연결을 확인한다. Native 기기의 코덱 재생,
|
|
107
|
+
OS 미디어 동작, 네트워크 복구는 미실행이다.
|
|
@@ -74,13 +74,13 @@ Native 화면에서 같은 일이 필요하면 이 구성을 옮기지 않는다
|
|
|
74
74
|
2. HEX 입력은 Enter·blur에 확정되고 Escape는 마지막 `value`로 되돌린다.
|
|
75
75
|
3. 문서 표시: 워터마크 위에서 본문을 선택하고 버튼을 누른다. 워터마크는 상태가 없다.
|
|
76
76
|
4. 고정 실행 영역: 스크롤하면 Affix가 상단에 붙고 `onChange(true)`, 다시 올리면 `onChange(false)`가 온다.
|
|
77
|
-
5.
|
|
77
|
+
5. 현재 예제의 저장은 동기 메모리 확정이다. 성공 뒤 같은 버튼을 다시 눌러 저장이 해제되지 않으며, 문서 표시·고정 실행 영역의 저장 상태는 독립적이다. 제품 비동기 저장에 연결할 때는 `loading`과 실패 Notice를 공급하고 성공하면 Notice를 내린다.
|
|
78
78
|
|
|
79
79
|
| 상태 | 모습 | 포커스·알림 |
|
|
80
80
|
| --- | --- | --- |
|
|
81
|
-
| 기본 | ColorPicker는 `value` 색, 워터마크 타일, Affix는 일반 위치(`data-affixed`
|
|
82
|
-
| 진행 중 | 저장 버튼 `loading`(Watermark·Affix 안). ColorPicker는 요청이 없다 | 포커스는 버튼에 남는다 |
|
|
83
|
-
| 실패 | 저장 실패 Notice danger, 버튼 다시 활성. Affix 고정 중이면 버튼은 그대로 위에 붙어 있다 | Notice `danger`는 `role="alert"` |
|
|
81
|
+
| 기본 | ColorPicker는 `value` 색, 워터마크 타일, Affix는 일반 위치(`data-affixed="false"`) | — |
|
|
82
|
+
| 진행 중(제품 연결) | 저장 버튼 `loading`(Watermark·Affix 안). ColorPicker는 요청이 없다 | 포커스는 버튼에 남는다 |
|
|
83
|
+
| 실패(제품 연결) | 저장 실패 Notice danger, 버튼 다시 활성. Affix 고정 중이면 버튼은 그대로 위에 붙어 있다 | Notice `danger`는 `role="alert"` |
|
|
84
84
|
| HEX 잘못 입력 | `labels.invalid` 문구가 오류로 보이고 외부 값은 그대로 | 오류 문구는 alert |
|
|
85
85
|
| 프리셋 선택됨 | 선택한 견본 테두리가 primary 색, `aria-pressed="true"` | — |
|
|
86
86
|
| `disabled` | fieldset 전체 흐림(opacity 0.5), 입력·프리셋 막힘 | 포커스 불가 |
|
|
@@ -143,4 +143,4 @@ import { Stack, Text } from "@hjmds/react/layout";
|
|
|
143
143
|
- 세 컴포넌트 모두 root barrel에 없다. granular subpath(`@hjmds/react/color-picker` 등)로만 import한다.
|
|
144
144
|
- Affix `offset`이 음수이거나 유한하지 않으면 `TypeError`를 던진다.
|
|
145
145
|
- Watermark 범위(`tileWidth` ≥ 80, `tileHeight` ≥ 60, `rotate` -90~90, `opacity` 0~1, 줄 1~3개·각 120자 이하)를 벗어나면 `TypeError`다.
|
|
146
|
-
- 현재 스토리의 저장은
|
|
146
|
+
- 현재 스토리의 저장은 메모리 상태만 확정하며 실제 파일·서버 저장이나 진행 중·실패 경로는 없다. 성공 뒤 `aria-disabled`로 중복 실행을 막아 키보드 초점과 sticky 안의 버튼 노드를 유지한다. 제품의 저장·실패 경로는 위 코드 골격처럼 제품이 공급한다.
|
|
@@ -126,7 +126,7 @@ import { Section, Stack, Text } from "@hjmds/react/layout";
|
|
|
126
126
|
// 없음. Breadcrumb·Pagination은 Web 전용이다.
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
라우팅(스토리는 데모용 hash fragment), 레코드 125개, pageSize 5는 예시다. 경로·페이지 크기·URL 동기화는 제품 소유다.
|
|
129
|
+
라우팅(스토리는 데모용 hash fragment), 레코드 125개, pageSize 5는 예시다. 데모는 처음에 모음 1페이지를 보여 주며, 상위 경로를 거친 뒤 브라우저 뒤로 가기로 최초 URL에 돌아와도 같은 초기 모음을 복구한다. 각 인스턴스의 fragment만 처리하므로 다른 미리보기의 이동은 현재 목록을 바꾸지 않는다. 페이지 번호는 메모리 상태이며 URL 이력에 추가하지 않는다. 경로·페이지 크기·URL 동기화는 제품 소유다.
|
|
130
130
|
|
|
131
131
|
## 플랫폼 차이
|
|
132
132
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
- 단계: 화면
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용:
|
|
7
|
-
- 검토일: 2026-10-
|
|
6
|
+
- 적용: 구성 API 지침 참조
|
|
7
|
+
- 검토일: 2026-10-13
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/공통 화면/채팅`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
|
|
9
9
|
- 스토리북: `배포/화면/소통/채팅`
|
|
10
10
|
|
|
@@ -38,9 +38,21 @@ host: 남은 높이·safe area·키보드
|
|
|
38
38
|
| --- | --- | --- |
|
|
39
39
|
| 기본 | 헤더 → 메시지 타임라인 → 작성창 | 각 공개 콜백을 제품 상태에 연결 |
|
|
40
40
|
| 로딩 | 최초 조회는 본문 상태, 저장은 해당 행동 pending | 중복 제출 차단; 성공을 먼저 표시하지 않음 |
|
|
41
|
-
| 빈 |
|
|
41
|
+
| 빈 | 타임라인 중앙에 첫 대화 안내, composer 유지 | 바로 첫 메시지 작성 |
|
|
42
42
|
| 오류 | 전송 실패는 초안과 타임라인을 유지하고 notice로 안내 | 실패 원인과 재시도 경로 제공 |
|
|
43
43
|
|
|
44
|
+
|
|
45
|
+
### 첫 메시지부터 실패 복구까지
|
|
46
|
+
|
|
47
|
+
2026-10-13 전수 점검에서 최초 조회와 변경 요청의 상태를 같은 골격으로 설명하던 부분을 구체화했다.
|
|
48
|
+
|
|
49
|
+
| 상황 | 연결 | 유지·복구 |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| 첫 메시지 | empty + composer | 미게시(1.18.0 이후): 첫 입력을 허용한다. 로딩·오류·권한 제한은 composer를 숨긴다 |
|
|
52
|
+
| 전송 중 | ready 또는 empty 유지, composer.pending | 입력 snapshot과 요청 식별자를 제품이 보관한다 |
|
|
53
|
+
| 전송 실패 | 같은 초안·첨부·답장 대상, notice | 동일 메시지 재시도와 새 메시지 전송을 구분한다 |
|
|
54
|
+
| 전송 확정 | receipt의 메시지 id로 목록 반영 | 확정된 초안만 비우며 사용자가 그 뒤 쓴 내용은 지우지 않는다 |
|
|
55
|
+
|
|
44
56
|
## 사용하는 지침
|
|
45
57
|
|
|
46
58
|
| 지침 | 쓰는 곳 |
|
|
@@ -58,7 +70,7 @@ import { ChatMessage, ChatScreen, MessageComposer } from "@hjmds/react/screens";
|
|
|
58
70
|
<ChatScreen
|
|
59
71
|
title={t("chat.title")}
|
|
60
72
|
scroll="screen" // 짧은 기록. 긴 기록은 제품 가상 목록 + 기본 scroll="content"
|
|
61
|
-
state={isPending ? { kind: "loading", title: t("chat.loading") } : { kind: "ready" }}
|
|
73
|
+
state={isPending ? { kind: "loading", title: t("chat.loading") } : messages.length ? { kind: "ready" } : { kind: "empty", title: t("chat.empty") }}
|
|
62
74
|
composer={<MessageComposer value={draft} label={t("chat.input")} sendLabel={t("chat.send")}
|
|
63
75
|
pending={sending} onValueChange={setDraft} onSend={send} />}
|
|
64
76
|
>
|
|
@@ -79,7 +91,7 @@ import { Text } from "@hjmds/react-native/primitives";
|
|
|
79
91
|
|
|
80
92
|
<ChatScreen
|
|
81
93
|
title={t("chat.title")}
|
|
82
|
-
state={isPending ? { kind: "loading", title: t("chat.loading") } : { kind: "ready" }}
|
|
94
|
+
state={isPending ? { kind: "loading", title: t("chat.loading") } : messages.length ? { kind: "ready" } : { kind: "empty", title: t("chat.empty") }}
|
|
83
95
|
composer={<MessageComposer value={draft} label={t("chat.input")} sendLabel={t("chat.send")}
|
|
84
96
|
pending={sending} onValueChange={setDraft} onSend={send} />}
|
|
85
97
|
>
|
|
@@ -103,11 +115,10 @@ HJM `Timeline`(이벤트 연표)과 `VirtualList`(고정 `rowHeight`)는 높이
|
|
|
103
115
|
`deliveryLabel`은 선택 prop이라 값이 없을 때 `undefined`를 넘기지 않고 조건부 spread로 뺀다(`exactOptionalPropertyTypes`).
|
|
104
116
|
콜백·데이터·지역화 함수는 제품에서 공급한다.
|
|
105
117
|
|
|
106
|
-
##
|
|
118
|
+
## 다크·좁은 폭
|
|
107
119
|
|
|
108
120
|
| 조건 | 바뀌는 것 |
|
|
109
121
|
| --- | --- |
|
|
110
|
-
| 큰 글자 | 2배 글자에서 제목·행은 내용 높이로 증가. footer·닫기·입력 필드가 겹치지 않는지 확인 |
|
|
111
122
|
| 다크 | semantic 색으로 내용과 표면을 함께 전환; 예제 브랜드 색을 제품 기본값으로 복사하지 않음 |
|
|
112
123
|
| 좁은 폭 | 320px부터 한 열로 읽기 순서 유지. 가상화 본문은 scroll=content, 중첩 스크롤 금지 |
|
|
113
124
|
| 키보드 | Native host가 safe area와 키보드를 한 번 처리; Web은 포커스된 입력과 footer 가림 확인 |
|
|
@@ -121,7 +132,9 @@ HJM `Timeline`(이벤트 연표)과 `VirtualList`(고정 `rowHeight`)는 높이
|
|
|
121
132
|
|
|
122
133
|
## 함정
|
|
123
134
|
|
|
135
|
+
- 대화 정보에 들어갔다 돌아와도 입력과 답장 대상을 유지한다. 원문 이동은 현재 메시지 목록에서 대상에 스크롤하고, Web은 원문 host로 초점도 옮긴다. 같은 원문을 다시 누르면 강조 시간을 다시 시작하며 언마운트 시 타이머를 정리한다.
|
|
136
|
+
- 전송 실패 예약은 읽음 저장 실패 예약과 구분해 다른 화면의 행동을 실패시키지 않는다.
|
|
124
137
|
- 전송 실패는 초안과 타임라인을 유지하고 notice로 안내.
|
|
125
|
-
- Storybook은 실제 서버·OS 권한·라우터 연동 증거가 아니다.
|
|
138
|
+
- Storybook은 실제 서버·OS 권한·라우터 연동 증거가 아니다. 기본·다크와 실패/복구를 각각 확인한다.
|
|
126
139
|
- 긴 대화를 `scroll="screen"` + map으로 그리지 않는다. Native는 `FlatList`(기본 `scroll="content"`, 답장 원문 이동은 `scrollToIndex`), Web은 가변 높이 가상 목록이다. Web 예제의 `scroll="screen"`은 짧은 고정 기록이라서다.
|
|
127
140
|
- 실패 복구 스토리(`실패와 초안 복구`)는 다음 전송 실패를 예약하는 데모 도구로 연다.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- 단계: 화면
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용:
|
|
6
|
+
- 적용: 구성 API 지침 참조
|
|
7
7
|
- 검토일: 2026-10-07
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/공통 화면/댓글`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
|
|
9
9
|
- 스토리북: `배포/화면/소통/댓글`
|
|
@@ -48,6 +48,18 @@ host: 남은 높이·safe area·키보드
|
|
|
48
48
|
| 빈 | 실제 조회 0건 또는 아직 작성하지 않은 상태 안내 | 시작·조건 해제 등 맥락에 맞는 대안 |
|
|
49
49
|
| 오류 | 빈 댓글에서도 작성창 유지; 실패한 전송은 초안 유지 | 실패 원인과 재시도 경로 제공 |
|
|
50
50
|
|
|
51
|
+
|
|
52
|
+
### 댓글 목록과 입력의 독립 복구
|
|
53
|
+
|
|
54
|
+
2026-10-13 전수 점검에서 최초 조회와 변경 요청의 상태를 같은 골격으로 설명하던 부분을 구체화했다.
|
|
55
|
+
|
|
56
|
+
| 상황 | 연결 | 유지·복구 |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
| 답글 접기·펼치기 | expandedIds와 원문 데이터 분리 | 서버 순서를 보존하고 접어도 답글 초안을 지우지 않는다 |
|
|
59
|
+
| 목록 추가 조회 실패 | 기존 items + threadFooter 재시도 | 이미 읽던 댓글과 스크롤을 유지한다 |
|
|
60
|
+
| 첫 댓글 | empty + composer | 최초 로딩 실패와 실제 0건을 구분한다 |
|
|
61
|
+
| 전송 실패 | ready + notice | 초안·답글 대상을 유지한다. 항목별 좋아요 실패는 해당 항목만 복구한다 |
|
|
62
|
+
|
|
51
63
|
## 사용하는 지침
|
|
52
64
|
|
|
53
65
|
| 지침 | 쓰는 곳 |
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
- 단계: 화면
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용:
|
|
7
|
-
- 검토일: 2026-10-
|
|
6
|
+
- 적용: 구성 API 지침 참조
|
|
7
|
+
- 검토일: 2026-10-13
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/공통 화면/알림함`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
|
|
9
9
|
- 스토리북: `배포/화면/소통/알림함`
|
|
10
10
|
|
|
@@ -41,6 +41,18 @@ host: 남은 높이·safe area·키보드
|
|
|
41
41
|
| 빈 | 실제 조회 0건 또는 아직 작성하지 않은 상태 안내 | 시작·조건 해제 등 맥락에 맞는 대안 |
|
|
42
42
|
| 오류 | 읽음 저장 실패와 초기 조회 실패를 분리 | 실패 원인과 재시도 경로 제공 |
|
|
43
43
|
|
|
44
|
+
|
|
45
|
+
### 조회와 읽음 저장 분리
|
|
46
|
+
|
|
47
|
+
2026-10-13 전수 점검에서 최초 조회와 변경 요청의 상태를 같은 골격으로 설명하던 부분을 구체화했다.
|
|
48
|
+
|
|
49
|
+
| 상황 | 연결 | 유지·복구 |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| 전체 0건 | empty | 필터는 유지하고 불필요한 다시 시도 버튼을 만들지 않는다 |
|
|
52
|
+
| 안 읽음 0건 | empty + 전체 보기 stateAction | 전체 알림 삭제나 데이터 없음으로 오인하지 않는다 |
|
|
53
|
+
| 모두 읽음 진행 | actions의 버튼 pending | 확정 전 읽음 상태를 바꾸면 실패 시 요청에 포함된 항목만 되돌린다 |
|
|
54
|
+
| 알림 이동 | 제품 라우터에서 목적지 확인 | 읽음 저장 실패는 notice로 알리고 행 이동의 실패와 분리한다 |
|
|
55
|
+
|
|
44
56
|
## 사용하는 지침
|
|
45
57
|
|
|
46
58
|
| 지침 | 쓰는 곳 |
|
|
@@ -87,11 +99,10 @@ import { NotificationInboxScreen, NotificationItem } from "@hjmds/react-native/s
|
|
|
87
99
|
|
|
88
100
|
콜백·데이터·지역화 함수는 제품에서 공급한다. 필수 props와 플랫폼별 차이는 위 API 지침에서 확인한다.
|
|
89
101
|
|
|
90
|
-
##
|
|
102
|
+
## 다크·좁은 폭
|
|
91
103
|
|
|
92
104
|
| 조건 | 바뀌는 것 |
|
|
93
105
|
| --- | --- |
|
|
94
|
-
| 큰 글자 | 2배 글자에서 제목·행은 내용 높이로 증가. footer·닫기·입력 필드가 겹치지 않는지 확인 |
|
|
95
106
|
| 다크 | semantic 색으로 내용과 표면을 함께 전환; 예제 브랜드 색을 제품 기본값으로 복사하지 않음 |
|
|
96
107
|
| 좁은 폭 | 320px부터 한 열로 읽기 순서 유지. 가상화 본문은 scroll=content, 중첩 스크롤 금지 |
|
|
97
108
|
| 키보드 | Native host가 safe area와 키보드를 한 번 처리; Web은 포커스된 입력과 footer 가림 확인 |
|
|
@@ -105,6 +116,8 @@ import { NotificationInboxScreen, NotificationItem } from "@hjmds/react-native/s
|
|
|
105
116
|
|
|
106
117
|
## 함정
|
|
107
118
|
|
|
108
|
-
- 읽음
|
|
119
|
+
- 예제의 읽지 않음 필터가 0건이면 빈 상태를 표시하고 빈 날짜 그룹은 숨긴다. Web 상세 복귀는 남아 있는 원래 알림, 읽음 처리로 행이 사라졌으면 선택한 필터로 초점을 돌린다.
|
|
120
|
+
- 예제에 제공된 대화는 서연 대화뿐이므로 서연 알림 상세에서만 대화 이어가기를 제공한다. 다른 사람·공지 상세를 같은 대화로 연결하지 않는다. 제품은 실제 알림의 대상 id로 라우팅한다.
|
|
121
|
+
- 읽음 저장 실패와 초기 조회 실패를 분리한다. 읽음 오류와 실패 예약은 알림 행동에만 연결하고, 대화로 이동해도 메시지 전송 오류로 표시하지 않는다.
|
|
109
122
|
- 읽음 저장 실패는 본문을 교체하지 않고 `notice`에 한 줄로 알리며 알림은 읽지 않음으로 남는다(스토리 `읽음 저장 실패와 복구`). 묶음 제목(오늘·이번 주)은 Heading `level5`다.
|
|
110
|
-
- Storybook은 실제 서버·OS 권한·라우터 연동 증거가 아니다.
|
|
123
|
+
- Storybook은 실제 서버·OS 권한·라우터 연동 증거가 아니다. 기본·다크와 실패/복구를 각각 확인한다.
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
- 단계: 화면
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용:
|
|
7
|
-
- 검토일: 2026-10-
|
|
6
|
+
- 적용: 구성 API 지침 참조
|
|
7
|
+
- 검토일: 2026-10-13
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/공통 화면/로그인`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
|
|
9
9
|
- 스토리북: `배포/화면/계정/로그인`
|
|
10
10
|
|
|
@@ -23,7 +23,7 @@ host: 남은 높이·safe area·키보드
|
|
|
23
23
|
| --- | --- | --- | --- |
|
|
24
24
|
| 바깥 틀 | AuthScreenLayout | route 본문 | [API 배치 규칙](../components/auth-screen-layout.md#배치), host 남은 높이 |
|
|
25
25
|
| 내용 | 공개 슬롯 | 히어로 → 제공자 카드 → 하단 동의 | 화면 recipe의 sectionGap·itemGap; 슬롯 안은 각 지침 토큰 |
|
|
26
|
-
| 상태 |
|
|
26
|
+
| 상태 | pendingLabel 또는 main 안 Notice | 제공자 카드 내부 | 카드 크기를 유지하고 동의 고지를 남긴다 |
|
|
27
27
|
|
|
28
28
|
## 버튼과 행동 위치
|
|
29
29
|
|
|
@@ -41,6 +41,18 @@ host: 남은 높이·safe area·키보드
|
|
|
41
41
|
| 빈 | — (로그인 화면에는 빈 상태가 없다) | — |
|
|
42
42
|
| 오류 | `pendingLabel`을 지워 버튼을 복원하고 카드 안 버튼 위에 Notice `danger` 한 줄(기술 오류 원문 금지) | 같은 제공자 버튼으로 다시 시도 |
|
|
43
43
|
|
|
44
|
+
|
|
45
|
+
### 제공자 목록과 로그인 요청 분리
|
|
46
|
+
|
|
47
|
+
2026-10-13 전수 점검에서 최초 조회와 변경 요청의 상태를 같은 골격으로 설명하던 부분을 구체화했다.
|
|
48
|
+
|
|
49
|
+
| 상황 | 연결 | 유지·복구 |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| 목록 조회 중 | 제품이 main 영역 상태 제공 | AuthScreenLayout에는 ScreenLayout.state prop이 없다 |
|
|
52
|
+
| 사용자 취소 | pendingLabel 제거 | 실패로 알리지 않고 제공자 버튼을 복원한다 |
|
|
53
|
+
| 제공자 실패 | main 안 Notice + 같은 버튼 | 기술 오류 원문 대신 지역화된 안내; 동의 링크 유지 |
|
|
54
|
+
| 버튼 목록 변경 | 동일 mainCard 안에서 갱신 | 진행 중 버튼을 제거해 카드 높이를 줄이지 않는다 |
|
|
55
|
+
|
|
44
56
|
## 사용하는 지침
|
|
45
57
|
|
|
46
58
|
| 지침 | 쓰는 곳 |
|
|
@@ -82,17 +94,17 @@ import { AuthScreenLayout } from "@hjmds/react-native/auth-screen";
|
|
|
82
94
|
|
|
83
95
|
콜백·데이터·지역화 함수는 제품에서 공급한다. 필수 props와 플랫폼별 차이는 위 API 지침에서 확인한다.
|
|
84
96
|
|
|
85
|
-
##
|
|
97
|
+
## 다크·좁은 폭
|
|
86
98
|
|
|
87
99
|
| 조건 | 바뀌는 것 |
|
|
88
100
|
| --- | --- |
|
|
89
|
-
| 큰 글자 | 2배 글자에서 제목·행은 내용 높이로 증가. footer·닫기·입력 필드가 겹치지 않는지 확인 |
|
|
90
101
|
| 다크 | semantic 색으로 내용과 표면을 함께 전환; 예제 브랜드 색을 제품 기본값으로 복사하지 않음 |
|
|
91
102
|
| 좁은 폭 | 320px부터 한 열로 읽기 순서 유지. 가상화 본문은 scroll=content, 중첩 스크롤 금지 |
|
|
92
103
|
| 키보드 | Native host가 safe area와 키보드를 한 번 처리; Web은 포커스된 입력과 footer 가림 확인 |
|
|
93
104
|
|
|
94
105
|
## 함정
|
|
95
106
|
|
|
107
|
+
- 예제는 로그인 시도마다 작업 식별자를 두고 제공자 중복 실행을 차단한다. 취소·언마운트 후 늦게 도착한 타이머는 홈이나 오류를 열지 않는다. 취소 뒤 새 시도는 별개의 작업이다. 성공 뒤 홈에서 대화·알림으로 이동할 때 로그인 예제의 초기 오류·진행 상태를 넘기지 않는다. 실제 OAuth 취소를 검증한 것은 아니다.
|
|
96
108
|
- 실패하면 버튼을 복원; 카드 크기 유지와 중복 요청 차단.
|
|
97
|
-
- 스토리는 기본·어두운
|
|
98
|
-
- Storybook은 실제 서버·OS 권한·라우터 연동 증거가 아니다.
|
|
109
|
+
- 스토리는 기본·어두운 테마, `로그인 중`, `로그인 실패와 다시 시도`(첫 시도 실패 → 다시 누르면 성공)다. 빈 화면·로그인 필요 같은 본문 상태는 이 화면에 해당하지 않아 2026-10-06 스토리에서 뺐다.
|
|
110
|
+
- Storybook은 실제 서버·OS 권한·라우터 연동 증거가 아니다. 기본·다크와 실패/복구를 각각 확인한다.
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
- 단계: 화면
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용:
|
|
7
|
-
- 검토일: 2026-10-
|
|
6
|
+
- 적용: 구성 API 지침 참조
|
|
7
|
+
- 검토일: 2026-10-13
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md), Web·Native `src/screen-flows.tsx`(`ProfileScreen`·`EditorScreen`), 예제 `showcase/*/screen-flow-previews.tsx`(`AccountFlowPreview`)·`saved-profile-previews.tsx`(`ProfileEditFields`), 기본 얼굴 `showcase/web/src/patterns/ProfileStudio.previews.tsx`·`showcase/native/src/profile-face-preview.tsx`·`showcase/shared/profile-studio.ts`. 2026-10-06 사용자 승인으로 실험 `공통 화면/프로필`을 배포하면서 같은 프로필 수정을 직접 조립으로 보이던 배포 `화면/프로필 편집`과 합쳤다(API 기반 우선, Web id `patterns-profile-studio` 보존, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
|
|
9
9
|
- 스토리북: `배포/화면/계정/프로필`
|
|
10
10
|
|
|
@@ -101,6 +101,18 @@
|
|
|
101
101
|
| 실패와 복구 | `실패와 복구`: 다음 저장 실패를 예약한 뒤 저장 → 실패 문구, 초안 유지 → 다시 저장 | 같은 저장 행동 |
|
|
102
102
|
| 로그아웃 확인 | AlertDialog: 제목·설명·[취소][로그아웃] | 확인 뒤 제품이 세션을 끝낸다 |
|
|
103
103
|
|
|
104
|
+
|
|
105
|
+
### 프로필과 게시물 상태 분리
|
|
106
|
+
|
|
107
|
+
2026-10-13 전수 점검에서 최초 조회와 변경 요청의 상태를 같은 골격으로 설명하던 부분을 구체화했다.
|
|
108
|
+
|
|
109
|
+
| 상황 | 연결 | 유지·복구 |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| 프로필 조회 전 | 지역화된 기본 title + loading | user가 없을 때 handle·summary를 평가하지 않는다 |
|
|
112
|
+
| 게시물 0개 | 프로필 ready + 게시물 영역만 empty | 프로필과 수정 행동은 계속 보인다 |
|
|
113
|
+
| 프로필 저장 실패 | EditorScreen ready + notice | 보기용 원본과 편집용 초안을 모두 보존한다 |
|
|
114
|
+
| 계정 변경·로그아웃 | 제품 세션과 데이터 초기화 | 이전 계정의 늦은 응답이나 draft를 다음 계정에 재사용하지 않는다 |
|
|
115
|
+
|
|
104
116
|
## 사용하는 지침
|
|
105
117
|
|
|
106
118
|
| 지침 | 쓰는 곳 |
|
|
@@ -145,9 +157,9 @@ editing ? <EditorScreen title={t("profile.edit")} submitPlacement="footer" dirty
|
|
|
145
157
|
{...(!draft.name.trim() ? { error: t("profile.nameRequired") } : {})} />
|
|
146
158
|
</Stack>
|
|
147
159
|
</EditorScreen> : <ProfileScreen
|
|
148
|
-
title={user.
|
|
160
|
+
title={user?.handle ?? t("profile.title")}
|
|
149
161
|
actions={<IconButton label={t("profile.accountMenu")} tone="ghost" onClick={openAccountMenu}>{menuIcon}</IconButton>}
|
|
150
|
-
summary={<ProfileSummary user={user} />}
|
|
162
|
+
summary={user ? <ProfileSummary user={user} /> : null}
|
|
151
163
|
edit={{ label: t("profile.edit"), onAction: openEditor }}
|
|
152
164
|
state={user ? { kind: "ready" } : { kind: "loading", title: t("profile.loading") }}
|
|
153
165
|
>
|
|
@@ -181,9 +193,9 @@ editing ? <EditorScreen title={t("profile.edit")} submitPlacement="footer" dirty
|
|
|
181
193
|
{...(!draft.name.trim() ? { error: t("profile.nameRequired") } : {})} />
|
|
182
194
|
</Stack>
|
|
183
195
|
</EditorScreen> : <ProfileScreen
|
|
184
|
-
title={user.
|
|
196
|
+
title={user?.handle ?? t("profile.title")}
|
|
185
197
|
actions={<IconButton label={t("profile.accountMenu")} tone="ghost" onPress={openAccountMenu}>{menuIcon}</IconButton>}
|
|
186
|
-
summary={<ProfileSummary user={user} />}
|
|
198
|
+
summary={user ? <ProfileSummary user={user} /> : null}
|
|
187
199
|
edit={{ label: t("profile.edit"), onAction: openEditor }}
|
|
188
200
|
state={user ? { kind: "ready" } : { kind: "loading", title: t("profile.loading") }}
|
|
189
201
|
>
|
|
@@ -194,11 +206,10 @@ editing ? <EditorScreen title={t("profile.edit")} submitPlacement="footer" dirty
|
|
|
194
206
|
`ProfileSummary`·`MyPostsSection`·`faces`·`openAccountMenu`(계정 Sheet와 로그아웃 AlertDialog)는 제품이 공급한다.
|
|
195
207
|
`save`가 실패하면 `editing`을 유지하고 초안을 지우지 않는다.
|
|
196
208
|
|
|
197
|
-
##
|
|
209
|
+
## 다크·좁은 폭
|
|
198
210
|
|
|
199
211
|
| 조건 | 바뀌는 것 |
|
|
200
212
|
| --- | --- |
|
|
201
|
-
| 큰 글자 | 요약의 수치 줄과 얼굴 줄이 감긴다. 수정 화면 footer는 고정이라 필드만 스크롤된다. 제목 열이 모자라면 계정 메뉴가 다음 줄로 내려간다 |
|
|
202
213
|
| 다크 | semantic 색으로 내용과 표면을 함께 전환한다. Blobatar 얼굴은 seed가 정하고 테마와 무관하다 |
|
|
203
214
|
| 좁은 폭 | 320부터 한 열, 게시물 격자는 3열 그대로 칸이 작아진다 |
|
|
204
215
|
| 키보드 | 수정 화면에서 Native host가 safe area와 키보드를 한 번 처리한다. Web은 포커스된 필드가 footer에 가리지 않는지 확인 |
|
|
@@ -218,4 +229,6 @@ editing ? <EditorScreen title={t("profile.edit")} submitPlacement="footer" dirty
|
|
|
218
229
|
- ProfileScreen에는 footer가 없다. 로그아웃·탈퇴를 footer에 두려 하지 말고 계정 메뉴(Sheet) 또는 본문 끝 `accountActions`에 둔다.
|
|
219
230
|
- 현재 `기본 얼굴 고르기` 스토리는 ProfileScreen·EditorScreen이 아닌 옛 직접 조립 화면이다. `hjm-profile-studio*` 클래스는 공개 스타일이 아니므로 제품에서 가져다 쓰지 않는다. 제품은 위 코드처럼 수정 화면 안에 얼굴 줄을 둔다.
|
|
220
231
|
- `createBlobatarFallback`은 seed가 바뀔 때만 다시 만들도록 `useMemo`로 묶는다.
|
|
221
|
-
- Storybook은 실제 서버·세션·라우터 연동 증거가 아니다. 기본·어두운
|
|
232
|
+
- Storybook은 실제 서버·세션·라우터 연동 증거가 아니다. 기본·어두운 테마와 실패와 복구를 각각 확인한다.
|
|
233
|
+
|
|
234
|
+
2026-10-13 실제 계정 예제 검수: 실패한 프로필 초안은 유지하고 성공 뒤에만 요약을 갱신한다. 계정 Sheet와 로그아웃 AlertDialog의 returnFocusRef는 계정 메뉴 행동을 가리킨다. 로그아웃을 확정하면 열림 상태도 함께 정리하므로 예제를 다시 열 때 확인창이 되살아나지 않는다. 이는 로컬 예제 상태이며 실제 세션 종료·Native OS 초점/발표 검증과 구분한다.
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
- 단계: 화면
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용:
|
|
7
|
-
- 검토일: 2026-10-
|
|
6
|
+
- 적용: 구성 API 지침 참조
|
|
7
|
+
- 검토일: 2026-10-13
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/공통 화면/저장한 항목`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
|
|
9
9
|
- 스토리북: `배포/화면/콘텐츠/저장한 항목`
|
|
10
10
|
|
|
@@ -45,6 +45,18 @@ host: 남은 높이·safe area·키보드
|
|
|
45
45
|
| 빈 | 실제 조회 0건 또는 아직 작성하지 않은 상태 안내 | 시작·조건 해제 등 맥락에 맞는 대안 |
|
|
46
46
|
| 오류 | 목록 비어 있음과 조회 실패를 구분하고 재시도 제공 | 실패 원인과 재시도 경로 제공 |
|
|
47
47
|
|
|
48
|
+
|
|
49
|
+
### 저장 해제와 목록 복귀
|
|
50
|
+
|
|
51
|
+
2026-10-13 전수 점검에서 최초 조회와 변경 요청의 상태를 같은 골격으로 설명하던 부분을 구체화했다.
|
|
52
|
+
|
|
53
|
+
| 상황 | 연결 | 유지·복구 |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| collectionId 없음 | 컬렉션 개요 | null의 전체 게시물 보기와 구분한다 |
|
|
56
|
+
| 컬렉션 0개·필터 0건 | 원인별 empty | 컬렉션 만들기와 조건 해제를 다른 행동으로 연결한다 |
|
|
57
|
+
| 상세 저장 해제 | 제품이 선택 id를 정리 | 상세를 닫고 같은 컬렉션·검색어로 복귀한다 |
|
|
58
|
+
| 실행 취소 | 서버 복구 성공 후 항목 복원 | 실패하면 성공 문구를 내지 않고 같은 범위 재시도를 제공한다 |
|
|
59
|
+
|
|
48
60
|
## 사용하는 지침
|
|
49
61
|
|
|
50
62
|
| 지침 | 쓰는 곳 |
|
|
@@ -125,3 +137,5 @@ import { SavedItemsScreen } from "@hjmds/react-native/saved-items";
|
|
|
125
137
|
- Storybook은 실제 서버·OS 권한·라우터 연동 증거가 아니다. 기본·다크·큰 글자와 실패/복구를 각각 확인한다.
|
|
126
138
|
|
|
127
139
|
- 새 컬렉션은 Sheet footer에 만들기 하나, 본문은 스크롤한다. Native는 `scrollable keyboardAvoidance`를 사용하고 사진 선택 Button은 `growWithContent`로 이미지 높이를 보존한다. 고정 44 높이에 사진을 넣으면 행이 겹친다(2026-10-06 시뮬레이터 재현).
|
|
140
|
+
|
|
141
|
+
미게시(1.18.0 이후): 선택한 컬렉션 또는 전체 게시물이 0개면 SavedItemsScreen이 목록 pane의 empty 상태를 사용해 남은 본문 중앙에 안내한다. 제품의 loading/error/restricted 상태는 우선하며, 컬렉션 개요의 만들기 행동은 유지한다.
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
- 단계: 화면
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용:
|
|
7
|
-
- 검토일: 2026-10-
|
|
6
|
+
- 적용: 구성 API 지침 참조
|
|
7
|
+
- 검토일: 2026-10-13
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md), Web·Native `src/screen-flows.tsx`(`SearchScreen` 두 단계 검색 prop, 2026-10-06 사용자 위임 결정으로 미리보기 로직을 공개 API로 올림), 예제 `showcase/*/search-discovery-preview.tsx`·`showcase/shared/search-discovery.ts`. 2026-10-06 사용자 요청("실험에 있는 검색화면 UI도 개편해. 검색과 필터 있는 앱 서비스 따라해.")에 따라 Material 3 Search·Apple HIG search fields·Baymard·NN/g·eBay Playbook 필터 패턴과 국내 커머스·생활 서비스 모바일 웹의 **구조·상호작용만** 조사해 다시 설계했다(브랜드 색·로고·문구·자산은 가져오지 않았다). 2026-10-06 사용자 승인으로 실험 `공통 화면/검색`을 배포하면서 같은 일을 직접 조립하던 배포 `화면/검색`(내 기록 검색)을 대체했다(Web id `patterns-search` 보존, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
|
|
9
9
|
- 스토리북: `배포/화면/검색/검색 결과와 필터`
|
|
10
10
|
|
|
@@ -111,6 +111,18 @@ Native · Web < 960: 아래 시트(size large) Web ≥ 960(expanded): 옆
|
|
|
111
111
|
| 실패와 복구 | `실패와 복구` 스토리: 다음 검색 실패를 예약한 뒤 검색하면 오류 행이 되고 `다시 시도`로 같은 검색어·조건을 다시 조회 | 검색어·조건·정렬은 그대로 |
|
|
112
112
|
| 로그인 필요 | `state={{ kind: "restricted" }}`로 본문 교체 | `stateAction` 로그인 |
|
|
113
113
|
|
|
114
|
+
|
|
115
|
+
### 제안·확정 결과·필터 개수의 요청 경계
|
|
116
|
+
|
|
117
|
+
2026-10-13 전수 점검에서 최초 조회와 변경 요청의 상태를 같은 골격으로 설명하던 부분을 구체화했다.
|
|
118
|
+
|
|
119
|
+
| 상황 | 연결 | 유지·복구 |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| 제안 조회 | onSearch(value, signal) | 중단된 응답은 무시하고 최근 검색에는 쓰지 않는다 |
|
|
122
|
+
| 검색 확정 | onSubmit + committedQuery | 검색어·필터·정렬을 요청 키로 묶고 최신 결과만 반영한다 |
|
|
123
|
+
| 재조회 실패 | 기존 결과 + resultSummary.notice | count를 0으로 바꿔 실제 0건처럼 보이지 않게 한다 |
|
|
124
|
+
| 필터 초안 개수 조회 | count(draft)=null | 아직 모르는 개수를 0으로 표시하지 않는다. 닫기는 적용값을 바꾸지 않는다 |
|
|
125
|
+
|
|
114
126
|
## 사용하는 지침
|
|
115
127
|
|
|
116
128
|
| 지침 | 쓰는 곳 |
|
|
@@ -247,8 +259,8 @@ import { SearchScreen } from "@hjmds/react-native/screen-flows";
|
|
|
247
259
|
| 다크 | semantic token만 쓰므로 따로 처리하지 않는다. 일치 강조는 색이 아니라 굵기라 다크·색각 차이에서도 남는다. 예제 색을 제품 기본값으로 복사하지 않는다 |
|
|
248
260
|
| 좁은 폭 | 320에서 칩 줄은 화면 끝까지 스크롤, 적용 조건 칩은 줄바꿈, 시트는 아래 전폭 |
|
|
249
261
|
| 넓은 폭(Web ≥ 960) | 한 열(최대 720) 유지, 필터 시트는 `placement="end"` 옆 시트로 결과를 가리지 않는다. 상시 왼쪽 필터 패널은 ScreenLayout 폭 계약 밖이라 두지 않는다 |
|
|
250
|
-
| 키보드 | Native 본문 ScrollView가 `keyboardShouldPersistTaps="handled"`라 키보드가 떠 있어도 제안 행이 한 번에 눌린다. 검색 키뿐 아니라 제안·최근·추천을 골라 확정해도 키보드가 닫힌다(Native `Keyboard.dismiss()`, Web은 결과 영역으로 포커스,
|
|
251
|
-
| 큰 글자 · 칩 줄 안 pills | 칩 줄에 SegmentedControl `presentation="pills"`를 넣어도 세로로 쌓이지 않고 한 줄 가로 스크롤로 남는다(
|
|
262
|
+
| 키보드 | Native 본문 ScrollView가 `keyboardShouldPersistTaps="handled"`라 키보드가 떠 있어도 제안 행이 한 번에 눌린다. 검색 키뿐 아니라 제안·최근·추천을 골라 확정해도 키보드가 닫힌다(Native `Keyboard.dismiss()`, Web은 결과 영역으로 포커스, 1.13.1 반영) |
|
|
263
|
+
| 큰 글자 · 칩 줄 안 pills | 칩 줄에 SegmentedControl `presentation="pills"`를 넣어도 세로로 쌓이지 않고 한 줄 가로 스크롤로 남는다(1.13.1 반영. 1.13.0 이하는 약 440pt 기둥이 됐다). 칩 높이는 라벨에 맞춰 늘어난다(Native Chip `minHeight`) |
|
|
252
264
|
|
|
253
265
|
## 플랫폼 차이
|
|
254
266
|
|
|
@@ -277,3 +289,11 @@ import { SearchScreen } from "@hjmds/react-native/screen-flows";
|
|
|
277
289
|
- Storybook은 실제 검색 서버·라우터 연동 증거가 아니다. Native 화면은 2026-10-06 개편에서 시뮬레이터로 확인하지 않았다(구조는 렌더 테스트로만 확인).
|
|
278
290
|
- 시트 안 검색(채팅 도구 고르기 등)은 Sheet `size`를 `medium`·`large`로 주고 `scrollable` 없이 SearchScreen을 넣는다. SearchScreen은 `contentInset="none"`·`hostGutter="regular"`.
|
|
279
291
|
1.13.0 이하 Native는 이 조합에서 화면이 0pt가 되고 칩 줄이 시트 여백에서 잘렸다(2026-10-06 utilverse, [Sheet 함정](../components/sheet.md#함정)).
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
### 하위 목록 로딩 계약
|
|
295
|
+
|
|
296
|
+
결과 개수가 아직 없는 `resultSummary.count === null`은 양 renderer에서 `ListRow loading`을
|
|
297
|
+
사용한다. 위쪽 개수 Skeleton과 실제 결과 행은 화면이 배치하지만, 행의 density·두 줄 높이·슬롯은
|
|
298
|
+
ListRow가 소유한다. 상태 이름은 첫 로딩 행 한 곳에만 제공한다. 검색 화면 안에서 임의 폭/간격의
|
|
299
|
+
Skeleton 두 개로 결과 행을 다시 만들지 않는다.
|