@hjmds/design-contracts 1.12.1 → 1.13.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/avatar-fallback.d.ts +11 -0
- package/dist/avatar-fallback.d.ts.map +1 -1
- package/dist/avatar-fallback.js +21 -0
- package/dist/avatar-fallback.js.map +1 -1
- package/dist/base-recipes.d.ts +17 -0
- package/dist/base-recipes.d.ts.map +1 -1
- package/dist/base-recipes.js +17 -0
- package/dist/base-recipes.js.map +1 -1
- package/dist/catalog.d.ts +25 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/command-palette.d.ts +14 -9
- package/dist/command-palette.d.ts.map +1 -1
- package/dist/command-palette.js +8 -9
- package/dist/command-palette.js.map +1 -1
- package/dist/component-recipes.d.ts +17 -0
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +5 -0
- package/dist/component-recipes.js.map +1 -1
- package/dist/provider-button.d.ts.map +1 -1
- package/dist/provider-button.js +3 -0
- package/dist/provider-button.js.map +1 -1
- package/dist/reactions.d.ts +10 -0
- package/dist/reactions.d.ts.map +1 -1
- package/dist/reactions.js +7 -0
- package/dist/reactions.js.map +1 -1
- package/dist/screen-patterns.d.ts +147 -0
- package/dist/screen-patterns.d.ts.map +1 -0
- package/dist/screen-patterns.js +149 -0
- package/dist/screen-patterns.js.map +1 -0
- package/dist/slider.d.ts +8 -0
- package/dist/slider.d.ts.map +1 -1
- package/dist/slider.js +6 -1
- package/dist/slider.js.map +1 -1
- package/dist/upload-item.d.ts +5 -0
- package/dist/upload-item.d.ts.map +1 -1
- package/dist/upload-item.js +5 -0
- 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/action-session.md +3 -3
- package/docs/agreement.md +5 -0
- package/docs/avatar-fallback.md +7 -0
- package/docs/bottom-navigation.md +6 -0
- package/docs/brand-boundary.md +1 -1
- package/docs/button-label.md +6 -0
- package/docs/clipboard.md +3 -0
- package/docs/command-palette.md +45 -2
- package/docs/consumer-policy.md +5 -1
- package/docs/data-table.md +6 -4
- package/docs/dialog.md +8 -2
- package/docs/form.md +51 -0
- 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/link.md +8 -0
- package/docs/migration-native-legacy-removal.md +45 -1
- package/docs/optional-adapters.md +1 -1
- package/docs/password-field.md +5 -0
- package/docs/product-composition-adoption.md +40 -0
- package/docs/progress.md +19 -1
- package/docs/provider-button.md +13 -0
- package/docs/result.md +3 -0
- package/docs/screen-chrome.md +10 -0
- package/docs/screen-patterns.md +376 -0
- package/docs/sheet.md +12 -0
- package/docs/splitter.md +8 -2
- package/docs/theming.md +36 -29
- package/docs/toggle-group.md +7 -0
- package/docs/tour.md +7 -1
- package/docs/tree.md +5 -2
- package/docs/upload-item.md +7 -0
- package/docs/usage/README.md +236 -0
- package/docs/usage/STANDARD.md +108 -0
- package/docs/usage/components/accordion.md +107 -0
- package/docs/usage/components/activity-heatmap.md +104 -0
- package/docs/usage/components/affix.md +86 -0
- package/docs/usage/components/agreement.md +129 -0
- package/docs/usage/components/alert-dialog.md +130 -0
- package/docs/usage/components/anchor.md +96 -0
- package/docs/usage/components/aspect-ratio.md +89 -0
- package/docs/usage/components/asset.md +126 -0
- package/docs/usage/components/auth-provider-button.md +116 -0
- package/docs/usage/components/auth-screen-layout.md +129 -0
- package/docs/usage/components/avatar.md +114 -0
- package/docs/usage/components/badge.md +84 -0
- package/docs/usage/components/bottom-cta.md +125 -0
- package/docs/usage/components/bottom-info.md +99 -0
- package/docs/usage/components/bottom-navigation.md +136 -0
- package/docs/usage/components/breadcrumb.md +81 -0
- package/docs/usage/components/button.md +118 -0
- package/docs/usage/components/calendar.md +122 -0
- package/docs/usage/components/card.md +110 -0
- package/docs/usage/components/carousel.md +113 -0
- package/docs/usage/components/celebration.md +96 -0
- package/docs/usage/components/chat-message.md +122 -0
- package/docs/usage/components/chat-screen.md +112 -0
- package/docs/usage/components/checkbox-group.md +104 -0
- package/docs/usage/components/checkbox.md +103 -0
- package/docs/usage/components/chip.md +104 -0
- package/docs/usage/components/code-block.md +111 -0
- package/docs/usage/components/collapsible.md +112 -0
- package/docs/usage/components/color-picker.md +86 -0
- package/docs/usage/components/combobox.md +137 -0
- package/docs/usage/components/command-palette.md +125 -0
- package/docs/usage/components/comment-thread-screen.md +125 -0
- package/docs/usage/components/container.md +98 -0
- package/docs/usage/components/content-transition.md +101 -0
- package/docs/usage/components/context-menu.md +136 -0
- package/docs/usage/components/counter-badge.md +107 -0
- package/docs/usage/components/data-table.md +122 -0
- package/docs/usage/components/date-picker.md +142 -0
- package/docs/usage/components/date-range-picker.md +111 -0
- package/docs/usage/components/description-list.md +103 -0
- package/docs/usage/components/design-system-provider.md +124 -0
- package/docs/usage/components/dialog.md +176 -0
- package/docs/usage/components/divider.md +89 -0
- package/docs/usage/components/editor-screen.md +126 -0
- package/docs/usage/components/effect-surface.md +120 -0
- package/docs/usage/components/empty-state.md +114 -0
- package/docs/usage/components/field.md +129 -0
- package/docs/usage/components/file-picker.md +114 -0
- package/docs/usage/components/floating-action-button.md +138 -0
- package/docs/usage/components/form.md +162 -0
- package/docs/usage/components/grid.md +99 -0
- package/docs/usage/components/heading.md +87 -0
- package/docs/usage/components/icon-button.md +126 -0
- package/docs/usage/components/icon.md +105 -0
- package/docs/usage/components/image.md +122 -0
- package/docs/usage/components/keyboard-avoiding.md +93 -0
- package/docs/usage/components/keyboard-dock.md +110 -0
- package/docs/usage/components/keyboard-form-scroll-view.md +95 -0
- package/docs/usage/components/keyboard-motion-provider.md +86 -0
- package/docs/usage/components/layout.md +117 -0
- package/docs/usage/components/link.md +121 -0
- package/docs/usage/components/list-detail-screen.md +103 -0
- package/docs/usage/components/list-row.md +124 -0
- package/docs/usage/components/list.md +119 -0
- package/docs/usage/components/load-more.md +115 -0
- package/docs/usage/components/masonry.md +109 -0
- package/docs/usage/components/media-selection-screen.md +119 -0
- package/docs/usage/components/mentions.md +119 -0
- package/docs/usage/components/menu.md +129 -0
- package/docs/usage/components/menubar.md +93 -0
- package/docs/usage/components/message-composer.md +124 -0
- package/docs/usage/components/moderation-screen.md +113 -0
- package/docs/usage/components/notice.md +106 -0
- package/docs/usage/components/notification-inbox-screen.md +97 -0
- package/docs/usage/components/notification-item.md +98 -0
- package/docs/usage/components/number-field.md +131 -0
- package/docs/usage/components/onboarding-screen.md +106 -0
- package/docs/usage/components/otp-field.md +101 -0
- package/docs/usage/components/pagination.md +82 -0
- package/docs/usage/components/password-field.md +137 -0
- package/docs/usage/components/permission-screen.md +107 -0
- package/docs/usage/components/photo-source-sheet.md +119 -0
- package/docs/usage/components/popover.md +108 -0
- package/docs/usage/components/profile-screen.md +89 -0
- package/docs/usage/components/progress.md +122 -0
- package/docs/usage/components/qr-code.md +122 -0
- package/docs/usage/components/radio-group.md +124 -0
- package/docs/usage/components/radio.md +104 -0
- package/docs/usage/components/result.md +116 -0
- package/docs/usage/components/saved-items-screen.md +126 -0
- package/docs/usage/components/screen-layout.md +119 -0
- package/docs/usage/components/search-field.md +120 -0
- package/docs/usage/components/search-screen.md +215 -0
- package/docs/usage/components/section.md +111 -0
- package/docs/usage/components/segmented-control.md +138 -0
- package/docs/usage/components/select.md +142 -0
- package/docs/usage/components/settings-screen.md +126 -0
- package/docs/usage/components/shared-transition-element.md +111 -0
- package/docs/usage/components/shared-transition-screen.md +86 -0
- package/docs/usage/components/sheet.md +151 -0
- package/docs/usage/components/side-panel.md +104 -0
- package/docs/usage/components/sidebar.md +107 -0
- package/docs/usage/components/skeleton.md +105 -0
- package/docs/usage/components/skip-nav.md +76 -0
- package/docs/usage/components/slider.md +121 -0
- package/docs/usage/components/sortable-collection.md +127 -0
- package/docs/usage/components/spinner.md +86 -0
- package/docs/usage/components/splitter.md +103 -0
- package/docs/usage/components/stack.md +93 -0
- package/docs/usage/components/statistic.md +123 -0
- package/docs/usage/components/steps.md +110 -0
- package/docs/usage/components/surface.md +91 -0
- package/docs/usage/components/swipe-actions.md +124 -0
- package/docs/usage/components/switch.md +120 -0
- package/docs/usage/components/tabs.md +134 -0
- package/docs/usage/components/tag.md +84 -0
- package/docs/usage/components/tags-input.md +111 -0
- package/docs/usage/components/text-area.md +112 -0
- package/docs/usage/components/text-format.md +75 -0
- package/docs/usage/components/text-transition.md +104 -0
- package/docs/usage/components/text.md +101 -0
- package/docs/usage/components/thinking-orb.md +105 -0
- package/docs/usage/components/timeline.md +105 -0
- package/docs/usage/components/toast.md +145 -0
- package/docs/usage/components/toggle-group.md +95 -0
- package/docs/usage/components/tooltip.md +103 -0
- package/docs/usage/components/top-bar.md +124 -0
- package/docs/usage/components/top.md +89 -0
- package/docs/usage/components/tour.md +118 -0
- package/docs/usage/components/transfer-list.md +115 -0
- package/docs/usage/components/tree.md +91 -0
- package/docs/usage/components/upload-item.md +99 -0
- package/docs/usage/components/virtual-list.md +105 -0
- package/docs/usage/components/visually-hidden.md +72 -0
- package/docs/usage/components/watermark.md +78 -0
- package/docs/usage/compositions/action-recovery-optimistic.md +180 -0
- package/docs/usage/compositions/action-recovery-save.md +235 -0
- package/docs/usage/compositions/action-recovery-undo.md +193 -0
- package/docs/usage/compositions/common-message.md +132 -0
- package/docs/usage/compositions/common-notification.md +101 -0
- package/docs/usage/compositions/compound-controls.md +186 -0
- package/docs/usage/compositions/data-layouts.md +157 -0
- package/docs/usage/compositions/disclosure.md +144 -0
- package/docs/usage/compositions/environment-matrix.md +139 -0
- package/docs/usage/compositions/expo-interactions.md +149 -0
- package/docs/usage/compositions/family-drawer.md +201 -0
- package/docs/usage/compositions/floating-action-button.md +197 -0
- package/docs/usage/compositions/input-sheet.md +148 -0
- package/docs/usage/compositions/interaction-adapters.md +190 -0
- package/docs/usage/compositions/interaction-flow-apply.md +205 -0
- package/docs/usage/compositions/interaction-flow-draft.md +188 -0
- package/docs/usage/compositions/interaction-flow-search.md +171 -0
- package/docs/usage/compositions/native-renderers.md +106 -0
- package/docs/usage/compositions/navigation-bar-collection.md +164 -0
- package/docs/usage/compositions/optional-adapters.md +169 -0
- package/docs/usage/compositions/optional-motion.md +109 -0
- package/docs/usage/compositions/photo-source.md +104 -0
- package/docs/usage/compositions/purpose-input-comment.md +110 -0
- package/docs/usage/compositions/purpose-input-message.md +119 -0
- package/docs/usage/compositions/reference-first.md +96 -0
- package/docs/usage/compositions/reference-review.md +107 -0
- package/docs/usage/compositions/reference-settings.md +107 -0
- package/docs/usage/compositions/selection-scope.md +174 -0
- package/docs/usage/compositions/stea-event-ticket.md +166 -0
- package/docs/usage/compositions/stea-flip-card.md +162 -0
- package/docs/usage/compositions/stea-order-progress.md +184 -0
- package/docs/usage/compositions/stea-otp-verify.md +215 -0
- package/docs/usage/compositions/stea-pixel-empty.md +140 -0
- package/docs/usage/compositions/stea-schedule-card.md +169 -0
- package/docs/usage/compositions/stea-stat-summary.md +154 -0
- package/docs/usage/compositions/time-selection.md +174 -0
- package/docs/usage/compositions/toast-layout.md +128 -0
- package/docs/usage/compositions/visual-foundations.md +185 -0
- package/docs/usage/compositions/web-additions.md +146 -0
- package/docs/usage/compositions/web-navigation.md +143 -0
- package/docs/usage/screens/common-chat.md +127 -0
- package/docs/usage/screens/common-comments.md +108 -0
- package/docs/usage/screens/common-inbox.md +110 -0
- package/docs/usage/screens/common-login.md +98 -0
- package/docs/usage/screens/common-profile.md +221 -0
- package/docs/usage/screens/common-saved.md +127 -0
- package/docs/usage/screens/common-search.md +274 -0
- package/docs/usage/screens/common-settings.md +126 -0
- package/docs/usage/screens/common-shell.md +108 -0
- package/docs/usage/screens/dashboard.md +245 -0
- package/docs/usage/screens/discovery-gallery.md +306 -0
- package/docs/usage/screens/flow-collection.md +96 -0
- package/docs/usage/screens/flow-editor.md +120 -0
- package/docs/usage/screens/flow-media.md +111 -0
- package/docs/usage/screens/flow-moderation.md +120 -0
- package/docs/usage/screens/flow-onboarding.md +193 -0
- package/docs/usage/screens/flow-permission.md +103 -0
- package/docs/usage/screens/landing.md +347 -0
- package/docs/usage/screens/mockup-studio.md +190 -0
- package/docs/usage/screens/notification-settings.md +206 -0
- package/docs/usage/screens/reference-comparison.md +159 -0
- package/docs/usage/templates/component.md +61 -0
- package/docs/usage/templates/composition.md +47 -0
- package/docs/usage/templates/screen.md +56 -0
- package/docs/usage/templates/token.md +32 -0
- package/docs/usage/tokens/color.md +142 -0
- package/docs/usage/tokens/elevation-opacity.md +86 -0
- package/docs/usage/tokens/layers.md +98 -0
- package/docs/usage/tokens/layout.md +114 -0
- package/docs/usage/tokens/motion.md +88 -0
- package/docs/usage/tokens/radius.md +53 -0
- package/docs/usage/tokens/size.md +74 -0
- package/docs/usage/tokens/spacing.md +73 -0
- package/docs/usage/tokens/stroke.md +50 -0
- package/docs/usage/tokens/theme-studio.md +70 -0
- package/docs/usage/tokens/typography-studio.md +70 -0
- package/docs/usage/tokens/typography.md +89 -0
- package/package.json +7 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# 알림 설정
|
|
2
|
+
|
|
3
|
+
- 단계: 화면
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: `showcase/web/src/patterns/NotificationSettings.stories.tsx`, `showcase/native/src/NotificationSettings.stories.tsx`, `src/component-recipes.ts`(`topBarRecipe`·`bottomCtaRecipe`·`sectionRecipe`·`switchRecipe`), `src/container.ts`, `src/responsive.ts`(`resolveWindowClass`), `packages/react/src/styles.css`(`.hjm-top-bar`·`.hjm-bottom-cta`)
|
|
9
|
+
- 스토리북: `배포/화면/설정/알림 설정`
|
|
10
|
+
|
|
11
|
+
## 목적
|
|
12
|
+
|
|
13
|
+
알림 종류 몇 개를 스위치로 켜고 끈 뒤 하단 버튼 하나로 저장하는 설정 화면이다. 상단 바 · 읽기 폭 본문 · 하단 행동 바의
|
|
14
|
+
세 층으로 된 화면 골격의 기준 예다. 스위치를 바꾸는 즉시 서버에 반영하는 설정이면 하단 저장 바를 빼고 각 Switch의
|
|
15
|
+
`onCheckedChange`에서 저장한다.
|
|
16
|
+
|
|
17
|
+
## 영역 구조
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
좁은 폭(Native·모바일 Web) — 상단·하단 바 고정, 본문만 스크롤
|
|
21
|
+
┌ 상단 안전 영역(TopBar safeAreaTop, Web은 env()와 큰 값) ┐
|
|
22
|
+
│ ① TopBar 알림 설정 │ 최소 높이 52, 좌우 spacing.md 16 (고정)
|
|
23
|
+
├──────────────────────────────────────────────┤ ↕ spacing.lg 20
|
|
24
|
+
│ ② Container reading (최대 720, │ ─ 스크롤 ─
|
|
25
|
+
│ 좌우 gutter: 폭<600 compact 16 · 이상 20) │
|
|
26
|
+
│ Section │
|
|
27
|
+
│ 필요한 소식만 받아요 ← title │ title-description spacing.xxs 4
|
|
28
|
+
│ 원하는 알림을 골라 주세요… ← description │ 머리-내용 spacing.xs 8
|
|
29
|
+
│ ┌────────────────────────────────────────┐ │
|
|
30
|
+
│ │ 내 활동 알림 (●──) │ │ Switch presentation=row
|
|
31
|
+
│ │ 댓글과 답글이 도착하면… │ │ 라벨 왼쪽, 스위치 끝
|
|
32
|
+
│ │ ↕ spacing.md 16 │ │
|
|
33
|
+
│ │ 주간 모아보기 (──○) │ │
|
|
34
|
+
│ │ 일주일의 소식을 한 번에… │ │
|
|
35
|
+
│ └────────────────────────────────────────┘ │
|
|
36
|
+
│ ┌ Notice success(저장 후) · danger(실패) ┐ │
|
|
37
|
+
│ └────────────────────────────────────────┘ │
|
|
38
|
+
├──────────────────────────────────────────────┤ ↕ 20
|
|
39
|
+
│ ③ BottomCTA (위 테두리 1 + 위쪽 그림자) │ 최소 높이 64 (고정)
|
|
40
|
+
│ 선택한 알림만 보내드릴게요. ← description │ caption, muted
|
|
41
|
+
│ [ 이 설정으로 저장하기 ] │ ← 주 행동, 폭 전체
|
|
42
|
+
└ 하단 안전 영역(BottomCTA safeAreaBottom) ────┘
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
넓은 폭 Web: TopBar와 BottomCTA는 화면 폭 전체, 본문만 Container `reading`으로 가운데 720 폭에 묶인다.
|
|
46
|
+
|
|
47
|
+
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
48
|
+
| --- | --- | --- | --- |
|
|
49
|
+
| 바깥 틀 | Web `main` > `Stack gap="lg"` > TopBar · Container · BottomCTA `position="sticky"`, 문서 스크롤 · Native `View`(`flex: 1`) > TopBar · `ScrollView contentContainerStyle={{ paddingVertical: spacing.lg }}` > Container · BottomCTA | 화면 전체. 본문만 스크롤하고 하단 바는 고정(Web TopBar는 고정 prop이 없어 문서와 함께 움직이며, 고정하려면 제품 레이아웃이 맡는다) | 세 층 사이 `spacing.lg` 20(Web Stack gap, Native ScrollView 위아래 여백). 좌우 여백은 Container gutter. 안전 영역: 위는 TopBar `safeAreaTop`, 아래는 BottomCTA `safeAreaBottom`(Native는 기기 inset을 넘긴다, 기본 0). 키보드 입력이 없어 키보드 처리는 없다 |
|
|
50
|
+
| ① 상단 바 | TopBar `title` | 맨 위 | 최소 높이 52(`control.buttonHeight.large`), 좌우 `spacing.md` 16, 슬롯 사이 `spacing.xs` 8, 양 끝 슬롯 최소 44 |
|
|
51
|
+
| ② 본문 | Container `size="reading"` `gutter={gutter}` > Section `title` `description` > Stack `gap="md"` > Switch `presentation="row"` ×n, 그 아래 Notice | 상단 바 아래, 스크롤 | 최대 폭 720(`layout.readingMaxWidth`), 좌우 gutter 폭 600 미만 `compact` 16 · 이상 `regular` 20(`layout.pagePadding`), 스위치 사이 `spacing.md` 16, 트랙 52×32 |
|
|
52
|
+
| ③ 하단 행동 | BottomCTA `description` `primaryAction` | 맨 아래 고정. Web `position="sticky"`(`inset-block-end: 0`, `z-index: var(--hjm-layer-sticky)`), Native는 `ScrollView` 바깥 | 최소 높이 64, 위아래 `spacing.sm` 12 + 안전 영역(`max(safeAreaBottom, spacing.sm 12)`), 좌우 `layout.pagePadding.regular` 20, 설명-버튼 `spacing.sm` 12 |
|
|
53
|
+
|
|
54
|
+
gutter는 창 폭으로 고른다: `resolveWindowClass(width) === "compact" ? "compact" : "regular"`([화면 여백과 너비](../tokens/layout.md)).
|
|
55
|
+
상단·스크롤 본문·하단 행동은 별도 영역으로 둔다. 목록이 길어져도 저장 행동이 본문과 함께 밀려나지 않는다.
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
Native 고정 배치
|
|
59
|
+
┌ TopBar safeAreaTop={insets.top} ─────┐ 고정
|
|
60
|
+
├ ScrollView paddingVertical 20 ───────┤
|
|
61
|
+
│ Container > Section > Switch … │ 스크롤
|
|
62
|
+
├ BottomCTA safeAreaBottom={insets.bottom} ┤ 고정
|
|
63
|
+
└──────────────────────────────────────┘
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 버튼과 행동 위치
|
|
67
|
+
|
|
68
|
+
| 행동 | 컴포넌트·tone | 위치 | 개수·순서 |
|
|
69
|
+
| --- | --- | --- | --- |
|
|
70
|
+
| 저장(주 행동) | BottomCTA `primaryAction`(primary, 폭 전체) | 화면 맨 아래 바 | 1. 저장 중 `loading`, 저장 뒤에는 라벨을 `t("settings.saved")`로 바꾸고 `disabled` |
|
|
71
|
+
| 보조 행동 | BottomCTA `secondaryAction`(기본 `secondary`) | 같은 바, 주 행동 앞(왼쪽) | 스토리에는 없음. 둘 다 있으면 [보조][주] 같은 폭으로 나뉜다 |
|
|
72
|
+
| 항목 켜기·끄기 | Switch `presentation="row"` 행 전체 | 본문 Section 안 | 항목 수만큼, 위→아래 |
|
|
73
|
+
| 다시 불러오기 | Notice `action` > Button `tone="secondary" size="small"` | 불러오기 실패 Notice 안 | 스토리에 없음. 1 |
|
|
74
|
+
| 뒤로 | TopBar `leading` | 상단 바 시작 끝 | 스토리에는 없음. 하위 설정 화면이면 1 |
|
|
75
|
+
| 파괴 행동 | — | — | 없음 |
|
|
76
|
+
|
|
77
|
+
## 상태
|
|
78
|
+
|
|
79
|
+
| 상태 | 화면 모습 | 행동 |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| 기본 | 스위치가 현재 값, 저장 버튼 활성 | 스위치 바꾸기, 저장 |
|
|
82
|
+
| 로딩 | 스토리에 없음. 첫 값을 불러오는 동안 Section 안 스위치 자리에 [Skeleton](../components/skeleton.md) `shape="text"`, 저장 버튼 `disabled`. 저장 중이면 `primaryAction`에 `loading`(누름을 막고 같은 자리에 스피너) | 기다림 |
|
|
83
|
+
| 빈 | 해당 없음(설정 항목은 항상 있다). 항목이 조건부면 Section 아래 안내 Text | — |
|
|
84
|
+
| 오류 | 스토리에 없음. **불러오기 실패**: 스위치 대신 [Notice](../components/notice.md) `tone="danger"` + `action` 다시 시도, 저장 버튼 `disabled`. **저장 실패**: Section 끝에 Notice `tone="danger"`(action 없음 — 다시 시도는 저장 버튼이 맡는다), 사용자가 고른 스위치 값을 그대로 두고 `loading`만 푼다. 문구는 네트워크 실패 `settings.error.offline`·서버 실패 `settings.error.server`로 나눈다. 다시 시도가 또 실패하면 같은 Notice가 같은 자리에 남고 문구만 갱신한다. Native는 `announcement="assertive"`(기본 `none`) | 다시 시도 / 다시 저장 |
|
|
85
|
+
| 저장됨 | Section 끝에 Notice `tone="success"`(제목 + 켜짐/꺼짐 요약). 버튼은 "저장했어요"로 바뀌고 비활성. Native Notice `announcement="polite"` | 스위치를 다시 바꾸면 저장 상태가 풀린다 |
|
|
86
|
+
|
|
87
|
+
## 사용하는 지침
|
|
88
|
+
|
|
89
|
+
| 지침 | 쓰는 곳 |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| [TopBar](../components/top-bar.md) | ① 상단 바 |
|
|
92
|
+
| [Container](../components/container.md) | ② 읽기 폭 |
|
|
93
|
+
| [Section](../components/section.md) | ② 설정 묶음 제목·설명 |
|
|
94
|
+
| [Switch](../components/switch.md) | ② 항목 행 |
|
|
95
|
+
| [Notice](../components/notice.md) | 저장 결과·오류 |
|
|
96
|
+
| [Skeleton](../components/skeleton.md) | 첫 값 불러오기 |
|
|
97
|
+
| [BottomCTA](../components/bottom-cta.md) | ③ 하단 저장 |
|
|
98
|
+
| [Stack](../components/stack.md) | 세 층 사이, 스위치 목록 |
|
|
99
|
+
| [화면 여백과 너비](../tokens/layout.md) | `layout.readingMaxWidth`, `layout.pagePadding.compact`·`regular`, `resolveWindowClass` |
|
|
100
|
+
| [간격](../tokens/spacing.md) | `spacing.xxs`·`xs`·`sm`·`md`·`lg` |
|
|
101
|
+
|
|
102
|
+
## 코드 골격
|
|
103
|
+
|
|
104
|
+
설정 항목·문구는 제품 소유다. 문구 키는 상태→키 표로 고르고 템플릿 문자열로 만들지 않는다.
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
// Web
|
|
108
|
+
import { TopBar } from "@hjmds/react/top-bar";
|
|
109
|
+
import { BottomCTA } from "@hjmds/react/bottom-cta";
|
|
110
|
+
import { Container, Stack, Section } from "@hjmds/react/layout";
|
|
111
|
+
import { Switch } from "@hjmds/react/selection";
|
|
112
|
+
import { Notice, Skeleton } from "@hjmds/react/feedback";
|
|
113
|
+
import { Button } from "@hjmds/react/actions";
|
|
114
|
+
import { resolveWindowClass } from "@hjmds/design-contracts/responsive";
|
|
115
|
+
|
|
116
|
+
const failureKey = { offline: "settings.error.offline", server: "settings.error.server" } as const;
|
|
117
|
+
const onOffKey = { on: "settings.on", off: "settings.off" } as const;
|
|
118
|
+
|
|
119
|
+
const gutter = resolveWindowClass(window.innerWidth) === "compact" ? "compact" : "regular";
|
|
120
|
+
const summary = t("settings.saved.summary", {
|
|
121
|
+
activity: t(onOffKey[activity ? "on" : "off"]),
|
|
122
|
+
digest: t(onOffKey[digest ? "on" : "off"]),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
<main><Stack gap="lg">
|
|
126
|
+
<TopBar title={t("settings.notifications.title")} />
|
|
127
|
+
<Container size="reading" gutter={gutter}>
|
|
128
|
+
<Section title={t("settings.notifications.section")} description={t("settings.notifications.hint")}>
|
|
129
|
+
{status === "loading" ? <Stack gap="md"><Skeleton shape="text" width="60%" /><Skeleton shape="text" width="40%" /></Stack> : null}
|
|
130
|
+
{status === "loadFailed" && failure ? <Notice tone="danger" title={t(failureKey[failure])}
|
|
131
|
+
action={<Button tone="secondary" size="small" onClick={reload}>{t("common.retry")}</Button>} /> : null}
|
|
132
|
+
{status === "ready" ? <Stack gap="md">
|
|
133
|
+
<Switch presentation="row" label={t("settings.activity")} description={t("settings.activity.hint")}
|
|
134
|
+
checked={activity} onCheckedChange={v => { setActivity(v); setSaved(false); }} />
|
|
135
|
+
</Stack> : null}
|
|
136
|
+
{status === "ready" && failure ? <Notice tone="danger" title={t(failureKey[failure])} description={t("settings.error.kept")} /> : null}
|
|
137
|
+
{saved ? <Notice tone="success" title={t("settings.saved.title")} description={summary} /> : null}
|
|
138
|
+
</Section>
|
|
139
|
+
</Container>
|
|
140
|
+
<BottomCTA position="sticky" description={t("settings.cta.hint")}
|
|
141
|
+
primaryAction={{ label: saved ? t("settings.saved") : t("settings.save"), onClick: save, loading: saving, disabled: saved || status !== "ready" }} />
|
|
142
|
+
</Stack></main>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
// Native — 상단·하단 바를 고정한 형태
|
|
147
|
+
import { ScrollView, View, useWindowDimensions } from "react-native";
|
|
148
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context"; // 제품 의존성
|
|
149
|
+
import { spacing } from "@hjmds/design-contracts/foundations";
|
|
150
|
+
import { resolveWindowClass } from "@hjmds/design-contracts/responsive";
|
|
151
|
+
import { TopBar } from "@hjmds/react-native/top-bar";
|
|
152
|
+
import { BottomCTA } from "@hjmds/react-native/bottom-cta";
|
|
153
|
+
import { Container, Stack, Section } from "@hjmds/react-native/primitives";
|
|
154
|
+
import { Switch } from "@hjmds/react-native/inputs";
|
|
155
|
+
import { Notice } from "@hjmds/react-native/feedback";
|
|
156
|
+
|
|
157
|
+
const failureKey = { offline: "settings.error.offline", server: "settings.error.server" } as const;
|
|
158
|
+
const insets = useSafeAreaInsets();
|
|
159
|
+
const { width } = useWindowDimensions();
|
|
160
|
+
const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
161
|
+
|
|
162
|
+
<View style={{ flex: 1 }}>
|
|
163
|
+
<TopBar title={t("settings.notifications.title")} safeAreaTop={insets.top} />
|
|
164
|
+
<ScrollView contentContainerStyle={{ paddingVertical: spacing.lg }}>
|
|
165
|
+
<Container size="reading" gutter={gutter}>
|
|
166
|
+
<Section title={t("settings.notifications.section")} description={t("settings.notifications.hint")}>
|
|
167
|
+
<Stack gap="md">
|
|
168
|
+
<Switch presentation="row" label={t("settings.activity")} description={t("settings.activity.hint")}
|
|
169
|
+
checked={activity} onCheckedChange={v => { setActivity(v); setSaved(false); }} />
|
|
170
|
+
</Stack>
|
|
171
|
+
{failure ? <Notice announcement="assertive" tone="danger" title={t(failureKey[failure])} description={t("settings.error.kept")} /> : null}
|
|
172
|
+
{saved ? <Notice announcement="polite" tone="success" title={t("settings.saved.title")} description={summary} /> : null}
|
|
173
|
+
</Section>
|
|
174
|
+
</Container>
|
|
175
|
+
</ScrollView>
|
|
176
|
+
<BottomCTA safeAreaBottom={insets.bottom} description={t("settings.cta.hint")}
|
|
177
|
+
primaryAction={{ label: saved ? t("settings.saved") : t("settings.save"), onPress: save, loading: saving, disabled: saved }} />
|
|
178
|
+
</View>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`react-native-safe-area-context`는 HJM 의존성이 아니다. 제품이 쓰는 inset 출처를 넘긴다. Native 로딩·불러오기 실패는 Web과 같은
|
|
182
|
+
자리에 같은 컴포넌트를 둔다(Button은 `onPress`).
|
|
183
|
+
|
|
184
|
+
## 큰 글자·다크·좁은 폭
|
|
185
|
+
|
|
186
|
+
| 조건 | 바뀌는 것 |
|
|
187
|
+
| --- | --- |
|
|
188
|
+
| 큰 글자(글자 배율 ≥ 1.6, `largeTextThreshold`) | TopBar 제목이 둘째 줄로 내려가 시작 정렬된다. BottomCTA 버튼이 세로로 쌓이고 **주 행동이 위**(`column-reverse`). Switch 행은 설명이 좁은 열이 되지 않게 세로로 쌓인다 |
|
|
189
|
+
| 다크 | 테마 토큰만 쓴다. Switch 꺼짐 상태는 트랙·손잡이 테두리(`trackOffBorder`·`thumbOffBorder`)로 다크에서도 모양이 보인다 |
|
|
190
|
+
| 좁은 폭 | 폭 600 미만이면 Container gutter `compact` 16. Section 머리는 Web 640 이하에서 제목·행동을 세로로 쌓는다 |
|
|
191
|
+
| 넓은 폭 Web | 본문만 720 폭 가운데, 상단·하단 바는 전체 폭 |
|
|
192
|
+
|
|
193
|
+
## 플랫폼 차이
|
|
194
|
+
|
|
195
|
+
| 항목 | Web | Native |
|
|
196
|
+
| --- | --- | --- |
|
|
197
|
+
| Switch 기본 배치 | `inline`(행으로 쓰려면 `presentation="row"` 명시) | `row` |
|
|
198
|
+
| 하단 바 고정 | `position="sticky"` | prop 없음 — `ScrollView` 바깥에 둔다 |
|
|
199
|
+
| 안전 영역 | CSS `env(safe-area-inset-*)`와 `safeAreaTop`/`safeAreaBottom` 중 큰 값 | `safeAreaTop`/`safeAreaBottom`만(기본 0) |
|
|
200
|
+
| 저장·오류 알림 | Notice가 `role="status"` | Notice `announcement` 명시(기본 `none`): 저장됨 `polite`, 오류 `assertive` |
|
|
201
|
+
| 스토리 상태 | 기본·큰 글자(어두운 테마 스토리 없음) | 기본만 |
|
|
202
|
+
|
|
203
|
+
## 함정
|
|
204
|
+
|
|
205
|
+
- Native에서 `safeAreaBottom`을 넘기지 않으면 홈 인디케이터가 저장 버튼 위에 겹친다. 기본값은 0이다.
|
|
206
|
+
- 현재 스토리는 저장 요약 문구를 템플릿 문자열(`내 활동 ${…}`)로 조립한다. 제품은 위 코드처럼 켜짐/꺼짐 키 표와 보간 값을 쓴다.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# 기록 표현 비교
|
|
2
|
+
|
|
3
|
+
- 단계: 화면
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 미게시(1.12.1 이후)
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/표현 비교/같은 기록의 세 가지 구성`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
|
|
9
|
+
- 스토리북: `배포/화면/화면 틀과 도구/기록 표현 비교`
|
|
10
|
+
|
|
11
|
+
## 목적
|
|
12
|
+
|
|
13
|
+
ScreenLayout을 사용해 같은 기록의 세 가지 구성 흐름을 구성한다. 제품이 데이터·권한·서버 확정·문구를 공급하며, 예제의 메모리 저장을 운영 저장으로 취급하지 않는다.
|
|
14
|
+
|
|
15
|
+
## 영역 구조
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
host: 남은 높이·safe area·키보드
|
|
19
|
+
└─ 표현 방식 선택 → 검색 → 같은 데이터 목록·읽기·이미지 표현
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
23
|
+
| --- | --- | --- | --- |
|
|
24
|
+
| 바깥 틀 | ScreenLayout | route 본문 | [API 배치 규칙](../components/screen-layout.md#배치), host 남은 높이 |
|
|
25
|
+
| 내용 | Stack `gap="xl"` > SegmentedControl → SearchField → 상태 문구 → 카드(Surface `padding="lg"`) | 표현 방식 선택 → 검색 → 같은 데이터 목록·읽기·이미지 표현 | 구획 사이 `spacing.xl` 24. 목록·읽기는 Stack `gap="lg"` 한 열, 이미지는 Grid 두 열(`minColumnWidth` 240 = `spacing.xxxl` 40 × 6) |
|
|
26
|
+
| 상태 | state 또는 해당 API 상태 | 본문 자리·비차단 notice | 입력 중 실패는 본문 높이와 초안을 유지 |
|
|
27
|
+
|
|
28
|
+
## 버튼과 행동 위치
|
|
29
|
+
|
|
30
|
+
| 행동 | 컴포넌트·tone | 위치 | 개수·순서 |
|
|
31
|
+
| --- | --- | --- | --- |
|
|
32
|
+
| 보기 방식 | SegmentedControl(`label` 보기 방식) | 검색 위 | 세 값, 항상 하나 선택 |
|
|
33
|
+
| 읽기 · 저장 | Button secondary · Button secondary `selected` | 카드 아래 | 카드마다 [읽기][저장] |
|
|
34
|
+
| 검색 초기화 | EmptyState `action` > Button secondary | 결과 없음 자리 | 하나. 이 화면에는 primary가 없다 |
|
|
35
|
+
| 읽기 마치기 | Button(Sheet footer) | 상세 Sheet | 하나 |
|
|
36
|
+
|
|
37
|
+
## 상태
|
|
38
|
+
|
|
39
|
+
| 상태 | 화면 모습 | 행동 |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| 기본 | 표현 방식 선택 → 검색 → 같은 데이터 목록·읽기·이미지 표현 | 각 공개 콜백을 제품 상태에 연결 |
|
|
42
|
+
| 로딩 | 최초 조회는 본문 상태, 저장은 해당 행동 pending | 중복 제출 차단; 성공을 먼저 표시하지 않음 |
|
|
43
|
+
| 빈 | 검색 결과 0건: EmptyState `compact`(상태 문구는 0개로 남는다) | `검색 초기화` |
|
|
44
|
+
| 오류 | 데모 저장은 메모리에서만 유지; 표현 전환은 검색·저장 상태를 초기화하지 않음 | 실패 원인과 재시도 경로 제공 |
|
|
45
|
+
|
|
46
|
+
## 사용하는 지침
|
|
47
|
+
|
|
48
|
+
| 지침 | 쓰는 곳 |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| [ScreenLayout](../components/screen-layout.md) | 필수 props·슬롯·플랫폼 차이 |
|
|
51
|
+
| [Button](../components/button.md) | 동작·로딩·보조 행동 |
|
|
52
|
+
| [SearchField](../components/search-field.md) | 같은 기록 검색 |
|
|
53
|
+
| [SegmentedControl](../components/segmented-control.md) | 보기 방식 전환 |
|
|
54
|
+
| [Heading](../components/heading.md) | 카드 제목(읽기 중심 `level4`, 나머지 `level5`) |
|
|
55
|
+
| [Grid](../components/grid.md) | 이미지 중심 두 열 |
|
|
56
|
+
| [EmptyState](../components/empty-state.md) | 결과 없음 |
|
|
57
|
+
|
|
58
|
+
## 코드 골격
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
// Web
|
|
62
|
+
import { Button } from "@hjmds/react/actions";
|
|
63
|
+
import { SearchField } from "@hjmds/react/forms";
|
|
64
|
+
import { EmptyState } from "@hjmds/react/feedback";
|
|
65
|
+
import { Heading } from "@hjmds/react/heading";
|
|
66
|
+
import { Stack, Surface, Text } from "@hjmds/react/layout";
|
|
67
|
+
import { SegmentedControl } from "@hjmds/react/selection";
|
|
68
|
+
import { ScreenLayout } from "@hjmds/react/screens";
|
|
69
|
+
|
|
70
|
+
// 보기 방식 id → 문구 키 표. 템플릿으로 만든 키는 키 추출·누락 검사가 찾지 못한다.
|
|
71
|
+
const modeKey = { list: "records.mode.list", reading: "records.mode.reading", image: "records.mode.image" } as const;
|
|
72
|
+
type Mode = keyof typeof modeKey;
|
|
73
|
+
|
|
74
|
+
<ScreenLayout
|
|
75
|
+
title={t("records.compare.title")}
|
|
76
|
+
description={t("records.compare.intro")}
|
|
77
|
+
state={loading ? { kind: "loading", title: t("records.loading") } : { kind: "ready" }}
|
|
78
|
+
>
|
|
79
|
+
<Stack gap="xl">
|
|
80
|
+
<SegmentedControl label={t("records.mode.label")} value={mode} onValueChange={(value) => setMode(value as Mode)}
|
|
81
|
+
items={(Object.keys(modeKey) as Mode[]).map((value) => ({ value, label: t(modeKey[value]) }))} />
|
|
82
|
+
<SearchField label={t("records.search")} clearLabel={t("common.clearSearch")} value={query} onValueChange={setQuery} />
|
|
83
|
+
<Text role="status">{t("records.count", { count: results.length })}</Text>
|
|
84
|
+
{results.map((record) => (
|
|
85
|
+
<Surface key={record.id} padding="lg">
|
|
86
|
+
<Stack gap={mode === "list" ? "sm" : "lg"}>
|
|
87
|
+
<Text tone="muted" variant="caption">{record.category}</Text>
|
|
88
|
+
<Heading level={mode === "reading" ? "level4" : "level5"} semanticLevel={2}>{record.title}</Heading>
|
|
89
|
+
{mode !== "list" ? <Text>{record.body}</Text> : null}
|
|
90
|
+
<Stack axis="inline" wrap gap="sm">
|
|
91
|
+
<Button tone="secondary" onClick={() => openRecord(record.id)}>{t("records.read", { title: record.title })}</Button>
|
|
92
|
+
<Button tone="secondary" selected={savedIds.includes(record.id)} onClick={() => toggleSaved(record.id)}>{t("records.save")}</Button>
|
|
93
|
+
</Stack>
|
|
94
|
+
</Stack>
|
|
95
|
+
</Surface>
|
|
96
|
+
))}
|
|
97
|
+
{results.length === 0 ? <EmptyState density="compact" title={t("records.empty")}
|
|
98
|
+
action={<Button tone="secondary" onClick={() => setQuery("")}>{t("records.resetSearch")}</Button>} /> : null}
|
|
99
|
+
</Stack>
|
|
100
|
+
</ScreenLayout>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
// Native
|
|
105
|
+
import { Button } from "@hjmds/react-native/actions";
|
|
106
|
+
import { EmptyState } from "@hjmds/react-native/feedback";
|
|
107
|
+
import { Heading } from "@hjmds/react-native/heading";
|
|
108
|
+
import { SearchField, SegmentedControl } from "@hjmds/react-native/inputs";
|
|
109
|
+
import { Stack, Surface, Text } from "@hjmds/react-native/primitives";
|
|
110
|
+
import { ScreenLayout } from "@hjmds/react-native/screens";
|
|
111
|
+
|
|
112
|
+
const modeKey = { list: "records.mode.list", reading: "records.mode.reading", image: "records.mode.image" } as const;
|
|
113
|
+
type Mode = keyof typeof modeKey;
|
|
114
|
+
|
|
115
|
+
<ScreenLayout
|
|
116
|
+
title={t("records.compare.title")}
|
|
117
|
+
description={t("records.compare.intro")}
|
|
118
|
+
state={loading ? { kind: "loading", title: t("records.loading") } : { kind: "ready" }}
|
|
119
|
+
>
|
|
120
|
+
<Stack gap="xl">
|
|
121
|
+
<SegmentedControl label={t("records.mode.label")} value={mode} onValueChange={setMode}
|
|
122
|
+
items={(Object.keys(modeKey) as Mode[]).map((value) => ({ value, label: t(modeKey[value]) }))} />
|
|
123
|
+
<SearchField label={t("records.search")} clearLabel={t("common.clearSearch")} busyLabel={t("common.searching")} value={query} onValueChange={setQuery} />
|
|
124
|
+
<Text accessibilityRole="text" accessibilityLiveRegion="polite">{t("records.count", { count: results.length })}</Text>
|
|
125
|
+
{results.map((record) => (
|
|
126
|
+
<Surface key={record.id} padding="lg">
|
|
127
|
+
<Stack gap={mode === "list" ? "sm" : "lg"}>
|
|
128
|
+
<Text tone="muted" variant="caption">{record.category}</Text>
|
|
129
|
+
<Heading level={mode === "reading" ? "level4" : "level5"}>{record.title}</Heading>
|
|
130
|
+
{mode !== "list" ? <Text>{record.body}</Text> : null}
|
|
131
|
+
<Stack axis="inline" wrap gap="sm">
|
|
132
|
+
<Button tone="secondary" onPress={() => openRecord(record.id)}>{t("records.read", { title: record.title })}</Button>
|
|
133
|
+
<Button tone="secondary" selected={savedIds.includes(record.id)} onPress={() => toggleSaved(record.id)}>{t("records.save")}</Button>
|
|
134
|
+
</Stack>
|
|
135
|
+
</Stack>
|
|
136
|
+
</Surface>
|
|
137
|
+
))}
|
|
138
|
+
{results.length === 0 ? <EmptyState density="compact" announcement="polite" title={t("records.empty")}
|
|
139
|
+
action={<Button tone="secondary" onPress={() => setQuery("")}>{t("records.resetSearch")}</Button>} /> : null}
|
|
140
|
+
</Stack>
|
|
141
|
+
</ScreenLayout>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
기록·검색·저장은 제품 상태다. 표현 전환은 같은 `results`를 다시 그리기만 하고 검색어·저장 상태를 초기화하지 않는다. 콜백·데이터·지역화 함수는 제품에서 공급한다. Web·Native import와 필수 props는 위 API 지침에서 확인한다.
|
|
145
|
+
|
|
146
|
+
## 큰 글자·다크·좁은 폭
|
|
147
|
+
|
|
148
|
+
| 조건 | 바뀌는 것 |
|
|
149
|
+
| --- | --- |
|
|
150
|
+
| 큰 글자 | 2배 글자에서 제목·행은 내용 높이로 증가. footer·닫기·입력 필드가 겹치지 않는지 확인 |
|
|
151
|
+
| 다크 | semantic 색으로 내용과 표면을 함께 전환; 예제 브랜드 색을 제품 기본값으로 복사하지 않음 |
|
|
152
|
+
| 좁은 폭 | 320px부터 한 열로 읽기 순서 유지. 가상화 본문은 scroll=content, 중첩 스크롤 금지 |
|
|
153
|
+
| 키보드 | Native host가 safe area와 키보드를 한 번 처리; Web은 포커스된 입력과 footer 가림 확인 |
|
|
154
|
+
|
|
155
|
+
## 함정
|
|
156
|
+
|
|
157
|
+
- 데모 저장은 메모리에서만 유지; 표현 전환은 검색·저장 상태를 초기화하지 않음.
|
|
158
|
+
- 결과 개수 문구는 Web `Text role="status"`, Native는 iOS 알림을 보내는 live region(예제의 `PatternStatus`)으로 둔다. Native `accessibilityLiveRegion`만으로는 iOS에서 읽히지 않는다.
|
|
159
|
+
- Storybook은 실제 서버·OS 권한·라우터 연동 증거가 아니다. 기본·다크·큰 글자와 실패/복구를 각각 확인한다.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# ComponentName
|
|
2
|
+
|
|
3
|
+
- 단계: 컴포넌트
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: YYYY-MM-DD
|
|
8
|
+
- 근거: `../../<계약 문서>.md`, `src/file.ts`
|
|
9
|
+
- 스토리북: `배포/컴포넌트/...`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
<한 문장 요약>다.
|
|
14
|
+
|
|
15
|
+
## 쓰지 않을 때
|
|
16
|
+
|
|
17
|
+
| 상황 | 대신 쓸 것 |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
|
|
20
|
+
## 공개 이름과 import
|
|
21
|
+
|
|
22
|
+
| 이름 | 역할 | Web | Native |
|
|
23
|
+
| --- | --- | --- | --- |
|
|
24
|
+
|
|
25
|
+
## 최소 사용 예
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
// Web
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
// Native
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 축과 기본값
|
|
36
|
+
|
|
37
|
+
| prop | 값 | 기본값 | 설명 |
|
|
38
|
+
| --- | --- | --- | --- |
|
|
39
|
+
|
|
40
|
+
## 배치
|
|
41
|
+
|
|
42
|
+
| 항목 | 값 | 근거 |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| 크기 | | |
|
|
45
|
+
| 간격 | | |
|
|
46
|
+
| 순서·정렬 | | |
|
|
47
|
+
| 고정·스크롤 | | |
|
|
48
|
+
| 좁은 폭·큰 글자 | | |
|
|
49
|
+
|
|
50
|
+
## 꼭 지킬 것
|
|
51
|
+
|
|
52
|
+
-
|
|
53
|
+
|
|
54
|
+
## 플랫폼 차이
|
|
55
|
+
|
|
56
|
+
| 항목 | Web | Native |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
|
|
59
|
+
## 함정
|
|
60
|
+
|
|
61
|
+
-
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 이름
|
|
2
|
+
|
|
3
|
+
- 단계: 구성
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: YYYY-MM-DD
|
|
8
|
+
- 근거: `../../<계약 문서>.md`, `src/file.ts`
|
|
9
|
+
- 스토리북: `배포/구성/...`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
<한 문장 요약>다.
|
|
14
|
+
|
|
15
|
+
## 구성 요소
|
|
16
|
+
|
|
17
|
+
| 컴포넌트 | 역할 | 지침 |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
|
|
20
|
+
## 배치
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
26
|
+
| --- | --- | --- | --- |
|
|
27
|
+
| 바깥 틀 | | | |
|
|
28
|
+
|
|
29
|
+
## 흐름과 상태
|
|
30
|
+
|
|
31
|
+
1.
|
|
32
|
+
|
|
33
|
+
| 상태 | 모습 | 포커스·알림 |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| 기본 | | |
|
|
36
|
+
| 진행 중 | | |
|
|
37
|
+
| 실패 | | |
|
|
38
|
+
|
|
39
|
+
## 코드 골격
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
// Web
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
// Native
|
|
47
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 이름
|
|
2
|
+
|
|
3
|
+
- 단계: 화면
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: YYYY-MM-DD
|
|
8
|
+
- 근거: `../../<계약 문서>.md`, `src/file.ts`
|
|
9
|
+
- 스토리북: `배포/화면/...`
|
|
10
|
+
|
|
11
|
+
## 목적
|
|
12
|
+
|
|
13
|
+
<한 문장 요약>다.
|
|
14
|
+
|
|
15
|
+
## 영역 구조
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
21
|
+
| --- | --- | --- | --- |
|
|
22
|
+
| 바깥 틀 | | | |
|
|
23
|
+
|
|
24
|
+
## 버튼과 행동 위치
|
|
25
|
+
|
|
26
|
+
| 행동 | 컴포넌트·tone | 위치 | 개수·순서 |
|
|
27
|
+
| --- | --- | --- | --- |
|
|
28
|
+
|
|
29
|
+
## 상태
|
|
30
|
+
|
|
31
|
+
| 상태 | 화면 모습 | 행동 |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| 기본 | | |
|
|
34
|
+
| 로딩 | | |
|
|
35
|
+
| 빈 | | |
|
|
36
|
+
| 오류 | | |
|
|
37
|
+
|
|
38
|
+
## 사용하는 지침
|
|
39
|
+
|
|
40
|
+
| 지침 | 쓰는 곳 |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
|
|
43
|
+
## 코드 골격
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
// Web
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
// Native
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 큰 글자·다크·좁은 폭
|
|
54
|
+
|
|
55
|
+
| 조건 | 바뀌는 것 |
|
|
56
|
+
| --- | --- |
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 이름
|
|
2
|
+
|
|
3
|
+
- 단계: 토큰
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: YYYY-MM-DD
|
|
8
|
+
- 근거: `../../<계약 문서>.md`, `src/file.ts`
|
|
9
|
+
- 스토리북: `배포/토큰/...`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
<한 문장 요약>다.
|
|
14
|
+
|
|
15
|
+
## 값
|
|
16
|
+
|
|
17
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
18
|
+
| --- | --- | --- | --- | --- |
|
|
19
|
+
|
|
20
|
+
## 쓰는 법
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
// Web
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
// Native
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 하지 말 것
|
|
31
|
+
|
|
32
|
+
-
|