@hjmds/design-contracts 1.26.1 → 1.27.1
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/agreement.d.ts +1 -0
- package/dist/agreement.d.ts.map +1 -1
- package/dist/agreement.js +2 -1
- package/dist/agreement.js.map +1 -1
- package/dist/auth-screen.d.ts +1 -1
- package/dist/behaviors.d.ts +1 -1
- package/dist/calendar.d.ts +1 -0
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js +4 -1
- package/dist/calendar.js.map +1 -1
- package/dist/catalog.d.ts +78 -30
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +10 -5
- package/dist/catalog.js.map +1 -1
- package/dist/chart.d.ts +2 -0
- package/dist/chart.d.ts.map +1 -1
- package/dist/chart.js +1 -1
- package/dist/chart.js.map +1 -1
- package/dist/component-definitions.d.ts +1 -0
- package/dist/component-definitions.d.ts.map +1 -1
- package/dist/component-definitions.js +1 -0
- package/dist/component-definitions.js.map +1 -1
- package/dist/component-recipes.d.ts +53 -6
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +64 -10
- package/dist/component-recipes.js.map +1 -1
- package/dist/data-table.d.ts +11 -0
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +5 -0
- package/dist/data-table.js.map +1 -1
- package/dist/description-list.d.ts +3 -0
- package/dist/description-list.d.ts.map +1 -1
- package/dist/description-list.js +5 -1
- package/dist/description-list.js.map +1 -1
- package/dist/design-profile-layout.d.ts +28 -0
- package/dist/design-profile-layout.d.ts.map +1 -1
- package/dist/design-profile-layout.js +27 -3
- package/dist/design-profile-layout.js.map +1 -1
- package/dist/design-profile.d.ts.map +1 -1
- package/dist/design-profile.js +40 -8
- package/dist/design-profile.js.map +1 -1
- package/dist/field-group.d.ts +13 -0
- package/dist/field-group.d.ts.map +1 -1
- package/dist/field-group.js +17 -2
- package/dist/field-group.js.map +1 -1
- package/dist/foundations.d.ts +24 -11
- package/dist/foundations.d.ts.map +1 -1
- package/dist/foundations.js +40 -7
- package/dist/foundations.js.map +1 -1
- package/dist/heading.d.ts +5 -5
- package/dist/image-choice.d.ts +23 -0
- package/dist/image-choice.d.ts.map +1 -0
- package/dist/image-choice.js +26 -0
- package/dist/image-choice.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/masonry.d.ts +8 -0
- package/dist/masonry.d.ts.map +1 -1
- package/dist/masonry.js +12 -1
- package/dist/masonry.js.map +1 -1
- package/dist/number-field.d.ts +2 -2
- package/dist/number-field.d.ts.map +1 -1
- package/dist/number-field.js +4 -2
- package/dist/number-field.js.map +1 -1
- package/dist/qr-code-recipe.d.ts +1 -0
- package/dist/qr-code-recipe.d.ts.map +1 -1
- package/dist/qr-code-recipe.js +3 -1
- package/dist/qr-code-recipe.js.map +1 -1
- package/dist/recipes.d.ts +1 -1
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +1 -1
- package/dist/recipes.js.map +1 -1
- package/dist/result.d.ts +9 -0
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +5 -0
- package/dist/result.js.map +1 -1
- package/dist/screen-patterns.d.ts +139 -2
- package/dist/screen-patterns.d.ts.map +1 -1
- package/dist/screen-patterns.js +140 -1
- package/dist/screen-patterns.js.map +1 -1
- package/dist/sheet.d.ts +15 -1
- package/dist/sheet.d.ts.map +1 -1
- package/dist/sheet.js +20 -1
- package/dist/sheet.js.map +1 -1
- package/dist/showcase.d.ts +27 -27
- package/dist/showcase.js +27 -27
- package/dist/showcase.js.map +1 -1
- package/dist/tag.d.ts +10 -2
- package/dist/tag.d.ts.map +1 -1
- package/dist/tag.js +3 -2
- package/dist/tag.js.map +1 -1
- package/dist/tags-input.d.ts +6 -0
- package/dist/tags-input.d.ts.map +1 -1
- package/dist/tags-input.js +3 -1
- package/dist/tags-input.js.map +1 -1
- package/dist/text-formats.d.ts +2 -2
- package/dist/top.d.ts +2 -2
- package/dist/transfer-list.d.ts +1 -1
- package/dist/transfer-list.d.ts.map +1 -1
- package/dist/transfer-list.js +2 -1
- package/dist/transfer-list.js.map +1 -1
- package/dist/upload-item.d.ts +1 -10
- package/dist/upload-item.d.ts.map +1 -1
- package/dist/upload-item.js +1 -2
- package/dist/upload-item.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/activity-chip.md +4 -3
- package/docs/catalog-freeze.json +5 -2
- package/docs/chart.md +1 -1
- package/docs/consumer-policy.md +13 -6
- package/docs/description-list.md +4 -0
- package/docs/design-profile.md +28 -2
- package/docs/font-roles.md +1 -1
- package/docs/generated/component-maturity.md +5 -4
- package/docs/generated/renderer-evidence.json +92 -6
- package/docs/generated/renderer-evidence.md +5 -3
- package/docs/generated/showcase-manifest.json +49 -1
- package/docs/gravity-letters.md +7 -2
- package/docs/masonry.md +4 -2
- package/docs/screen-patterns.md +5 -2
- package/docs/showcase.md +7 -9
- package/docs/stable-promotion.md +13 -1
- package/docs/usage/README.md +40 -10
- package/docs/usage/SELECTION.md +30 -4
- package/docs/usage/STANDARD.md +11 -2
- package/docs/usage/components/account-switcher.md +98 -0
- package/docs/usage/components/activity-chip.md +4 -4
- package/docs/usage/components/badge.md +1 -1
- package/docs/usage/components/bezier-editor.md +99 -0
- package/docs/usage/components/bottom-cta.md +2 -1
- package/docs/usage/components/button.md +26 -12
- package/docs/usage/components/carousel.md +4 -4
- package/docs/usage/components/chart.md +1 -0
- package/docs/usage/components/chat-message.md +18 -12
- package/docs/usage/components/chat-screen.md +6 -3
- package/docs/usage/components/cohort-retention-table.md +83 -0
- package/docs/usage/components/collapsible.md +7 -2
- package/docs/usage/components/comment-thread-row.md +102 -0
- package/docs/usage/components/comment-thread-screen.md +27 -13
- package/docs/usage/components/composition-layout.md +1 -0
- package/docs/usage/components/context-menu.md +32 -15
- package/docs/usage/components/conversation-row.md +11 -4
- package/docs/usage/components/conversion-funnel.md +85 -0
- package/docs/usage/components/correlation-matrix.md +80 -0
- package/docs/usage/components/data-table.md +4 -0
- package/docs/usage/components/description-list.md +2 -1
- package/docs/usage/components/editor-screen.md +9 -5
- package/docs/usage/components/effect-surface.md +4 -2
- package/docs/usage/components/empty-state.md +2 -2
- package/docs/usage/components/event-attendance-card.md +104 -0
- package/docs/usage/components/expandable-search.md +105 -0
- package/docs/usage/components/file-picker.md +5 -0
- package/docs/usage/components/filter-recovery.md +97 -0
- package/docs/usage/components/follow-profile-card.md +100 -0
- package/docs/usage/components/heading.md +6 -2
- package/docs/usage/components/histogram-explorer.md +89 -0
- package/docs/usage/components/icon-button.md +18 -2
- package/docs/usage/components/icon-tile.md +12 -4
- package/docs/usage/components/image-choice-group.md +81 -0
- package/docs/usage/components/import-field-mapping.md +94 -0
- package/docs/usage/components/inline-edit.md +105 -0
- package/docs/usage/components/keyboard-avoidance-context.md +60 -0
- package/docs/usage/components/keyboard-avoiding.md +5 -1
- package/docs/usage/components/list-detail-screen.md +4 -4
- package/docs/usage/components/list.md +1 -1
- package/docs/usage/components/masonry.md +6 -7
- package/docs/usage/components/media-selection-screen.md +7 -5
- package/docs/usage/components/media-transport.md +104 -0
- package/docs/usage/components/message-composer.md +13 -3
- package/docs/usage/components/moderation-screen.md +5 -2
- package/docs/usage/components/notification-inbox-screen.md +3 -3
- package/docs/usage/components/notification-row.md +64 -8
- package/docs/usage/components/onboarding-screen.md +1 -1
- package/docs/usage/components/overview-screen.md +9 -3
- package/docs/usage/components/pagination.md +18 -3
- package/docs/usage/components/password-rules-field.md +90 -0
- 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 +6 -3
- package/docs/usage/components/pricing-choice.md +85 -0
- package/docs/usage/components/profile-summary.md +56 -8
- package/docs/usage/components/progress.md +3 -3
- package/docs/usage/components/qr-code.md +5 -9
- package/docs/usage/components/repeatable-field-group.md +92 -0
- package/docs/usage/components/reply-composer.md +83 -0
- package/docs/usage/components/reply-message.md +85 -0
- package/docs/usage/components/result.md +3 -3
- package/docs/usage/components/saved-items-screen.md +7 -5
- package/docs/usage/components/screen-layout.md +21 -10
- package/docs/usage/components/search-field.md +1 -1
- package/docs/usage/components/section.md +2 -2
- package/docs/usage/components/selection-action-bar.md +89 -0
- package/docs/usage/components/settings-screen.md +13 -11
- package/docs/usage/components/sheet.md +19 -7
- package/docs/usage/components/shortcut-recorder.md +77 -0
- package/docs/usage/components/split-action.md +94 -0
- package/docs/usage/components/streaming-answer.md +94 -0
- package/docs/usage/components/switch.md +6 -2
- package/docs/usage/components/table-column-settings.md +88 -0
- package/docs/usage/components/target-summary.md +79 -0
- package/docs/usage/components/text.md +11 -5
- package/docs/usage/components/toggle-feedback-button.md +94 -0
- package/docs/usage/components/top-bar.md +8 -12
- package/docs/usage/components/transfer-task.md +87 -0
- package/docs/usage/compositions/action-recovery-optimistic.md +18 -14
- package/docs/usage/compositions/action-recovery-save.md +18 -14
- package/docs/usage/compositions/action-recovery-undo.md +18 -14
- package/docs/usage/compositions/activity-status.md +1 -1
- package/docs/usage/compositions/adaptive-content.md +2 -1
- package/docs/usage/compositions/collection-detail.md +6 -5
- package/docs/usage/compositions/collection-editing.md +13 -6
- package/docs/usage/compositions/command-records.md +5 -4
- package/docs/usage/compositions/communication.md +7 -5
- package/docs/usage/compositions/compound-controls.md +7 -3
- package/docs/usage/compositions/content-transition-comparison.md +10 -5
- package/docs/usage/compositions/data-layouts.md +12 -16
- package/docs/usage/compositions/data-workflows.md +7 -4
- package/docs/usage/compositions/date-time-selection.md +5 -5
- package/docs/usage/compositions/design-profile-comparison.md +22 -6
- package/docs/usage/compositions/disclosure.md +14 -9
- package/docs/usage/compositions/document-resource.md +1 -1
- package/docs/usage/compositions/environment-matrix.md +16 -12
- package/docs/usage/compositions/expo-interactions.md +9 -8
- package/docs/usage/compositions/family-drawer.md +9 -5
- package/docs/usage/compositions/field-group.md +7 -3
- package/docs/usage/compositions/interaction-adapters.md +11 -9
- package/docs/usage/compositions/interaction-flow-apply.md +17 -15
- package/docs/usage/compositions/interaction-flow-search.md +11 -7
- package/docs/usage/compositions/live-list.md +12 -8
- package/docs/usage/compositions/navigation-bar-collection.md +8 -3
- package/docs/usage/compositions/optional-motion.md +2 -2
- package/docs/usage/compositions/paper-surface.md +11 -6
- package/docs/usage/compositions/purpose-input-comment.md +10 -4
- package/docs/usage/compositions/purpose-input-message.md +10 -4
- package/docs/usage/compositions/reference-first.md +4 -4
- package/docs/usage/compositions/reference-review.md +4 -4
- package/docs/usage/compositions/reference-settings.md +4 -4
- package/docs/usage/compositions/selection-motion.md +2 -1
- package/docs/usage/compositions/social-compositions.md +7 -6
- package/docs/usage/compositions/stea-event-ticket.md +2 -4
- package/docs/usage/compositions/stea-order-progress.md +19 -21
- package/docs/usage/compositions/stea-pixel-empty.md +26 -26
- package/docs/usage/compositions/task-controls.md +7 -2
- package/docs/usage/compositions/texture-comparison.md +5 -5
- package/docs/usage/compositions/toast-layout.md +7 -3
- package/docs/usage/compositions/toggle-feedback.md +1 -1
- package/docs/usage/compositions/visual-foundations.md +10 -5
- package/docs/usage/compositions/web-additions.md +5 -1
- package/docs/usage/screens/common-chat.md +5 -3
- package/docs/usage/screens/common-comments.md +14 -9
- package/docs/usage/screens/common-conversation-list.md +109 -0
- package/docs/usage/screens/common-inbox.md +5 -3
- package/docs/usage/screens/common-login.md +7 -4
- package/docs/usage/screens/common-profile.md +16 -12
- package/docs/usage/screens/common-saved.md +12 -8
- package/docs/usage/screens/common-search.md +16 -8
- package/docs/usage/screens/common-settings.md +12 -11
- package/docs/usage/screens/common-shell.md +12 -10
- package/docs/usage/screens/dashboard.md +59 -56
- package/docs/usage/screens/discovery-gallery.md +18 -9
- package/docs/usage/screens/flow-collection.md +10 -6
- package/docs/usage/screens/flow-editor.md +9 -6
- package/docs/usage/screens/flow-media.md +12 -7
- package/docs/usage/screens/flow-moderation.md +9 -3
- package/docs/usage/screens/flow-onboarding.md +13 -8
- package/docs/usage/screens/flow-permission.md +5 -3
- package/docs/usage/screens/landing.md +20 -14
- package/docs/usage/screens/mockup-studio.md +100 -88
- package/docs/usage/screens/notification-settings.md +9 -7
- package/docs/usage/screens/product-bento.md +22 -10
- package/docs/usage/screens/reference-comparison.md +2 -2
- package/docs/usage/tokens/font-roles.md +18 -2
- package/docs/usage/tokens/typography.md +22 -11
- package/package.json +7 -1
|
@@ -23,25 +23,26 @@
|
|
|
23
23
|
| HjmProvider/HjmNativeProvider | 앱 소유 profile 한 번 주입 | [테마](../../theming.md) |
|
|
24
24
|
| OverviewScreen | canvas·카드·도구·footer의 기존 틀 | [개요 화면](../components/overview-screen.md) |
|
|
25
25
|
| EffectSurface | 화면 내부 canvas의 grain/ruled | [배경 효과](../components/effect-surface.md) |
|
|
26
|
-
| SegmentedControl | 상속/명시 표현과 간격
|
|
26
|
+
| SegmentedControl + 보이는 이름 Text(`label`) | 상속/명시 표현과 간격 선택. SegmentedControl `label`은 접근성 이름이라 보이는 이름을 위에 둔다 | [단일 선택](../components/segmented-control.md) |
|
|
27
27
|
| TextField/Button/Dialog | 초안·확인·상세와 복귀 | [입력](../components/field.md), [Dialog](../components/dialog.md) |
|
|
28
28
|
|
|
29
29
|
## 배치
|
|
30
30
|
|
|
31
31
|
```text
|
|
32
|
-
|
|
33
|
-
[줄무늬
|
|
32
|
+
설명 + 표현 한계(caption)
|
|
33
|
+
종이 표현 [테마 따르기 / 줄무늬 없는 면 / 줄무늬 추가]
|
|
34
|
+
줄무늬 간격 [좁게 24 / 넓게 40] (줄무늬 추가일 때만 사용 가능, 그 외 흐리게)
|
|
35
|
+
미리보기 도구: 테마 pills 10개
|
|
34
36
|
┌ OverviewScreen: 테마의 기존 화면/구성 기본값 ┐
|
|
35
|
-
│ 제목 → 설명 → 산책 메모
|
|
37
|
+
│ 제목 → 설명 → 기록 도구(산책 메모 입력) │
|
|
36
38
|
│ 기록 2개: 제목 → 본문 │
|
|
37
39
|
│ 확인 결과 → [기록 확인] [자세히 읽기] │
|
|
38
40
|
└ 정적 canvas: 내용 뒤, 입력/읽기 순서 없음 ──┘
|
|
39
|
-
[표현 한계]
|
|
40
41
|
```
|
|
41
42
|
|
|
42
43
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
43
44
|
| --- | --- | --- | --- |
|
|
44
|
-
| 바깥 틀 | Stack | 화면 앞쪽 비교 도구부터 세로 | gap lg20; Native는 바깥 ScrollView와 기존 테마 비교와 같은 640host-unit 화면 viewport |
|
|
45
|
+
| 바깥 틀 | Stack, 스토리 `Container size="reading"` | 화면 앞쪽 비교 도구부터 세로 | gap lg20, 최대 폭 720; Native는 바깥 ScrollView와 기존 테마 비교와 같은 640host-unit 화면 viewport |
|
|
45
46
|
| 기록 항목 | OverviewScreen·Stack | items 본문 | gap sm12; rows/grid/cards와 화면은 profile 기본값 |
|
|
46
47
|
| 줄무늬 | EffectSurface | 화면 canvas 뒤 SVG | 한 선 1host unit, spacing 기본24, 명시 비교40; userSpaceOnUse |
|
|
47
48
|
| footer | Stack·Button | 기존 OverviewScreen footer | gap sm12, primary 확인 다음 secondary 상세 |
|
|
@@ -101,3 +102,7 @@ Native 비교 도구를 높이 없는 Stack에 넣으면 flex 화면이 접혔
|
|
|
101
102
|
밀어냈다. 그래서 기존 테마 비교의 640unit viewport를 사용하고 바깥도 스크롤한다. 제품은 이 예제 높이를
|
|
102
103
|
복사하지 않고 실제 route 영역을 제공한다. iOS 줄무늬는 1unit 폭/백분율 Rect 조합에서 보이지 않아
|
|
103
104
|
spacing×spacing 타일과 명시적 물리 폭을 사용한다. [수정 전후 기기 결과](../../../../../docs/qa/2026-10-07-native-reference-validation.md)를 참고한다.
|
|
105
|
+
|
|
106
|
+
## 근거
|
|
107
|
+
|
|
108
|
+
- 2026-10-17 구성 리뷰: 예제 전용 "다음 테마"가 primary라 화면의 primary "기록 확인"과 경쟁했고, 도구 접기 제목과 입력 label이 같은 "산책 메모"였으며, 두 선택에 보이는 이름이 없었다. Web은 그룹 `disabled`를 보이지 않게 그렸다(SegmentedControl이 항목에 전달하도록 수정).
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
|
|
32
32
|
```text
|
|
33
33
|
┌ 바깥 틀: Web 문서 스크롤 · Native ScrollView(위아래 spacing.md 16) ┐
|
|
34
|
-
│ ← Container gutter 16(폭 600 미만)/20 →
|
|
34
|
+
│ ← Container reading, gutter 16(폭 600 미만)/20 → │
|
|
35
35
|
│ ┌ Surface padding lg ────────────────────────────────┐ │
|
|
36
36
|
│ │ 댓글 작성 Heading level5 │ │
|
|
37
37
|
│ │ ┌ MessageComposer ───────────────────────────────┐ │ │
|
|
@@ -41,17 +41,19 @@
|
|
|
41
41
|
│ │ └────────────────────────────────────────────────┘ │ │
|
|
42
42
|
│ │ 등록한 댓글 목록(예제) │ │
|
|
43
43
|
│ └────────────────────────────────────────────────────┘ │
|
|
44
|
-
│
|
|
44
|
+
│ 전송 상태 문구 (status, Surface 밖) │
|
|
45
|
+
│ 미리보기 도구: [답글 대상 선택] [다음 전송 실패시키기] │
|
|
45
46
|
└──────────────────────────────────────────────────────────────────────┘
|
|
46
47
|
```
|
|
47
48
|
|
|
48
49
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
49
50
|
| --- | --- | --- | --- |
|
|
50
|
-
| 바깥 틀 | 화면의 composer 슬롯(ChatScreen·CommentThreadScreen `composer`). 단독 예제는 Web 문서 스크롤 > `Container`, Native `ScrollView` > `Container` | 화면 하단(footer). 키보드 adapter는 화면 host 하나만 | 좌우 `Container gutter`: 폭 600 미만 `compact` 16, 이상 `regular` 20. Native 위아래 `spacing.md` 16 |
|
|
51
|
+
| 바깥 틀 | 화면의 composer 슬롯(ChatScreen·CommentThreadScreen `composer`). 단독 예제는 Web 문서 스크롤 > `Container size="reading"`, Native `ScrollView` > `Container size="reading"` | 화면 하단(footer). 키보드 adapter는 화면 host 하나만 | 좌우 `Container gutter`: 폭 600 미만 `compact` 16, 이상 `regular` 20. Native 위아래 `spacing.md` 16 |
|
|
51
52
|
| 제목(선택) | `Heading level5` | Surface 맨 위 | 아래 `spacing.md` 16 |
|
|
52
53
|
| composer | `MessageComposer` | 제목 아래 | 입력 1~5줄(`composerMaxLines`), 세로 묶음 `screenPatternRecipe.itemGap` 12(두 플랫폼, 1.12.1 이하 Web은 8)([배치](../components/message-composer.md#배치)) |
|
|
53
54
|
| 실패 문구 | `context` | composer 맨 위 | composer 세로 간격 |
|
|
54
|
-
|
|
|
55
|
+
| 전송 상태 | `Text role="status"`(Native `StatusText`) | Surface 아래. 항상 마운트해 알림 영역을 유지하되 카드 안에 빈 줄을 만들지 않는다 | 바깥 `Stack` 간격 |
|
|
56
|
+
| 데모 행동 | `PreviewTools` 안 `PreviewToolButton`([미리보기 도구](../SELECTION.md)) | 구성 뒤 | 제품에는 넣지 않는다 |
|
|
55
57
|
|
|
56
58
|
## 흐름과 상태
|
|
57
59
|
|
|
@@ -116,3 +118,7 @@ import { Text } from "@hjmds/react-native/primitives";
|
|
|
116
118
|
- 스토리의 "다음 전송 실패시키기"·350ms 지연·예제 사진은 로컬 시연이다. 업로드·서버 저장·권한은 제품이 연결한다.
|
|
117
119
|
|
|
118
120
|
2026-10-13 예제 검수: Web의 원형 전송 버튼이 잠기거나 없어질 때 초점이 body로 빠져나가던 경로를 `inputRef`로 보완했다. 전송 시작 때만 입력에 초점을 두므로 대기 중 사용자가 다른 곳으로 옮긴 초점을 완료 시 다시 가져오지 않는다. 실패 후 수정한 초안으로 재시도하며, 사진·답장 대상은 성공을 확인한 뒤에만 정리한다.
|
|
121
|
+
|
|
122
|
+
## 근거
|
|
123
|
+
|
|
124
|
+
- 2026-10-17 구성 리뷰: 답글 대상 선택·대기 예제 종료가 secondary·ghost 제품 버튼처럼 보였고, 항상 마운트된 빈 상태 문구가 Surface 아래에 빈 띠를 만들었으며, 데스크톱에서 카드가 1280 폭 전체로 늘어났다. 그래서 데모 행동을 미리보기 도구로 옮기고 상태 문구를 Surface 밖에 두고 읽기 폭 Container를 적용했다.
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
```text
|
|
35
35
|
┌ 바깥 틀: Web 문서 스크롤 · Native ScrollView(위아래 spacing.md 16) ┐
|
|
36
|
-
│ ← Container gutter 16(폭 600 미만)/20 →
|
|
36
|
+
│ ← Container reading, gutter 16(폭 600 미만)/20 → │
|
|
37
37
|
│ ┌ Surface padding lg ────────────────────────────────┐ │
|
|
38
38
|
│ │ 메시지 작성 Heading level5 │ │
|
|
39
39
|
│ │ ┌ MessageComposer ───────────────────────────────┐ │ │
|
|
@@ -44,17 +44,19 @@
|
|
|
44
44
|
│ │ └────────────────────────────────────────────────┘ │ │
|
|
45
45
|
│ │ 보낸 메시지 목록(예제) │ │
|
|
46
46
|
│ └────────────────────────────────────────────────────┘ │
|
|
47
|
-
│
|
|
47
|
+
│ 전송 상태 문구 (status, Surface 밖) │
|
|
48
|
+
│ 미리보기 도구: [답장할 메시지 선택] [다음 전송 실패시키기] │
|
|
48
49
|
└──────────────────────────────────────────────────────────────────────┘
|
|
49
50
|
```
|
|
50
51
|
|
|
51
52
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
52
53
|
| --- | --- | --- | --- |
|
|
53
|
-
| 바깥 틀 | 화면의 composer 슬롯(ChatScreen·CommentThreadScreen `composer`). 단독 예제는 Web 문서 스크롤 > `Container`, Native `ScrollView` > `Container` | 화면 하단(footer). 키보드 adapter는 화면 host 하나만 | 좌우 `Container gutter`: 폭 600 미만 `compact` 16, 이상 `regular` 20. Native 위아래 `spacing.md` 16 |
|
|
54
|
+
| 바깥 틀 | 화면의 composer 슬롯(ChatScreen·CommentThreadScreen `composer`). 단독 예제는 Web 문서 스크롤 > `Container size="reading"`, Native `ScrollView` > `Container size="reading"` | 화면 하단(footer). 키보드 adapter는 화면 host 하나만 | 좌우 `Container gutter`: 폭 600 미만 `compact` 16, 이상 `regular` 20. Native 위아래 `spacing.md` 16 |
|
|
54
55
|
| 제목(선택) | `Heading level5` | Surface 맨 위 | 아래 `spacing.md` 16 |
|
|
55
56
|
| composer | `MessageComposer` | 제목 아래 | 입력 1~5줄(`composerMaxLines`), 세로 묶음 `screenPatternRecipe.itemGap` 12(두 플랫폼, 1.12.1 이하 Web은 8)([배치](../components/message-composer.md#배치)) |
|
|
56
57
|
| 실패 문구 | `context` | composer 맨 위 | composer 세로 간격 |
|
|
57
|
-
|
|
|
58
|
+
| 전송 상태 | `Text role="status"`(Native `StatusText`) | Surface 아래. 항상 마운트해 알림 영역을 유지하되 카드 안에 빈 줄을 만들지 않는다 | 바깥 `Stack` 간격 |
|
|
59
|
+
| 데모 행동 | `PreviewTools` 안 `PreviewToolButton`([미리보기 도구](../SELECTION.md)) | 구성 뒤 | 제품에는 넣지 않는다 |
|
|
58
60
|
|
|
59
61
|
## 흐름과 상태
|
|
60
62
|
|
|
@@ -121,3 +123,7 @@ import { Text } from "@hjmds/react-native/primitives";
|
|
|
121
123
|
- 스토리의 "다음 전송 실패시키기"·350ms 지연·예제 사진은 로컬 시연이다. 업로드·서버 저장·권한은 제품이 연결한다.
|
|
122
124
|
|
|
123
125
|
2026-10-13 예제 검수: Web의 원형 전송 버튼이 잠기거나 없어질 때 초점이 body로 빠져나가던 경로를 `inputRef`로 보완했다. 전송 시작 때만 입력에 초점을 두므로 대기 중 사용자가 다른 곳으로 옮긴 초점을 완료 시 다시 가져오지 않는다. 실패 후 수정한 초안으로 재시도하며, 사진·답장 대상은 성공을 확인한 뒤에만 정리한다.
|
|
126
|
+
|
|
127
|
+
## 근거
|
|
128
|
+
|
|
129
|
+
- 2026-10-17 구성 리뷰: 답글 대상 선택·대기 예제 종료가 secondary·ghost 제품 버튼처럼 보였고, 항상 마운트된 빈 상태 문구가 Surface 아래에 빈 띠를 만들었으며, 데스크톱에서 카드가 1280 폭 전체로 늘어났다. 그래서 데모 행동을 미리보기 도구로 옮기고 상태 문구를 Surface 밖에 두고 읽기 폭 Container를 적용했다.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
6
|
- 적용: 구성 API 지침 참조
|
|
7
|
-
- 검토일: 2026-10-
|
|
7
|
+
- 검토일: 2026-10-17
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/구성/시작하기/첫 작업을 만들고 이어하기`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
|
|
9
9
|
- 스토리북: `배포/구성/입력과 작성/첫 작업을 만들고 이어하기`
|
|
10
10
|
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
|
|
54
54
|
이 이름의 독립 공개 컴포넌트는 없다. 아래는 공개 `createActionSession`과 입력·Steps·Sheet를 조합하는 예제다. Storybook 예제 파일을 제품에서 import하지 않는다.
|
|
55
55
|
|
|
56
|
-
- [Web 실행 예제](
|
|
57
|
-
- [Native 실행 예제](
|
|
58
|
-
- [공통 검증·초안 비교](
|
|
56
|
+
- [Web 실행 예제](https://github.com/jim1286/hjm-design-system/blob/9fccb921216a06bac2568ca8252bd0b4d7c48d95/showcase/web/src/patterns/reference-flow-previews.tsx)의 `Editor(kind="first")`
|
|
57
|
+
- [Native 실행 예제](https://github.com/jim1286/hjm-design-system/blob/9fccb921216a06bac2568ca8252bd0b4d7c48d95/showcase/native/src/reference-flow-previews.tsx)의 `Editor(kind="first")`
|
|
58
|
+
- [공통 검증·초안 비교](https://github.com/jim1286/hjm-design-system/blob/9fccb921216a06bac2568ca8252bd0b4d7c48d95/showcase/shared/reference-flows.ts)의 `draftError`, `sameDraft`는 예제 데이터 규칙이며 제품 규칙으로 교체한다.
|
|
59
59
|
|
|
60
60
|
주제 선택(0) → 입력(1) → 검토(2) → 저장 결과 순서다. 중단은 paused 표시만 바꾸고 draft와 stage를 보존한다. 이어하기는 이를 다시 보여 준다.
|
|
61
61
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
6
|
- 적용: 구성 API 지침 참조
|
|
7
|
-
- 검토일: 2026-10-
|
|
7
|
+
- 검토일: 2026-10-17
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/구성/확인/선택 내용 검토와 수정`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
|
|
9
9
|
- 스토리북: `배포/구성/입력과 작성/선택 내용 검토와 수정`
|
|
10
10
|
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
|
|
54
54
|
이 이름의 독립 공개 컴포넌트는 없다. 아래는 공개 `createActionSession`과 입력·Steps·Sheet를 조합하는 예제다. Storybook 예제 파일을 제품에서 import하지 않는다.
|
|
55
55
|
|
|
56
|
-
- [Web 실행 예제](
|
|
57
|
-
- [Native 실행 예제](
|
|
58
|
-
- [공통 검증·초안 비교](
|
|
56
|
+
- [Web 실행 예제](https://github.com/jim1286/hjm-design-system/blob/9fccb921216a06bac2568ca8252bd0b4d7c48d95/showcase/web/src/patterns/reference-flow-previews.tsx)의 `Editor(kind="review")`
|
|
57
|
+
- [Native 실행 예제](https://github.com/jim1286/hjm-design-system/blob/9fccb921216a06bac2568ca8252bd0b4d7c48d95/showcase/native/src/reference-flow-previews.tsx)의 `Editor(kind="review")`
|
|
58
|
+
- [공통 검증·초안 비교](https://github.com/jim1286/hjm-design-system/blob/9fccb921216a06bac2568ca8252bd0b4d7c48d95/showcase/shared/reference-flows.ts)의 `draftError`, `sameDraft`는 예제 데이터 규칙이며 제품 규칙으로 교체한다.
|
|
59
59
|
|
|
60
60
|
입력(1) → 검토(2) → 저장 결과 순서다. 검토에서 수정을 누르면 같은 draft로 입력 단계에 돌아간다. 저장은 검토한 draft의 복사본을 캡처한다.
|
|
61
61
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
6
|
- 적용: 구성 API 지침 참조
|
|
7
|
-
- 검토일: 2026-10-
|
|
7
|
+
- 검토일: 2026-10-17
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/구성/설정/변경 저장과 이탈 확인`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
|
|
9
9
|
- 스토리북: `배포/구성/피드백과 복구/변경 저장과 이탈 확인`
|
|
10
10
|
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
|
|
54
54
|
이 이름의 독립 공개 컴포넌트는 없다. 아래는 공개 `createActionSession`과 입력·Steps·Sheet를 조합하는 예제다. Storybook 예제 파일을 제품에서 import하지 않는다.
|
|
55
55
|
|
|
56
|
-
- [Web 실행 예제](
|
|
57
|
-
- [Native 실행 예제](
|
|
58
|
-
- [공통 검증·초안 비교](
|
|
56
|
+
- [Web 실행 예제](https://github.com/jim1286/hjm-design-system/blob/9fccb921216a06bac2568ca8252bd0b4d7c48d95/showcase/web/src/patterns/reference-flow-previews.tsx)의 `Editor(kind="settings")`
|
|
57
|
+
- [Native 실행 예제](https://github.com/jim1286/hjm-design-system/blob/9fccb921216a06bac2568ca8252bd0b4d7c48d95/showcase/native/src/reference-flow-previews.tsx)의 `Editor(kind="settings")`
|
|
58
|
+
- [공통 검증·초안 비교](https://github.com/jim1286/hjm-design-system/blob/9fccb921216a06bac2568ca8252bd0b4d7c48d95/showcase/shared/reference-flows.ts)의 `draftError`, `sameDraft`는 예제 데이터 규칙이며 제품 규칙으로 교체한다.
|
|
59
59
|
|
|
60
60
|
저장값 session.value와 편집 draft를 비교한다. dirty일 때만 저장·되돌리기를 켜고, 이탈은 확인 Sheet를 연다. 계속 수정은 Sheet만 닫고, 버리기는 마지막 저장값으로 draft를 복원한다.
|
|
61
61
|
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
|
|
33
33
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
34
34
|
| --- | --- | --- | --- |
|
|
35
|
-
| 바깥 틀 | Stack | 화면의 본문 흐름 | gap lg=20px,
|
|
35
|
+
| 바깥 틀 | Stack. Web은 제품 화면의 `Container size="reading"` 안 | 화면의 본문 흐름 | gap lg=20px, 폭은 Container를 따른다(Web 최대 `layout.readingMaxWidth` 720) |
|
|
36
|
+
| 제목 | `Heading level="level3"` | 선택 위 | `heading` 글자 크기·semibold |
|
|
36
37
|
| 선택 | SegmentedControl 2개 | 제목 아래, 연결형 다음 필터형 | 기본 44px 터치 영역, 실제 글자에 따른 높이 |
|
|
37
38
|
| 결과·메모 | Text · TextField | 선택 컨트롤 아래 | 선택 변경 시 필드 재생성 없음 |
|
|
38
39
|
| Native 화면 host | ScreenLayout · KeyboardAvoiding | 본문 스크롤과 바깥 키보드 여백 | flex 1, 끝 여백 spacing.md=16 |
|
|
@@ -21,7 +21,7 @@ Web은 기존 AnimatedStatistic과 같은 `@number-flow/react` peer가 필요하
|
|
|
21
21
|
| 컴포넌트 | 역할 | 지침 |
|
|
22
22
|
| --- | --- | --- |
|
|
23
23
|
| FollowProfileCard | Card · Avatar · AnimatedStatistic · Button으로 프로필·팔로워·controlled follow 요청 | [Card](../components/card.md) |
|
|
24
|
-
| EventAttendanceCard | Card · Avatar · AnimatedStatistic · Button으로 행사·참석·캘린더 연결 | [Avatar](../components/avatar.md) |
|
|
24
|
+
| EventAttendanceCard | Card · Avatar · AnimatedStatistic · RadioGroup(응답) · Button(캘린더)으로 행사·참석·캘린더 연결 | [Avatar](../components/avatar.md) · [RadioGroup](../components/radio-group.md) |
|
|
25
25
|
|
|
26
26
|
## 배치
|
|
27
27
|
|
|
@@ -30,7 +30,7 @@ Web은 기존 AnimatedStatistic과 같은 `@number-flow/react` peer가 필요하
|
|
|
30
30
|
[Avatar] 이름 · 소개 제목 · 소개
|
|
31
31
|
팔로워 수 날짜/시간
|
|
32
32
|
[팔로우 / 팔로잉] 참석 인원 → 확인된 참가자 Avatar + 이름
|
|
33
|
-
응답
|
|
33
|
+
응답 RadioGroup: (○참석) (○미정) (○불참)
|
|
34
34
|
[캘린더 연결]
|
|
35
35
|
오류: 해당 카드 내부 중앙 오류: 해당 카드 내부 중앙
|
|
36
36
|
```
|
|
@@ -40,8 +40,9 @@ Web은 기존 AnimatedStatistic과 같은 `@number-flow/react` peer가 필요하
|
|
|
40
40
|
| 바깥 틀 | Card | 본문·목록·ProfileScreen 안 | 기존 Card recipe |
|
|
41
41
|
| 프로필 header | Avatar · Card title/description | 상단 leading → 이름·소개 | 기존 Card와 Avatar 기본 크기 |
|
|
42
42
|
| 본문 | AnimatedStatistic · Button · Text | 수치 → 주요 행동 → 오류 | Stack gap=md (`spacing.md`, 16px) |
|
|
43
|
-
| 행사 | Text · AnimatedStatistic · Avatar · Button | 날짜 → 인원 → 참가자 → 응답 → 캘린더 → 오류 | Stack gap=md (`spacing.md`, 16px) |
|
|
44
|
-
|
|
|
43
|
+
| 행사 | Text · AnimatedStatistic · Avatar · RadioGroup · Button | 날짜 → 인원 → 참가자 → 응답 → 캘린더(secondary) → 오류 | Stack gap=md (`spacing.md`, 16px) |
|
|
44
|
+
| 참가자 행 | Stack | inline, 폭이 모자라면 wrap. Avatar와 이름은 세로 가운데 정렬 | gap=xs (`spacing.xs`, 8px) |
|
|
45
|
+
| 응답 | RadioGroup | 가로 plain, 그룹 라벨 = 응답 제목. 아직 응답하지 않았으면 선택 없음 | `selectionGroupRecipe` horizontal plain gap `spacing.sm` 12 |
|
|
45
46
|
| 오류 | Text | 자신의 안내 영역 가로·세로 중앙 | 임의 상단 margin 없음 |
|
|
46
47
|
|
|
47
48
|
## 흐름과 상태
|
|
@@ -53,8 +54,8 @@ Web은 기존 AnimatedStatistic과 같은 `@number-flow/react` peer가 필요하
|
|
|
53
54
|
|
|
54
55
|
| 상태 | 모습 | 포커스·알림 |
|
|
55
56
|
| --- | --- | --- |
|
|
56
|
-
| 기본 | 확인된 선택·수치·참가자 | Button
|
|
57
|
-
| 진행 중 | 캡처한 제안 선택·수치, 행동 잠금 | 로딩은 버튼 안, 수치 전환은 AnimatedStatistic |
|
|
57
|
+
| 기본 | 확인된 선택·수치·참가자 | 팔로우는 Button 선택 계약, 응답은 RadioGroup 키보드 계약 |
|
|
58
|
+
| 진행 중 | 캡처한 제안 선택·수치, 행동 잠금 | 팔로우·캘린더 로딩은 버튼 안, 응답은 그룹 비활성 + 설명에 진행 문구, 수치 전환은 AnimatedStatistic |
|
|
58
59
|
| 실패 | 현재 confirmed state로 복구, 중앙 오류 | Web alert / Native assertive, 같은 행동 재시도 |
|
|
59
60
|
| 캘린더 완료 | 실제 callback fulfilled 후 완료 label | 같은 mount에서 중복 추가 차단 |
|
|
60
61
|
| 엔티티 변경 | 새 profileId/eventId의 상태 | 이전 응답은 새 카드에 반영하지 않음 |
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
| 바깥 틀 | Web: `Container size="reading"`. Native: `ScrollView` → `Container` | 화면 본문, 카드는 스크롤과 함께 움직인다. 안전 영역은 화면 host(SafeArea·navigation header)가 준다. 입력이 없어 키보드 처리는 없다 | Native `ScrollView` 위아래 `spacing.md` 16, 좌우는 `Container` `gutter`(폭 600 미만 `compact` 16, 이상 `regular` 20). 최대 폭 `layout.readingMaxWidth` 720 |
|
|
58
58
|
| 틀 | `Card` | 바깥 틀 안 | body padding `spacing.md` 16(`cardRecipe`) |
|
|
59
59
|
| 정보 | `DescriptionList` | Card 머리 아래 | 정보–식별 묶음 `Stack gap="lg"` 20 |
|
|
60
|
-
| 식별 묶음 | `QRCode`(+fallback) → 라벨 → 번호 | 정보 아래, 시작 정렬 | 묶음 안 `Stack gap="sm"` 12, QR `size` 기본 192(모듈 수의 정수배로 내림), quiet zone 4모듈. QR
|
|
60
|
+
| 식별 묶음 | `QRCode`(+fallback) → 라벨 → 번호 | 정보 아래, 시작 정렬 | 묶음 안 `Stack gap="sm"` 12, QR `size` 기본 192(모듈 수의 정수배로 내림), quiet zone 4모듈. QR 아래에 fallback을 세로로 쌓고 가운데 정렬하며 둘 사이 간격 12(`qrCodeRecipe.fallbackGap`)는 컴포넌트가 준다. fallback을 Stack으로 다시 감싸지 않는다 |
|
|
61
61
|
| 로딩·실패 | `Skeleton`·`Result` | 카드 자리 | Skeleton `block` 기본 높이 `spacing.xxl` 32. Result는 자체 여백(`resultRecipe`) |
|
|
62
62
|
|
|
63
63
|
- QR은 번호 **위**에 두고, 번호는 QR이 읽히지 않을 때 직원이 입력할 수 있도록 항상 같이 보인다.
|
|
@@ -99,9 +99,8 @@ import { QRCode } from "@hjmds/react/qr-code";
|
|
|
99
99
|
<Stack gap="lg">
|
|
100
100
|
<DescriptionList aria-label={t("ticket.details")} items={ticketState.ticket.details} />
|
|
101
101
|
<Stack gap="sm">
|
|
102
|
-
{/* QRCode의 svg가 inline이라 fallback을 블록으로 감싸 아래 줄에 둔다. */}
|
|
103
102
|
<QRCode value={ticketState.ticket.booking} label={t("ticket.qrLabel", { booking: ticketState.ticket.booking })}
|
|
104
|
-
fallback={<
|
|
103
|
+
fallback={<Text tone="muted">{t("ticket.qrFallback")}</Text>} />
|
|
105
104
|
<Text variant="label" tone="muted">{t("ticket.bookingLabel")}</Text>
|
|
106
105
|
<Text variant="title" emphasis="strong">{ticketState.ticket.booking}</Text>
|
|
107
106
|
</Stack>
|
|
@@ -155,7 +154,6 @@ const gutter = resolveWindowClass(useWindowDimensions().width) === "compact" ? "
|
|
|
155
154
|
| --- | --- | --- |
|
|
156
155
|
| 바깥 틀 | 문서 스크롤 + `Container` | `ScrollView`(위아래 `spacing.md`) + `Container` |
|
|
157
156
|
| `DescriptionList` 이름·항목 | `aria-label` + `items` | `label` + `descriptor={{ items }}` |
|
|
158
|
-
| fallback 감싸기 | inline svg 옆으로 흐르지 않게 `Stack`으로 감싼다 | `Text` 그대로 |
|
|
159
157
|
| 번호 선택 | 브라우저 기본 텍스트 선택 | `Text selectable` |
|
|
160
158
|
| QR 배치 prop | 배치 전용 `layoutStyle`만 | 없음(감싸는 레이아웃에서 정한다) |
|
|
161
159
|
| QR peer | `qrcode-generator` 2.0.4 | `qrcode-generator` 2.0.4 + `react-native-svg` 15.15.5(dev client 재빌드) |
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
| --- | --- | --- |
|
|
23
23
|
| `Card` | 제목("주문 처리 단계")·설명 틀 | [Card](../components/card.md) |
|
|
24
24
|
| `Steps` | 가로 단계 표시. `currentStepStatus`: `current` · `error` · `complete` | [Steps](../components/steps.md) |
|
|
25
|
-
| 진행 문구 `
|
|
25
|
+
| 진행 문구 `StatusText` | "서버 확인을 기다리는 중이에요." / "모든 단계가 확정됐어요." | [Text](../components/text.md#statustext) |
|
|
26
26
|
| `Notice` `tone="danger"` | 실패 안내(상태는 그대로이니 다시 요청) | [Notice](../components/notice.md) |
|
|
27
27
|
| `Button` primary | 다음 단계 요청 / 다시 요청. 요청 중 `loading` | [Button](../components/button.md) |
|
|
28
28
|
| `Button` secondary | 전체 완료 후 "처음부터 다시" | [Button](../components/button.md) |
|
|
29
29
|
| `Text` label muted + `Timeline` | "처리 기록" 제목과 확정·실패 사건 목록 | [Timeline](../components/timeline.md) |
|
|
30
|
-
|
|
|
30
|
+
| 미리보기 도구 | 스토리 전용 "다음 요청 실패시키기" 선택 칩. Card 밖 아래에 두며 제품에 넣지 않는다 | — |
|
|
31
31
|
| `ScrollView` + `Container`(Native) | 바깥 틀. 세로 스크롤·위아래 여백, 좌우 gutter | [Container](../components/container.md), [화면 여백과 너비](../tokens/layout.md) |
|
|
32
32
|
|
|
33
33
|
## 배치
|
|
@@ -62,7 +62,7 @@ Native 화면(ScrollView, 위아래 spacing.lg 20) > Container gutter 16(폭 < 6
|
|
|
62
62
|
| 단계 | `Steps` | Card 머리 아래 맨 위 | 단계 사이 `stepsRecipe.gap` `spacing.xs` 8, 표시 원 `glyph.md` |
|
|
63
63
|
| 진행 | 진행 문구 | 단계 아래 | `spacing.lg` 20 |
|
|
64
64
|
| 실패 | `Notice` danger | 진행 문구 아래, 실패 때만 | `spacing.lg` 20 |
|
|
65
|
-
| 행동 | `Button` primary(또는 완료 후 secondary) | Notice 아래, 꽉 찬 폭(`Stack` 기본 `align="stretch"`가 채우므로 Web `layoutStyle`·Native `fullWidth`를 따로 주지 않는다) | 높이 44,
|
|
65
|
+
| 행동 | `Button` primary(또는 완료 후 secondary) | Notice 아래, 꽉 찬 폭(`Stack` 기본 `align="stretch"`가 채우므로 Web `layoutStyle`·Native `fullWidth`를 따로 주지 않는다) | 높이 44, 버튼 하나만 둔다 |
|
|
66
66
|
| 기록 | label + `Timeline` | 맨 아래, 스크롤 | `spacing.lg` 20, 기록은 아래로 쌓인다 |
|
|
67
67
|
|
|
68
68
|
- 실패 Notice는 버튼 **위**에 둔다. 사용자가 이유를 읽은 다음 "다시 요청"에 닿는다.
|
|
@@ -86,7 +86,7 @@ Native 화면(ScrollView, 위아래 spacing.lg 20) > Container gutter 16(폭 < 6
|
|
|
86
86
|
| 완료 | 모든 단계 complete, 진행 문구 "모든 단계가 확정됐어요.", secondary 버튼 | 진행 문구가 읽힌다 |
|
|
87
87
|
|
|
88
88
|
- 요청 중 중복 요청은 상태 로직에서 무시한다. 버튼 `loading`만으로는 키보드 반복 입력을 막지 못한다.
|
|
89
|
-
- 예제 실패
|
|
89
|
+
- 예제 실패 도구도 요청 중에는 잠그고 늦은 toggle callback을 reducer에서 무시한다. 응답을 기다리는 동안 도구가 진행 중 요청의 결과를 뒤집던 실제 예제 검수에 따른 보완이다.
|
|
90
90
|
- 문구 키는 상태별 상수로 둔다. 상태 이름으로 키를 조립하지 않는다.
|
|
91
91
|
|
|
92
92
|
| 상태 | 진행 문구 키 | Notice 키 | 버튼 키 |
|
|
@@ -106,6 +106,7 @@ import { Card, Timeline } from "@hjmds/react/display";
|
|
|
106
106
|
import { Notice } from "@hjmds/react/feedback";
|
|
107
107
|
import { Stack, Text } from "@hjmds/react/layout";
|
|
108
108
|
import { Steps } from "@hjmds/react/steps";
|
|
109
|
+
import { StatusText } from "@hjmds/react/status-text";
|
|
109
110
|
|
|
110
111
|
// 상태 → 문구 키
|
|
111
112
|
const noticeKey = { rejected: "order.failed", requestFailed: "order.requestFailed" } as const;
|
|
@@ -115,14 +116,12 @@ const progressKey = { requesting: "order.requesting", done: "order.done" } as co
|
|
|
115
116
|
<Stack gap="lg">
|
|
116
117
|
<Steps descriptor={{ steps, currentStepId, currentStepStatus }} statusLabels={statusLabels}
|
|
117
118
|
composeAccessibleName={({ position, total, label }) => t("order.stepName", { position, total, label })} />
|
|
118
|
-
{/* 알림 자리를 위해
|
|
119
|
-
<
|
|
119
|
+
{/* 알림 자리를 위해 live node는 늘 마운트한다. 비어 있으면 레이아웃에서 빠진다 */}
|
|
120
|
+
<StatusText>{requesting ? t(progressKey.requesting) : done ? t(progressKey.done) : ""}</StatusText>
|
|
120
121
|
{failure ? <Notice tone="danger" title={t(noticeKey[failure])} /> : null}
|
|
121
|
-
|
|
122
|
-
{
|
|
123
|
-
|
|
124
|
-
: <Button loading={requesting} onClick={request}>{failure ? t("order.retry") : t("order.request")}</Button>}
|
|
125
|
-
</Stack>
|
|
122
|
+
{done
|
|
123
|
+
? <Button tone="secondary" onClick={restart}>{t("order.restart")}</Button>
|
|
124
|
+
: <Button loading={requesting} onClick={request}>{failure ? t("order.retry") : t("order.request")}</Button>}
|
|
126
125
|
<Text variant="label" tone="muted">{t("order.log")}</Text>
|
|
127
126
|
<Timeline items={log} composeAccessibleName={({ position, total, label }) => t("order.logName", { position, total, label })} />
|
|
128
127
|
</Stack>
|
|
@@ -139,6 +138,7 @@ import { Card, Timeline } from "@hjmds/react-native/data-display";
|
|
|
139
138
|
import { Notice } from "@hjmds/react-native/feedback";
|
|
140
139
|
import { Container, Stack, Text } from "@hjmds/react-native/primitives";
|
|
141
140
|
import { Steps } from "@hjmds/react-native/steps";
|
|
141
|
+
import { StatusText } from "@hjmds/react-native/status-text";
|
|
142
142
|
|
|
143
143
|
const noticeKey = { rejected: "order.failed", requestFailed: "order.requestFailed" } as const;
|
|
144
144
|
const { width } = useWindowDimensions();
|
|
@@ -151,13 +151,11 @@ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
|
151
151
|
<Steps descriptor={{ steps, currentStepId, currentStepStatus }} statusLabels={statusLabels}
|
|
152
152
|
composeAccessibleName={({ position, total, label }) => t("order.stepName", { position, total, label })} />
|
|
153
153
|
{/* 빈 Text가 gap 하나만큼 틈을 남기므로 문구가 있을 때만 그린다. */}
|
|
154
|
-
{requesting || done ? <
|
|
154
|
+
{requesting || done ? <StatusText announceOnMount>{done ? t("order.done") : t("order.requesting")}</StatusText> : null}
|
|
155
155
|
{failure ? <Notice tone="danger" announcement="assertive" title={t(noticeKey[failure])} /> : null}
|
|
156
|
-
|
|
157
|
-
{
|
|
158
|
-
|
|
159
|
-
: <Button loading={requesting} onPress={request}>{failure ? t("order.retry") : t("order.request")}</Button>}
|
|
160
|
-
</Stack>
|
|
156
|
+
{done
|
|
157
|
+
? <Button tone="secondary" onPress={restart}>{t("order.restart")}</Button>
|
|
158
|
+
: <Button loading={requesting} onPress={request}>{failure ? t("order.retry") : t("order.request")}</Button>}
|
|
161
159
|
<Text variant="label" tone="muted">{t("order.log")}</Text>
|
|
162
160
|
<Timeline items={log} composeAccessibleName={({ position, total, label }) => t("order.logName", { position, total, label })} />
|
|
163
161
|
</Stack>
|
|
@@ -166,14 +164,14 @@ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
|
166
164
|
</ScrollView>;
|
|
167
165
|
```
|
|
168
166
|
|
|
169
|
-
단계 이름·문구, 실패
|
|
167
|
+
단계 이름·문구, 실패 미리보기 도구, 900ms 지연은 예시다. 단계 정의와 확정·거절 판단은 서버와 제품 소유다.
|
|
170
168
|
상태 전이는 스토리의 `orderReducer`·`orderStepsDescriptor`를 참고한다.
|
|
171
169
|
|
|
172
170
|
## 플랫폼 차이
|
|
173
171
|
|
|
174
172
|
| 항목 | Web | Native |
|
|
175
173
|
| --- | --- | --- |
|
|
176
|
-
| 진행 문구 자리 |
|
|
174
|
+
| 진행 문구 자리 | `StatusText`를 항상 마운트(알림 자리 유지). 비어 있으면 레이아웃에서 빠져 gap을 만들지 않는다 | 문구가 있을 때만 마운트(iOS는 live region을 무시하고 빈 Text가 틈을 남김, 2026-10-02 시뮬레이터 확인) |
|
|
177
175
|
| 이벤트 | `onClick` | `onPress` |
|
|
178
176
|
| 바깥 틀 | HJM ScreenLayout의 본문 | `ScrollView` > `Container` |
|
|
179
177
|
| 실패 Notice 발표 | `danger`는 늘 `role="alert"` | `announcement="assertive"`를 지정해야 발표(기본 `none`) |
|
|
@@ -183,5 +181,5 @@ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
|
183
181
|
- 실패할 때 커서를 옮기거나 완료 표시를 먼저 그리지 않는다. 서버가 거절한 단계를 사용자가 끝난 것으로 오해한다.
|
|
184
182
|
- `Timeline` 이름은 단계 이름과 다르게 짓는다("기록 N개 중 M번째"). "5단계 중"으로 읽히면 단계 수와 섞인다.
|
|
185
183
|
- Steps는 세로 방향·클릭 이동을 지원하지 않는다(`src/steps.ts`). 단계 사이를 눌러 이동하는 UI를 기대하지 않는다.
|
|
186
|
-
- 현재 스토리에는 네트워크·서버 오류로 요청 자체가 실패하는 경로가 없다(실패는
|
|
187
|
-
- Web
|
|
184
|
+
- 현재 스토리에는 네트워크·서버 오류로 요청 자체가 실패하는 경로가 없다(실패는 미리보기 도구로 만든 거절뿐이다).
|
|
185
|
+
- Web에서 빈 `role="status"` Text를 직접 두면 Stack 자식이라 gap 한 칸(`spacing.lg` 20)이 Steps 아래에 남는다. `StatusText`를 쓴다.
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
| EmptyState | 그림·제목·설명·행동 한 묶음 | [EmptyState](../components/empty-state.md) |
|
|
23
23
|
| 제품 캐릭터(스토리: 12×12 픽셀 스프라이트) | 장식 그림, 4프레임 220ms 반복 | 제품 소유 |
|
|
24
24
|
| Button `primary` | 첫 기록 남기기(시작 행동) | [Button](../components/button.md) |
|
|
25
|
-
|
|
|
26
|
-
|
|
|
25
|
+
| Switch | 캐릭터 움직임 켜기·끄기(멈춤 제어) | [Switch](../components/switch.md) |
|
|
26
|
+
| StatusText `tone="muted"` 상태 문구 | 시작 행동 결과 | [Text](../components/text.md#statustext) |
|
|
27
27
|
| `ScrollView` + `Container`(Native) | 바깥 틀. 세로 스크롤·위아래 여백, 좌우 gutter | [Container](../components/container.md), [화면 여백과 너비](../tokens/layout.md) |
|
|
28
28
|
|
|
29
29
|
## 배치
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
```text
|
|
32
32
|
Native 화면(ScrollView, 위아래 spacing.lg 20) > Container gutter 16(폭 < 600) · 20(폭 ≥ 600)
|
|
33
33
|
┌──────── Stack gap spacing.md 16 ─────────┐
|
|
34
|
-
│ ┌──── EmptyState (가운데
|
|
34
|
+
│ ┌──── EmptyState (남은 높이, 가운데) ──┐ │
|
|
35
35
|
│ │ ┌────────┐ │ │
|
|
36
36
|
│ │ │ 캐릭터 │ 96×96 │ │
|
|
37
37
|
│ │ └────────┘ ↕ spacing.xs 8│ │
|
|
@@ -39,7 +39,7 @@ Native 화면(ScrollView, 위아래 spacing.lg 20) > Container gutter 16(폭 < 6
|
|
|
39
39
|
│ │ 말랑이가 첫 기록을 기다리고 있어요. │ │
|
|
40
40
|
│ │ [ 첫 기록 남기기 ] primary │ │ ← 주 행동
|
|
41
41
|
│ └────────────────────────────────────────┘ │
|
|
42
|
-
│
|
|
42
|
+
│ 캐릭터 움직임 (●──) │ ← Switch(멈춤 제어)
|
|
43
43
|
│ 상태 문구 (muted, live) │
|
|
44
44
|
└───────────────────────────────────────────┘
|
|
45
45
|
고정 영역 없음. 안전 영역은 화면 골격이 맡는다
|
|
@@ -47,25 +47,25 @@ Native 화면(ScrollView, 위아래 spacing.lg 20) > Container gutter 16(폭 < 6
|
|
|
47
47
|
|
|
48
48
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
49
49
|
| --- | --- | --- | --- |
|
|
50
|
-
| 바깥 틀 | Web: HJM ScreenLayout의 본문. Native: `ScrollView` > `Container` | 이 구성은 바깥 폭·여백을 정하지 않는다(Web 스토리는 Stack만 그린다). Native는 `ScrollView` 안 [Container](../components/container.md)에
|
|
51
|
-
| 빈 상태 | EmptyState `density="regular"` |
|
|
50
|
+
| 바깥 틀 | Web: HJM ScreenLayout의 본문. Native: `ScrollView`(`contentContainerStyle` `flexGrow: 1`) > `Container` | 이 구성은 바깥 폭·여백을 정하지 않는다(Web 스토리는 캔버스 높이를 채운 Stack만 그린다). 본문 높이를 받은 Stack 안에서 EmptyState가 남은 높이를 채운다. Native는 `ScrollView` 안 [Container](../components/container.md)에 두고 Container·Stack도 `flexGrow: 1`로 높이를 잇는다. 입력이 없어 키보드 처리는 없다. 안전 영역은 화면 골격(내비게이션 헤더·탭 바)이 맡는다 | Native ScrollView 위아래 `spacing.lg` 20, Container gutter 폭 600 미만 `compact` 16 · 이상 `regular` 20(`layout.pagePadding`) |
|
|
51
|
+
| 빈 상태 | EmptyState `density="regular"` | 본문의 남은 높이를 채우고 가로·세로 가운데 | 위아래 Web `spacing.xxl` 32 / Native `spacing.xxxl` 40, 좌우 `spacing.xl` 24, 슬롯 사이 `spacing.xs` 8 |
|
|
52
52
|
| 캐릭터 | Web `icon` / Native `illustration` | EmptyState 맨 위 | 96×96(12칸 × `spacing.xs` 8). Native도 그림 노드 자체의 96×96 크기를 사용한다 |
|
|
53
53
|
| 시작 행동 | Button `primary` | EmptyState `action` | 높이 44, 하나만 |
|
|
54
|
-
| 멈춤 제어 |
|
|
55
|
-
| 상태 문구 |
|
|
54
|
+
| 멈춤 제어 | Switch "캐릭터 움직임" | EmptyState 바로 아래, 꽉 찬 폭(`Stack` 기본 `align="stretch"`) | 묶음 사이 `spacing.md` 16 |
|
|
55
|
+
| 상태 문구 | StatusText muted | 맨 아래. Web은 늘 마운트하고 비어 있으면 레이아웃에서 빠진다, Native는 문구가 있을 때만 | `spacing.md` 16 |
|
|
56
56
|
|
|
57
57
|
## 흐름과 상태
|
|
58
58
|
|
|
59
59
|
1. 화면이 열리면 캐릭터가 220ms 간격 4프레임으로 반복한다.
|
|
60
60
|
2. "첫 기록 남기기"를 누르면 제품의 작성 화면으로 간다(스토리는 상태 문구만 보인다).
|
|
61
|
-
3. "움직임
|
|
61
|
+
3. "캐릭터 움직임" Switch를 끄면 첫 프레임에서 멈추고, 다시 켜면 움직인다.
|
|
62
62
|
|
|
63
63
|
| 상태 | 모습 | 포커스·알림 |
|
|
64
64
|
| --- | --- | --- |
|
|
65
65
|
| 기본 | 캐릭터가 움직인다 | 캐릭터는 숨김, 제목·설명이 의미를 전한다 |
|
|
66
66
|
| 진행 중 | 시작 행동이 화면 이동이면 없다. 서버 요청(예: 첫 항목 생성)이면 primary `loading` | 포커스는 버튼 유지 |
|
|
67
67
|
| 실패 | 시작 행동이 서버 요청일 때 실패하면 상태 문구 자리에 실패 문구, primary는 다시 누를 수 있다. 빈 상태 자체를 불러오지 못한 경우는 이 구성이 아니라 오류 화면([Result](../components/result.md) failure)이다 | 상태 문구가 Web `role="status"`, Native live region으로 읽힌다 |
|
|
68
|
-
| 멈춤 | 첫 프레임 정지 |
|
|
68
|
+
| 멈춤 | 첫 프레임 정지 | Switch 상태(켬·끔)가 읽힌다 |
|
|
69
69
|
| reduced motion | 타이머 없이 첫 프레임 | — |
|
|
70
70
|
| 탭 숨김(Web)·앱 배경(Native) | 타이머를 걸지 않는다 | — |
|
|
71
71
|
| 시작 행동 후 | 상태 문구 표시 | Web `role="status"`, Native `accessibilityLiveRegion` |
|
|
@@ -77,20 +77,20 @@ Native 화면(ScrollView, 위아래 spacing.lg 20) > Container gutter 16(폭 < 6
|
|
|
77
77
|
// Web
|
|
78
78
|
import { EmptyState } from "@hjmds/react/feedback";
|
|
79
79
|
import { Button } from "@hjmds/react/actions";
|
|
80
|
-
import { Stack
|
|
80
|
+
import { Stack } from "@hjmds/react/layout";
|
|
81
|
+
import { Switch } from "@hjmds/react/selection";
|
|
82
|
+
import { StatusText } from "@hjmds/react/status-text";
|
|
81
83
|
|
|
82
84
|
// 상태 → 문구 키
|
|
83
85
|
const statusKey = { idle: undefined, started: "records.empty.started", failed: "records.empty.startFailed" } as const;
|
|
84
86
|
const statusText = statusKey[startState];
|
|
85
87
|
|
|
86
|
-
<Stack gap="md">
|
|
88
|
+
<Stack gap="md" layoutStyle={{ flexGrow: 1 }}>{/* 본문 높이를 가진 flex 부모 안에서 채운다 */}
|
|
87
89
|
<EmptyState icon={<ProductMascot paused={paused} />} title={t("records.empty.title")}
|
|
88
90
|
description={t("records.empty.description")}
|
|
89
91
|
action={<Button loading={starting} onClick={startRecord}>{t("records.empty.start")}</Button>} />
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
</Button>
|
|
93
|
-
<Text role="status" tone="muted">{statusText ? t(statusText) : ""}</Text>
|
|
92
|
+
<Switch label={t("mascot.motion")} checked={!paused} onCheckedChange={(on) => setPaused(!on)} />
|
|
93
|
+
<StatusText tone="muted">{statusText ? t(statusText) : ""}</StatusText>
|
|
94
94
|
</Stack>;
|
|
95
95
|
```
|
|
96
96
|
|
|
@@ -101,23 +101,23 @@ import { spacing } from "@hjmds/design-contracts/foundations";
|
|
|
101
101
|
import { resolveWindowClass } from "@hjmds/design-contracts/responsive";
|
|
102
102
|
import { EmptyState } from "@hjmds/react-native/feedback";
|
|
103
103
|
import { Button } from "@hjmds/react-native/actions";
|
|
104
|
-
import {
|
|
104
|
+
import { Switch } from "@hjmds/react-native/inputs";
|
|
105
|
+
import { Container, Stack } from "@hjmds/react-native/primitives";
|
|
106
|
+
import { StatusText } from "@hjmds/react-native/status-text";
|
|
105
107
|
|
|
106
108
|
const statusKey = { idle: undefined, started: "records.empty.started", failed: "records.empty.startFailed" } as const;
|
|
107
109
|
const statusText = statusKey[startState];
|
|
108
110
|
const { width } = useWindowDimensions();
|
|
109
111
|
const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
110
112
|
|
|
111
|
-
<ScrollView contentContainerStyle={{ paddingVertical: spacing.lg }}>
|
|
112
|
-
<Container gutter={gutter}>
|
|
113
|
-
<Stack gap="md">
|
|
113
|
+
<ScrollView contentContainerStyle={{ flexGrow: 1, paddingVertical: spacing.lg }}>
|
|
114
|
+
<Container gutter={gutter} layoutStyle={{ flexGrow: 1 }}>
|
|
115
|
+
<Stack gap="md" layoutStyle={{ flexGrow: 1 }}>
|
|
114
116
|
<EmptyState illustration={<ProductMascot paused={paused} />}
|
|
115
117
|
title={t("records.empty.title")} description={t("records.empty.description")}
|
|
116
118
|
action={<Button loading={starting} onPress={startRecord}>{t("records.empty.start")}</Button>} />
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
</Button>
|
|
120
|
-
{statusText ? <Text accessibilityLiveRegion="polite" tone="muted">{t(statusText)}</Text> : null}
|
|
119
|
+
<Switch label={t("mascot.motion")} checked={!paused} onCheckedChange={(on) => setPaused(!on)} />
|
|
120
|
+
{statusText ? <StatusText announceOnMount tone="muted">{t(statusText)}</StatusText> : null}
|
|
121
121
|
</Stack>
|
|
122
122
|
</Container>
|
|
123
123
|
</ScrollView>;
|
|
@@ -132,11 +132,11 @@ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
|
132
132
|
| 그림 슬롯 | `icon` | `illustration`(최소 `glyph.lg` 크기만 보장하고 캐릭터 자체 크기를 존중한다) |
|
|
133
133
|
| 세로 여백 | `.hjm-empty-state` `spacing.xxl` 32 | recipe `spacing.xxxl` 40 |
|
|
134
134
|
| 일시정지 감지 | `document.hidden` | `AppState` |
|
|
135
|
-
| 상태 문구 자리 |
|
|
135
|
+
| 상태 문구 자리 | `StatusText`를 늘 마운트(비면 레이아웃에서 빠짐) | 문구가 있을 때만 마운트 |
|
|
136
136
|
| 바깥 틀 | HJM ScreenLayout의 본문 | `ScrollView` > `Container` |
|
|
137
137
|
|
|
138
138
|
## 함정
|
|
139
139
|
|
|
140
|
-
- 5초 넘게 반복되는 움직임에는 멈춤 제어를 둔다(WCAG 2.2.2).
|
|
140
|
+
- 5초 넘게 반복되는 움직임에는 멈춤 제어를 둔다(WCAG 2.2.2). 켜고 끄는 제어이므로 라벨이 바뀌는 텍스트 버튼이 아니라 Switch로 둔다([켜고 끄기](../SELECTION.md)).
|
|
141
141
|
- 캐릭터에 접근성 이름을 붙이지 않는다. EmptyState가 그림을 장식으로 숨긴다.
|
|
142
142
|
- 현재 스토리의 시작 행동은 상태 문구만 바꾼다. 진행 중·실패 경로는 없다.
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
## 배치
|
|
29
29
|
|
|
30
30
|
```text
|
|
31
|
-
분할 실행: [현재 방식 실행] [방식 메뉴]
|
|
31
|
+
분할 실행: [현재 방식 실행] [⌄ 방식 메뉴 IconButton]
|
|
32
32
|
계정 전환: 확인된 현재 계정 → [계정 메뉴]
|
|
33
33
|
비밀번호: 기존 비밀번호 입력 → 규칙 충족 막대 → 규칙별 상태
|
|
34
34
|
요금: 주기별 라디오 카드 → 선택한 실제 가격·혜택 → [계속]
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
39
39
|
| --- | --- | --- | --- |
|
|
40
40
|
| 바깥 틀 | Stack/Card | 기존 본문·패널 안 | sm 또는 md |
|
|
41
|
-
| 분할 행동 | Button
|
|
41
|
+
| 분할 행동 | Button + Menu(IconButton chevronDown 트리거, label=`menuLabel`) | 같은 inline 행, 메뉴는 실행 버튼 끝 | 인라인 간격 `spacing.sm` 12(button.md) |
|
|
42
|
+
| 메뉴 트리거 | 분할 행: IconButton(secondary). 계정 전환: Button(secondary) | 분할 행 끝·계정 이름 아래 | 내용 폭. Web·Native 모두 전체 폭으로 늘이지 않음 |
|
|
42
43
|
| 비밀번호 | PasswordField/Progress/Text | 위→아래 | sm |
|
|
43
44
|
| 요금 선택 | RadioGroup | 카드 상단 | card presentation |
|
|
44
45
|
| 실패 | Text | 구성 내부 중앙 | 임의 상단 여백 없음 |
|
|
@@ -93,3 +94,7 @@ import { AccountSwitcher } from "@hjmds/react-native/task-controls";
|
|
|
93
94
|
- 옵션 선택으로 결제를 시작하지 않는다. 실제 결제·약관·상품 검증은 onContinue 뒤 제품 흐름 책임이다.
|
|
94
95
|
- 다른 엔티티/세션으로 전환할 때 host key를 바꾸고 늦은 제품 API 응답도 제품 상태 계층에서 검증한다.
|
|
95
96
|
- npm 1.21.0 API다. 기기·실제 로그인·결제 검증과 npm 게시를 구분한다.
|
|
97
|
+
|
|
98
|
+
## 근거
|
|
99
|
+
|
|
100
|
+
- 2026-10-17 구성 리뷰: 분할 실행의 방식 메뉴가 두 번째 텍스트 버튼("실행 방식 선택")으로 보여 별개 행동처럼 읽혔고 인라인 간격이 8이었다. 그래서 메뉴를 chevron IconButton 도구로 바꾸고 간격을 12로 맞췄다.
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
| --- | --- | --- |
|
|
25
25
|
| EffectSurface | 질감·테마·장식 수명주기 | [배경 효과](../components/effect-surface.md) |
|
|
26
26
|
| SegmentedControl | 두 영역의 강도를 함께 선택 | [단일 선택](../components/segmented-control.md) |
|
|
27
|
+
| Heading | 각 비교 영역 제목 | [제목](../components/heading.md) |
|
|
27
28
|
| TextField·Button·Text | 읽기·입력·행동·결과 확인 | [입력](../components/field.md) |
|
|
28
29
|
|
|
29
30
|
noise는 생성된64×64 PNG alpha mask이며 자체 런타임 픽셀 생성·외부 요청이 없다.
|
|
@@ -41,11 +42,10 @@ seed는 고정 타일의 위상만 이동한다. 새 타일 픽셀을 생성하
|
|
|
41
42
|
|
|
42
43
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
43
44
|
| --- | --- | --- | --- |
|
|
44
|
-
| 바깥 틀 | Stack |
|
|
45
|
-
| 비교 영역 | EffectSurface | grain 다음 noise | 내부
|
|
45
|
+
| 바깥 틀 | Web `Container size="reading"` → Stack, Native KeyboardAvoiding → ScrollView → `Container gutter="compact"` → Stack | 본문 세로 흐름. 화면 머리(ScreenLayout 제목)는 두지 않는다 | gap lg20, Native ScrollView 위아래 spacing.lg20 |
|
|
46
|
+
| 비교 영역 | EffectSurface `padding="lg"` | grain 다음 noise | 내부 여백 20, Stack gap md16, 제목 `Heading level="level5"` |
|
|
46
47
|
| 질감 | SVG mask | absolute 배경 | viewBox 밖64×64 CSS pixel/Native logical point 타일 반복 |
|
|
47
48
|
| 본문 | TextField·Button | 장식 앞 일반 레이아웃 | 기존 recipe |
|
|
48
|
-
| Native 화면 | ScreenLayout·KeyboardAvoiding | 본문 스크롤과 바깥 키보드 여백 | 긴 내용·큰 글자 접근용 |
|
|
49
49
|
|
|
50
50
|
## 흐름과 상태
|
|
51
51
|
|
|
@@ -63,13 +63,13 @@ seed는 고정 타일의 위상만 이동한다. 새 타일 픽셀을 생성하
|
|
|
63
63
|
```tsx
|
|
64
64
|
// Web
|
|
65
65
|
import { EffectSurface } from "@hjmds/react/effect-surface";
|
|
66
|
-
<EffectSurface descriptor={{ layers: ["noise"], active: false }}><Content /></EffectSurface>
|
|
66
|
+
<EffectSurface descriptor={{ layers: ["noise"], active: false }} padding="lg"><Content /></EffectSurface>
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
```tsx
|
|
70
70
|
// Native
|
|
71
71
|
import { EffectSurface } from "@hjmds/react-native/effect-surface";
|
|
72
|
-
<EffectSurface descriptor={{ layers: ["noise"], active: false }}><Content /></EffectSurface>
|
|
72
|
+
<EffectSurface descriptor={{ layers: ["noise"], active: false }} padding="lg"><Content /></EffectSurface>
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
## 플랫폼 차이
|