@hjmds/design-contracts 1.12.1 → 1.13.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/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 +26 -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 +31 -0
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +20 -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 +378 -0
- package/docs/sheet.md +21 -0
- package/docs/splitter.md +8 -2
- package/docs/theming.md +36 -29
- package/docs/toggle-group.md +13 -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 +107 -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 +221 -0
- package/docs/usage/components/section.md +111 -0
- package/docs/usage/components/segmented-control.md +146 -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 +157 -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 +279 -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,86 @@
|
|
|
1
|
+
# Spinner
|
|
2
|
+
|
|
3
|
+
- 단계: 컴포넌트
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: `src/component-recipes.ts`(`spinnerRecipe`)
|
|
9
|
+
- 스토리북: `배포/컴포넌트/상태와 알림/로딩 표시`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
진행량을 모르고 도착할 내용의 모양도 정해지지 않은 **짧은 대기**를 한 자리에서 알릴 때 쓴다.
|
|
14
|
+
패널 하나를 다시 불러오는 동안, 작은 영역의 결과를 기다리는 동안이 여기에 속한다.
|
|
15
|
+
|
|
16
|
+
## 쓰지 않을 때
|
|
17
|
+
|
|
18
|
+
| 상황 | 대신 쓸 것 |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| 도착할 목록·카드의 모양을 알고 있음 | [Skeleton](skeleton.md) |
|
|
21
|
+
| 진행량을 알거나 긴 작업(업로드·내보내기) | [Progress](progress.md) |
|
|
22
|
+
| 버튼을 누른 뒤의 처리 중 | [Button](button.md)의 `loading`(같은 자리에 Spinner를 겹치지 않는다) |
|
|
23
|
+
| 목록 끝에서 다음 페이지 | [LoadMore](load-more.md) |
|
|
24
|
+
|
|
25
|
+
## 공개 이름과 import
|
|
26
|
+
|
|
27
|
+
| 이름 | 역할 | Web | Native |
|
|
28
|
+
| --- | --- | --- | --- |
|
|
29
|
+
| `Spinner` | 기본 | `@hjmds/react`, `/feedback` | `@hjmds/react-native`, `/feedback` |
|
|
30
|
+
|
|
31
|
+
## 최소 사용 예
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
// Web
|
|
35
|
+
import { Spinner } from "@hjmds/react/feedback";
|
|
36
|
+
|
|
37
|
+
<Spinner label={t("feed.loading")} />
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
// Native
|
|
42
|
+
import { Spinner } from "@hjmds/react-native/feedback";
|
|
43
|
+
|
|
44
|
+
<Spinner label={t("feed.loading")} size="large" />
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 축과 기본값
|
|
48
|
+
|
|
49
|
+
| prop | 값 | 기본값 | 설명 |
|
|
50
|
+
| --- | --- | --- | --- |
|
|
51
|
+
| `label` | `string` | — (필수) | 화면에는 보이지 않고 보조기기에만 읽힌다 |
|
|
52
|
+
| `size`(Web) | `small` · `medium` · `large` | `medium` | — |
|
|
53
|
+
| `tone`(Web) | `brand` · `neutral` · `inverse` | `brand` | — |
|
|
54
|
+
| `size`(Native) | `small` · `large` | `small` | 플랫폼 `ActivityIndicator`를 테마 `contentBrand` 색으로 그린다. `tone`은 없다 |
|
|
55
|
+
| `layoutStyle` | `HjmCompositionStyleProp`(margin·width·flex 계열·`alignSelf`) | — | 루트 배치. Web·Native 모두 |
|
|
56
|
+
| `style`(Native) | `StyleProp<ViewStyle>` | — | deprecated — `layoutStyle` 또는 `size`. 개발 모드에서 한 번 경고하고 다음 major에서 제거된다 |
|
|
57
|
+
|
|
58
|
+
## 배치
|
|
59
|
+
|
|
60
|
+
| 항목 | 값 | 근거 |
|
|
61
|
+
| --- | --- | --- |
|
|
62
|
+
| 크기 | Web `small` 14 · `medium` 20(기본) · `large` 28(`glyph.xs`·`glyph.sm`·`glyph.lg`, 테두리 `stroke.strong` 2). Native는 플랫폼 `ActivityIndicator`의 `small`(기본)·`large`이고 HJM 숫자를 쓰지 않는다 | `spinnerRecipe`, `styles.css` `.hjm-spinner`·`.hjm-spinner__glyph`, `react-native/src/feedback.tsx` |
|
|
63
|
+
| 간격 | — | — |
|
|
64
|
+
| 순서·정렬 | 기다리는 영역의 가운데에 하나만 둔다. Web·Native 모두 루트가 가운데 정렬(`align-items`·`justify-content: center`)이므로 부모가 영역 크기를 잡아 주면 된다. 화면 전체 대기는 `large`, 카드·행 안의 대기는 `small` | `styles.css` `.hjm-spinner`, `react-native/src/feedback.tsx` |
|
|
65
|
+
| 고정·스크롤 | — | — |
|
|
66
|
+
| 좁은 폭·큰 글자 | — | — |
|
|
67
|
+
|
|
68
|
+
버튼 안 대기는 Spinner를 겹치지 않고 [Button](button.md)의 `loading`을 쓴다.
|
|
69
|
+
|
|
70
|
+
## 꼭 지킬 것
|
|
71
|
+
|
|
72
|
+
- `label`은 i18n 키로 넣고 "무엇을" 기다리는지 적는다("불러오는 중"만 반복하지 않는다).
|
|
73
|
+
- 한 영역에 Spinner는 하나다. 목록 행마다 Spinner를 두면 대신 Skeleton을 쓴다.
|
|
74
|
+
- 대기가 끝나면 Spinner를 결과(내용·[EmptyState](empty-state.md)·오류)로 바꾼다.
|
|
75
|
+
- 색은 Web `tone`과 테마로만 바꾼다. 어두운 배경 위에서는 `tone="inverse"`를 쓴다.
|
|
76
|
+
- 배치(바깥 여백·정렬)는 `layoutStyle`로만 한다. Native `style`은 쓰지 않는다.
|
|
77
|
+
|
|
78
|
+
## 플랫폼 차이
|
|
79
|
+
|
|
80
|
+
| 항목 | Web | Native |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| 역할 | `role="status"`, `aria-live="polite"` | `accessibilityRole="progressbar"`, `busy: true` |
|
|
83
|
+
| 크기 | 3단계 | `small`·`large` 2단계 |
|
|
84
|
+
| tone | 있음 | 없음(항상 brand 색) |
|
|
85
|
+
| reduced motion | 회전 멈춤(CSS) | 별도 처리 없음(`ActivityIndicator` 그대로) |
|
|
86
|
+
| `style` | HTML 속성(`layoutStyle`과 합쳐지고 `layoutStyle`이 이김) | deprecated |
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Splitter
|
|
2
|
+
|
|
3
|
+
- 단계: 컴포넌트
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: [Splitter](../../splitter.md), `src/splitter.ts`(`splitterRecipe`)
|
|
9
|
+
- 스토리북: `배포/컴포넌트/레이아웃/분할 영역 조절`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
넓은 Web 화면에서 두 영역의 경계를 사용자가 드래그나 키보드로 옮겨 크기를 정할 때 쓴다.
|
|
14
|
+
파일 트리/편집기 폭, 목록/상세 폭처럼 사용자가 정한 크기가 다시 방문해도 남길 바라는 레이아웃이다.
|
|
15
|
+
|
|
16
|
+
## 쓰지 않을 때
|
|
17
|
+
|
|
18
|
+
| 상황 | 대신 쓸 것 |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| 개발자가 정한 고정 비율의 나란한 배치 | [Grid](grid.md), [Stack](stack.md) |
|
|
21
|
+
| 옆에서 열고 닫는 보조 패널 | [SidePanel](side-panel.md), [Sidebar](sidebar.md) |
|
|
22
|
+
| 패널 접기(collapse), 분리선 여러 개 | 없음(계약이 넣지 않았다) |
|
|
23
|
+
| 범위 안의 값 하나 고르기 | [Slider](slider.md) |
|
|
24
|
+
| Native 화면 | 없음 |
|
|
25
|
+
|
|
26
|
+
## 공개 이름과 import
|
|
27
|
+
|
|
28
|
+
| 이름 | 역할 | Web | Native |
|
|
29
|
+
| --- | --- | --- | --- |
|
|
30
|
+
| `Splitter` | 기본 | `@hjmds/react`, `/splitter` | — |
|
|
31
|
+
|
|
32
|
+
## 최소 사용 예
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
// Web
|
|
36
|
+
import { Splitter } from "@hjmds/react/splitter";
|
|
37
|
+
|
|
38
|
+
<Splitter
|
|
39
|
+
label={t("editor.resizeTree")}
|
|
40
|
+
min={15}
|
|
41
|
+
max={60}
|
|
42
|
+
step={5}
|
|
43
|
+
value={treeWidth}
|
|
44
|
+
onValueChange={setTreeWidth}
|
|
45
|
+
onValueChangeEnd={persistTreeWidth}
|
|
46
|
+
getValueText={(v) => t("editor.percent", { value: v })}
|
|
47
|
+
primaryPane={<FileTree />}
|
|
48
|
+
secondaryPane={<Editor />}
|
|
49
|
+
/>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Native: 없음.
|
|
53
|
+
|
|
54
|
+
## 축과 기본값
|
|
55
|
+
|
|
56
|
+
| prop | 값 | 기본값 | 설명 |
|
|
57
|
+
| --- | --- | --- | --- |
|
|
58
|
+
| `label`·`min`·`max`·`primaryPane`·`secondaryPane` | — | — (필수) | — |
|
|
59
|
+
| `step` | `number` | `1` | — |
|
|
60
|
+
| `axis` | `horizontal` · `vertical` | `horizontal` | `horizontal`은 패널이 좌우로 나란하고 분리선이 세로, `vertical`은 위아래 |
|
|
61
|
+
| `value`/`defaultValue` | `number` | 둘 다 없으면 `min` | `primaryPane`이 값만큼, `secondaryPane`이 나머지를 차지한다 |
|
|
62
|
+
| `onValueChange` | `(value: number) => void` | — | 드래그 중 매번 |
|
|
63
|
+
| `onValueChangeEnd` | `(value: number) => void` | — | 드래그를 놓을 때와 값이 실제로 바뀐 키보드 step마다 한 번 |
|
|
64
|
+
| `getValueText` | `(value: number) => string` | — | 분리선의 `aria-valuetext`. 없으면 숫자만 읽힌다 |
|
|
65
|
+
| `disabled` | `boolean` | `false` | — |
|
|
66
|
+
| `layoutStyle` | `HjmCompositionStyleProp` | — | 루트 배치(margin·width·flex 계열·`alignSelf`) |
|
|
67
|
+
| `className`, `style` | `string`, `CSSProperties` | — | 루트에 붙는다. `style`과 `layoutStyle`이 겹치면 `layoutStyle`이 이긴다 |
|
|
68
|
+
|
|
69
|
+
## 배치
|
|
70
|
+
|
|
71
|
+
| 항목 | 값 | 근거 |
|
|
72
|
+
| --- | --- | --- |
|
|
73
|
+
| 크기 | `primaryPane`이 값만큼 고정 폭(`flex: 0 0`), `secondaryPane`이 나머지를 채운다. 분리선은 보이는 선 1(`stroke.default`, `border` 색), 잡는 영역 44(`control.minTouchTarget`) | `splitterRecipe.separator`, `styles.css` `.hjm-splitter*`, `react/src/splitter.tsx` |
|
|
74
|
+
| 간격 | 패널 사이 간격은 분리선의 44 영역이 대신하므로 패널에 따로 바깥 여백을 더하지 않는다 | `styles.css` `.hjm-splitter__separator` |
|
|
75
|
+
| 순서·정렬 | 넓은 Web 화면의 본문 영역을 둘로 나눈다. `horizontal`은 primary가 시작(왼쪽), `vertical`은 위 | `styles.css` `.hjm-splitter` |
|
|
76
|
+
| 고정·스크롤 | 두 패널은 각자 스크롤한다(`overflow: auto`). 부모가 높이를 정해야 세로 스크롤이 생긴다. 내용이 넘칠 때만 그 패널이 Tab 정지점(`tabIndex=0`)이 되어 키보드로 스크롤할 수 있고, 넘치지 않으면 정지점이 없다 | `styles.css` `.hjm-splitter__pane`, `splitter.tsx` `useScrollableTabStop` |
|
|
77
|
+
| 좁은 폭·큰 글자 | `min`은 좁은 쪽 패널 내용이 깨지지 않는 폭으로 정한다. 폭이 `breakpoint.medium`(600) 아래인 화면에서는 Splitter를 그리지 말고 [Stack](stack.md)으로 위아래로 쌓거나 한 패널만 보인다(HJM은 자동 전환하지 않는다) | `foundations.ts` `breakpoint` |
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
axis="horizontal"(기본)
|
|
81
|
+
┌──────────────┬┬──────────────────────────┐
|
|
82
|
+
│ primaryPane ││ secondaryPane │
|
|
83
|
+
│ (값만큼 고정)││ (나머지, 각자 스크롤) │
|
|
84
|
+
│ ││ │
|
|
85
|
+
└──────────────┴┴──────────────────────────┘
|
|
86
|
+
└┘ 분리선: 선 1 · 잡는 영역 44
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 꼭 지킬 것
|
|
90
|
+
|
|
91
|
+
- 크기 저장은 `onValueChangeEnd`에서 한다. 경계값에서 더 누른 키는 end를 보내지 않는다.
|
|
92
|
+
- `label`과 `getValueText`는 i18n 문구로 준다. 단위(%·px) 표현은 제품 소유다.
|
|
93
|
+
- 분리선 두께·44px hit target·핸들 모양은 HJM 소유다. 배치는 `layoutStyle`로 하고 분리선을 덮지 않는다.
|
|
94
|
+
- 좁은 화면(모바일 Web)에서 쓸지는 제품이 판단한다. 계약은 데스크톱 패턴으로 정의한다.
|
|
95
|
+
|
|
96
|
+
## 함정
|
|
97
|
+
|
|
98
|
+
- 키보드는 pane 축 방향키만 쓴다(가로면 좌/우, 세로면 위/아래). 다른 방향키와 PageUp/PageDown은 pane 콘텐츠의 것이다.
|
|
99
|
+
- 패널의 Tab 정지점은 넘침에 따라 생기고 사라진다(ResizeObserver·MutationObserver로 다시 잰다). 테스트에서 패널 `tabIndex`를 고정값으로 기대하지 않는다.
|
|
100
|
+
패널에는 접근성 이름이 없다(이름 prop은 breaking 변경이라 두지 않았다, `splitter.tsx` 주석). 미게시(1.12.1 이후) 변경이며 1.12.1까지는 패널이 포커스를 받지 않았다.
|
|
101
|
+
- RTL에서는 ArrowLeft가 increment다. 드래그 거리도 같은 기준으로 잰다.
|
|
102
|
+
- primary pane 폭은 `value`가 아니라 `(value-min)/(max-min)` 비율로 그려진다. `value=min`이면 primary pane이 0%, `max`면 100%다.
|
|
103
|
+
예를 들어 `min=15, max=60, value=30`은 33%로 그려진다. 원하는 실제 폭 범위에 맞춰 `min`/`max`를 정하고 화면에서 확인한다.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Stack
|
|
2
|
+
|
|
3
|
+
- 단계: 컴포넌트
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: [Flex·Space와의 관계](../../layout-primitives.md), `src/component-recipes.ts`(`stackRecipe`)
|
|
9
|
+
- 스토리북: `배포/컴포넌트/레이아웃/가로·세로 배치`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
자식들을 한 방향으로 늘어놓고 사이 간격을 토큰으로 맞출 때 쓴다. 폼 필드 세로 나열, 버튼 두 개의
|
|
14
|
+
가로 배치, 카드 안 제목·설명 묶음이 여기에 속한다. antd `Flex`·`Space`에 해당하는 자리도 Stack이다.
|
|
15
|
+
|
|
16
|
+
## 쓰지 않을 때
|
|
17
|
+
|
|
18
|
+
| 상황 | 대신 쓸 것 |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| 2차원 열·반응형 칸 | [Grid](grid.md) |
|
|
21
|
+
| 높이가 제각각인 카드 벽 | [Masonry](masonry.md) |
|
|
22
|
+
| 콘텐츠 최대 폭·좌우 여백 | [Container](container.md) |
|
|
23
|
+
| 배경·테두리·radius가 있는 상자 | [Surface](surface.md), [Card](card.md) |
|
|
24
|
+
| 항목 사이 구분선 | Stack 안에 [Divider](divider.md) |
|
|
25
|
+
| 헤더·사이드바·본문 화면 골격 | [Layout](layout.md) |
|
|
26
|
+
| 제목이 있는 본문 묶음 | [Section](section.md) |
|
|
27
|
+
|
|
28
|
+
## 공개 이름과 import
|
|
29
|
+
|
|
30
|
+
| 이름 | 역할 | Web | Native |
|
|
31
|
+
| --- | --- | --- | --- |
|
|
32
|
+
| `Stack` | 기본 | `@hjmds/react`, `/layout` | `@hjmds/react-native`, `/primitives` |
|
|
33
|
+
|
|
34
|
+
## 최소 사용 예
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
// Web
|
|
38
|
+
import { Button } from "@hjmds/react/actions";
|
|
39
|
+
import { Stack } from "@hjmds/react/layout";
|
|
40
|
+
|
|
41
|
+
<Stack axis="inline" gap="xs" justify="end">
|
|
42
|
+
<Button tone="secondary" onClick={cancel}>{t("common.cancel")}</Button>
|
|
43
|
+
<Button onClick={save}>{t("common.save")}</Button>
|
|
44
|
+
</Stack>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// Native
|
|
49
|
+
import { Heading } from "@hjmds/react-native/heading";
|
|
50
|
+
import { Stack, Text } from "@hjmds/react-native/primitives";
|
|
51
|
+
|
|
52
|
+
<Stack gap="sm">
|
|
53
|
+
<Heading level="level4" semanticLevel={2}>{t("profile.title")}</Heading>
|
|
54
|
+
<Text tone="muted">{t("profile.description")}</Text>
|
|
55
|
+
</Stack>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 축과 기본값
|
|
59
|
+
|
|
60
|
+
| prop | 값 | 기본값 | 설명 |
|
|
61
|
+
| --- | --- | --- | --- |
|
|
62
|
+
| `axis` | `block` · `inline` | `block` | `block` 세로, `inline` 가로 |
|
|
63
|
+
| `gap` | `xxs`(4) · `xs`(8) · `sm`(12) · `md`(16) · `lg`(20) · `xl`(24) · `xxl`(32) · `xxxl`(40) | `md` | spacing 토큰 |
|
|
64
|
+
| `align` | `start` · `center` · `end` · `stretch` | `stretch` | — |
|
|
65
|
+
| `justify` | `start` · `center` · `end` · `between` | `start` | — |
|
|
66
|
+
| `wrap` | `boolean` | `false` | — |
|
|
67
|
+
| `layoutStyle` | `HjmCompositionStyleProp` | — | Stack 자신의 바깥 여백·폭·flex·`alignSelf` |
|
|
68
|
+
|
|
69
|
+
## 배치
|
|
70
|
+
|
|
71
|
+
| 항목 | 값 | 근거 |
|
|
72
|
+
| --- | --- | --- |
|
|
73
|
+
| 크기 | — | — |
|
|
74
|
+
| 간격 | 형제 사이 간격만 정한다. 화면 섹션 사이 `xl` 24(`layout.sectionGap`), 섹션 안 내용 사이 `md` 16(`layout.contentGap`, 기본), 관련 묶음(라벨과 값, 나란한 버튼) `sm` 12·`xs` 8. 이 밖의 숫자를 새로 만들지 않는다. 바깥 여백은 부모(화면 여백 `layout.pagePadding` compact 16 · regular 20 · spacious 24)가 정한다 | `stackRecipe`, `foundations.ts` `layout` |
|
|
75
|
+
| 순서·정렬 | 가로로 버튼을 나열할 때 주 행동 순서는 [Button](button.md#배치)을 따른다 | — |
|
|
76
|
+
| 고정·스크롤 | — | — |
|
|
77
|
+
| 좁은 폭·큰 글자 | 가로(`axis="inline"`)로 버튼·칩을 나열해 좁은 폭에서 넘치면 `wrap`을 켠다 | `stackRecipe.defaults.wrap` |
|
|
78
|
+
|
|
79
|
+
## 꼭 지킬 것
|
|
80
|
+
|
|
81
|
+
- 간격은 `gap` 토큰으로만 정한다. 자식마다 margin을 붙여 간격을 만들지 않는다.
|
|
82
|
+
- Stack 자체의 배치(바깥 여백·폭·flex)는 `layoutStyle`로 한다. Stack은 배경·테두리를 갖지 않으므로
|
|
83
|
+
상자가 필요하면 Surface로 감싼다.
|
|
84
|
+
- 시각 순서는 DOM/자식 순서와 같게 둔다. 순서를 뒤집는 CSS로 읽기 순서를 바꾸지 않는다.
|
|
85
|
+
|
|
86
|
+
## 플랫폼 차이
|
|
87
|
+
|
|
88
|
+
| 항목 | Web | Native |
|
|
89
|
+
| --- | --- | --- |
|
|
90
|
+
| `gap` 타입 | 토큰만 | 토큰 또는 숫자 |
|
|
91
|
+
| 방향 | CSS 상속 | provider `environment.direction`을 `direction`으로 적용 |
|
|
92
|
+
| import 경로 | `/layout` | `/primitives` |
|
|
93
|
+
| ref | `forwardRef`(`div`) | 없음 |
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Statistic
|
|
2
|
+
|
|
3
|
+
- 단계: 컴포넌트
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: [Statistic](../../statistic.md), [optional adapters](../../optional-adapters.md), `src/component-recipes.ts`(`statisticRecipe`)
|
|
9
|
+
- 스토리북: `배포/컴포넌트/데이터 표시/수치 표시`, `배포/컴포넌트/데이터 표시/움직이는 수치`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
라벨이 붙은 **숫자 지표 하나**(또는 여러 개)를 보여 줄 때 쓴다. 오늘 기록 수, 승률, 잔액,
|
|
14
|
+
전주 대비 증감처럼 값·단위·추세·보조 설명이 한 묶음인 자리다. 여러 지표를 나란히 두면 `StatisticGroup`을 쓴다.
|
|
15
|
+
|
|
16
|
+
## 쓰지 않을 때
|
|
17
|
+
|
|
18
|
+
| 상황 | 대신 쓸 것 |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| 항목 이름과 값이 짝인 속성 목록(숫자가 주인공이 아님) | [DescriptionList](description-list.md) |
|
|
21
|
+
| 진행률·완료 비율 | [Progress](progress.md) |
|
|
22
|
+
| 아이콘·탭 옆의 작은 개수 | [CounterBadge](counter-badge.md), [Badge](badge.md) |
|
|
23
|
+
| 행·열이 있는 수치 표 | [DataTable](data-table.md) |
|
|
24
|
+
| 기간별 활동 밀도 | [ActivityHeatmap](activity-heatmap.md) |
|
|
25
|
+
| 누르면 이동하는 지표 | 바깥을 [Link](link.md)·[Button](button.md)으로 감싼다(Statistic은 interactive하지 않다) |
|
|
26
|
+
|
|
27
|
+
## 공개 이름과 import
|
|
28
|
+
|
|
29
|
+
| 이름 | 역할 | Web | Native |
|
|
30
|
+
| --- | --- | --- | --- |
|
|
31
|
+
| `Statistic` | 기본 | `@hjmds/react`, `/display` | `@hjmds/react-native`, `/data-display` |
|
|
32
|
+
| `StatisticGroup` | 동반(1~4열 묶음) | `@hjmds/react`, `/display` | `@hjmds/react-native`, `/data-display` |
|
|
33
|
+
| `AnimatedStatistic` | 확장(값 변화 모션, optional-extension) | `/statistic-motion` | `/statistic-motion` |
|
|
34
|
+
|
|
35
|
+
`AnimatedStatistic`은 granular subpath로만 import 된다. Web은 optional peer `@number-flow/react` 0.6.2를
|
|
36
|
+
앱에 설치해야 한다. Native는 추가 peer 없이 HJM `ContentTransition`(RN `Animated`)으로 전체 지표를 전환한다.
|
|
37
|
+
|
|
38
|
+
## 최소 사용 예
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
// Web
|
|
42
|
+
import { StatisticGroup } from "@hjmds/react/display";
|
|
43
|
+
|
|
44
|
+
<StatisticGroup
|
|
45
|
+
label={t("stats.weekly")}
|
|
46
|
+
descriptor={{
|
|
47
|
+
columns: 2,
|
|
48
|
+
items: [
|
|
49
|
+
{ id: "letters", label: t("stats.letters"), value: formatNumber(sent), suffix: t("stats.unitLetters") },
|
|
50
|
+
{
|
|
51
|
+
id: "streak", label: t("stats.streak"), value: formatNumber(streak),
|
|
52
|
+
trend: { direction: "up", tone: "success", label: t("stats.trendUp") },
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
// Native
|
|
61
|
+
import { Statistic } from "@hjmds/react-native/data-display";
|
|
62
|
+
|
|
63
|
+
<Statistic
|
|
64
|
+
presentation="surface"
|
|
65
|
+
descriptor={{ id: "balance", label: t("wallet.balance"), value: formatCurrency(balance) }}
|
|
66
|
+
/>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 축과 기본값
|
|
70
|
+
|
|
71
|
+
| prop | 값 | 기본값 | 설명 |
|
|
72
|
+
| --- | --- | --- | --- |
|
|
73
|
+
| `descriptor` | `id`·`label`·`value`(포맷이 끝난 문자열) 필수, `prefix`·`suffix`·`hint`·`trend` 선택 | — | 빈 문자열은 `TypeError` |
|
|
74
|
+
| `trend.direction` | `up` · `down` · `flat` | — | — |
|
|
75
|
+
| `trend.tone` | `neutral` · `success` · `warning` · `danger` | `neutral` | 보이는 `trend.label` 필수 |
|
|
76
|
+
| `density` | `comfortable` · `compact` | `comfortable` | 값 크기 `heading` · `title` |
|
|
77
|
+
| `presentation` | `plain` · `surface` | `plain` | `surface`는 테두리 상자 |
|
|
78
|
+
| `columns`(`StatisticGroup`) | 1~4 | 3 | 폭이 좁거나 글자가 크면 renderer가 1열까지 줄인다 |
|
|
79
|
+
| `value`(`AnimatedStatistic`) | 유한한 숫자 | — (필수) | `locale` 필수, `format`(`Intl.NumberFormatOptions`) |
|
|
80
|
+
| `animated`(`AnimatedStatistic`) | `boolean` | `true` | — |
|
|
81
|
+
| `contextLabel` | `string` | — | 접근성 이름 앞에 붙는 맥락. `StatisticGroup`은 group `label`을 넘긴다 |
|
|
82
|
+
| `composeAccessibilityLabel` | `(input: { contextLabel?, descriptor, valueText }) => string` | 기본 어순 | `descriptor`는 trend tone 기본값이 채워진 descriptor, `valueText`는 prefix·value·suffix를 이은 문자열 |
|
|
83
|
+
| `renderTrendMark` | `(props: { name: "trendUp" \| "trendDown" \| "trendFlat", color, size }) => ReactNode` | 내장 표시 | 추세 아이콘을 제품 아이콘으로 바꾼다. `color`는 Web `"currentColor"`, Native trend tone 색 |
|
|
84
|
+
| `renderValue`(Web) | `(value: string) => ReactNode` | — | 보이는 값만 바꾼다. 접근성 값은 descriptor 그대로 |
|
|
85
|
+
| `availableWidth`(Native `StatisticGroup`) | `number` | 측정 폭 | 열 수 계산에 쓸 폭 |
|
|
86
|
+
| `layoutStyle` | `HjmCompositionStyleProp` | — | 루트 배치. Web·Native 모두 |
|
|
87
|
+
| Native `style`·`labelStyle`·`valueStyle`·`affixStyle`·`trendStyle`·`hintStyle`, 그룹 `style`·`itemStyle` | `StyleProp` | — | deprecated — `layoutStyle` 또는 `density`·`presentation`. 개발 모드에서 한 번 경고하고 다음 major에서 제거된다 |
|
|
88
|
+
|
|
89
|
+
## 배치
|
|
90
|
+
|
|
91
|
+
| 항목 | 값 | 근거 |
|
|
92
|
+
| --- | --- | --- |
|
|
93
|
+
| 크기 | `presentation="surface"`의 안쪽 여백: `comfortable` `spacing.md` 16 · `compact` `spacing.sm` 12(두 플랫폼). 테두리 `stroke.default` 1, radius `md` 12 | `statisticRecipe.density`·`presentations`, `styles.css` `.hjm-statistic[data-presentation="surface"]` |
|
|
94
|
+
| 글자 | 라벨 `label` 12 semibold(`compact` `caption` 11), 값 `heading` 24 heavy(`compact` `title` 18), prefix·suffix `body` 14 semibold 본문색, hint `caption`, 추세 `caption` bold(두 플랫폼). 2026-10-06까지 Web은 라벨 본문 14 medium, 값은 밀도와 관계없이 18, prefix·suffix는 흐린색이었다(1.12.1 이후 미게시) | `statisticRecipe.density`·`label`·`value`·`affix`·`hint`·`trend`, `.hjm-statistic__*` |
|
|
95
|
+
| 간격 | 그룹 간격 `statisticRecipe.group.gap` = `spacing.xs` 8. 지표 안 줄 간격(라벨·값·추세) `comfortable` `spacing.xs` 8 · `compact` `spacing.xxs` 4(두 플랫폼) | `styles.css` `.hjm-statistic`·`.hjm-statistic-group`, `statisticRecipe` |
|
|
96
|
+
| 순서·정렬 | 화면 위쪽 요약 영역이나 카드 안에 둔다. 지표 여러 개는 [Grid](grid.md)를 직접 짜지 말고 `StatisticGroup`으로 묶는다 | — |
|
|
97
|
+
| 고정·스크롤 | — | — |
|
|
98
|
+
| 좁은 폭·큰 글자 | 그룹 열 수 `columns`(1~4, 기본 3). Web은 폭 600 미만(`breakpoint.medium`)에서 1열. Native는 항목 폭이 120×글자 배율(`statisticRecipe.group.minItemWidth`) 아래면 열을 줄인다. 값은 줄바꿈된다(자르지 않음). 긴 통화 값이 들어갈 칸이면 열 수를 줄인다 | `styles.css` `@media (max-width: 599.98px)`, `react-native/src/data-display.tsx` |
|
|
99
|
+
|
|
100
|
+
## 꼭 지킬 것
|
|
101
|
+
|
|
102
|
+
- 숫자·통화·단위 포맷은 제품이 한다. HJM은 계산·포맷하지 않고 받은 `value` 문자열을 그대로 그린다.
|
|
103
|
+
- 모든 문구(label·trend label·hint·group label)는 i18n 키로 넣는다. 추세는 색만으로 알리지 않는다.
|
|
104
|
+
- `direction`과 `tone`은 따로 정한다. 증가가 나쁜 지표는 `up` + `danger`다([계약](../../statistic.md)).
|
|
105
|
+
- 접근성 이름은 기본으로 `[contextLabel, label, 값, trend label, hint]`를 이어 만든다. 어순을 바꿔야 하면
|
|
106
|
+
`composeAccessibilityLabel`을 쓴다. `StatisticGroup`은 group `label`을 각 항목의 `contextLabel`로 넘긴다.
|
|
107
|
+
- 배치는 `layoutStyle`로만 한다. Native의 deprecated `style`·`*Style`·`itemStyle`로 recipe 색·굵기를 덮지 않는다.
|
|
108
|
+
|
|
109
|
+
## 플랫폼 차이
|
|
110
|
+
|
|
111
|
+
| 항목 | Web | Native |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| 값 렌더 교체 | `renderValue`(접근성 값은 descriptor 유지) | 없음 |
|
|
114
|
+
| 그룹 폭 | CSS grid, 좁은 화면에서 1열 | `availableWidth` 또는 측정 폭, 최소 항목 폭 120×글자 배율로 열 수 감소 |
|
|
115
|
+
| 슬롯 스타일 prop | 없음(`className`, 배치는 `layoutStyle`) | `style`·`*Style`, 그룹 `itemStyle`(모두 deprecated, 배치는 `layoutStyle`) |
|
|
116
|
+
| `AnimatedStatistic` 모션 | 숫자 자리 단위 NumberFlow | 지표 전체 `rise` 전환 |
|
|
117
|
+
| `renderTrendMark` color | `"currentColor"` | trend tone을 푼 색 문자열 |
|
|
118
|
+
|
|
119
|
+
## 함정
|
|
120
|
+
|
|
121
|
+
- Web `AnimatedStatistic`은 reduced motion, RTL, 라틴 숫자가 아닌 numbering system, `ar`·`fa`·`he`·`ur` locale,
|
|
122
|
+
`scientific`·`engineering` 표기에서는 애니메이션 없이 `Intl` 결과 문자열만 그린다.
|
|
123
|
+
- `AnimatedStatistic`의 `descriptor`에는 `value`를 넣지 않는다. 값은 `value` prop의 숫자로 받는다.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Steps
|
|
2
|
+
|
|
3
|
+
- 단계: 컴포넌트
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: [Steps](../../steps.md), [StepPlayer](../../step-player.md), `src/steps.ts`(`stepsRecipe`)
|
|
9
|
+
- 스토리북: `배포/컴포넌트/탐색/단계 탐색`, `배포/컴포넌트/상태와 알림/단계별 진행 표시`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
여러 단계로 된 **선형 흐름에서 지금 어디인지** 보여 줄 때 쓴다. 온보딩(환영 → 구단 → 알림 → 완료),
|
|
14
|
+
가입·주문 처리 단계가 여기에 속한다. 읽기 전용이며 단계를 눌러 이동하지 않는다.
|
|
15
|
+
|
|
16
|
+
## 쓰지 않을 때
|
|
17
|
+
|
|
18
|
+
| 상황 | 대신 쓸 것 |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| 단계 이름 없이 진행률만 | [Progress](progress.md) |
|
|
21
|
+
| 시간 순 사건 기록 | [Timeline](timeline.md) |
|
|
22
|
+
| 같은 수준의 화면 사이 이동 | [Tabs](tabs.md), [SegmentedControl](segmented-control.md) |
|
|
23
|
+
| 페이지 번호 이동 | [Pagination](pagination.md) |
|
|
24
|
+
| 화면을 가리키며 하는 기능 안내 | [Tour](tour.md) |
|
|
25
|
+
| 흐름이 끝난 뒤의 결과 | [Result](result.md) (또는 `currentStepStatus: "complete"`) |
|
|
26
|
+
|
|
27
|
+
## 공개 이름과 import
|
|
28
|
+
|
|
29
|
+
| 이름 | 역할 | Web | Native |
|
|
30
|
+
| --- | --- | --- | --- |
|
|
31
|
+
| `Steps` | 기본 | `@hjmds/react`, `/navigation`, `/steps` | `@hjmds/react-native`, `/steps` |
|
|
32
|
+
| `StepPlayer` | 확장(재생형 단계 소개, optional-extension) | `/step-player` | `/step-player` |
|
|
33
|
+
|
|
34
|
+
`StepPlayer`는 granular subpath로만 import 된다. Steps·Progress·Button·Stack만 합성하므로 추가 peer는 없다.
|
|
35
|
+
|
|
36
|
+
## 최소 사용 예
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
// Web
|
|
40
|
+
import { Steps } from "@hjmds/react/steps";
|
|
41
|
+
|
|
42
|
+
<Steps
|
|
43
|
+
descriptor={{
|
|
44
|
+
currentStepId: "team",
|
|
45
|
+
steps: [
|
|
46
|
+
{ id: "welcome", label: t("onboarding.welcome") },
|
|
47
|
+
{ id: "team", label: t("onboarding.team") },
|
|
48
|
+
{ id: "alerts", label: t("onboarding.alerts") },
|
|
49
|
+
],
|
|
50
|
+
}}
|
|
51
|
+
statusLabels={{
|
|
52
|
+
pending: t("steps.pending"), current: t("steps.current"),
|
|
53
|
+
complete: t("steps.complete"), error: t("steps.error"),
|
|
54
|
+
}}
|
|
55
|
+
composeAccessibleName={({ position, total, label }) => t("steps.name", { position, total, label })}
|
|
56
|
+
/>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
// Native — props가 같다
|
|
61
|
+
import { Steps } from "@hjmds/react-native/steps";
|
|
62
|
+
|
|
63
|
+
<Steps descriptor={descriptor} statusLabels={statusLabels} composeAccessibleName={composeStepName} />
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 축과 기본값
|
|
67
|
+
|
|
68
|
+
| prop | 값 | 기본값 | 설명 |
|
|
69
|
+
| --- | --- | --- | --- |
|
|
70
|
+
| `descriptor.currentStepId` | 단계 `id` | — (필수) | 상태는 단계별로 넘기지 않는다. 앞은 `complete`, 커서는 지정 상태, 뒤는 `pending`이 유도된다 |
|
|
71
|
+
| `descriptor.currentStepStatus` | `current` · `error` · `complete` | `current` | 커서 단계의 상태 |
|
|
72
|
+
| `descriptor.steps` | 단계 배열 | — (필수) | 2개 이상, `id`는 고유하고 앞뒤 공백이 없어야 한다. 어기면 `RangeError`/`TypeError` |
|
|
73
|
+
| `descriptor.steps[]` | `{ id, label, description? }` | — | — |
|
|
74
|
+
| `statusLabels` | `{ pending, current, complete, error }`(모두 `string`) | — (필수) | 상태를 읽는 문구 |
|
|
75
|
+
| `composeAccessibleName` | `(info: { position: number, total: number, label: string }) => string` | — (필수) | `position`은 1부터 |
|
|
76
|
+
| `renderMark` | `(status: StepStatus, position: number) => ReactNode` | `✓`·`!`·번호 | 원 안 표시를 바꾼다 |
|
|
77
|
+
| `layoutStyle` | `HjmCompositionStyleProp` | — | 루트 배치. Web·Native 모두 |
|
|
78
|
+
| `style`(Native) | `StyleProp<ViewStyle>` | — | deprecated — `layoutStyle`. 개발 모드에서 한 번 경고하고 다음 major에서 제거된다 |
|
|
79
|
+
| `StepPlayer` `progress`·`playing` | `number`(0~1)·`boolean` | — (필수) | 호스트 소유 |
|
|
80
|
+
| `StepPlayer` `onPlayingChange`·`onReplay` | `(playing: boolean) => void`·`() => void` | — (필수) | 재생/일시정지·다시 보기 요청 |
|
|
81
|
+
| `StepPlayer` `labels` | `{ play, pause, replay, progress }` | — (필수) | — |
|
|
82
|
+
|
|
83
|
+
방향·clickable·variant 축은 없다(계약에서 배제). 항상 가로 한 줄이다.
|
|
84
|
+
|
|
85
|
+
## 배치
|
|
86
|
+
|
|
87
|
+
| 항목 | 값 | 근거 |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| 크기 | 단계마다 같은 폭(`minmax(0, 1fr)`, Native `flex: 1`). 원(indicator) 24(`glyph.md`), 연결선 1(`stroke.default`), 현재·오류 단계의 원 테두리 2(`stroke.strong`) | `stepsRecipe`, `styles.css` `.hjm-steps*`, `react-native/src/steps.tsx` |
|
|
90
|
+
| 간격 | 단계 사이·원과 글자 사이 `spacing.xs` 8(`stepsRecipe.gap`) | `stepsRecipe.gap` |
|
|
91
|
+
| 순서·정렬 | 흐름 화면의 맨 위(TopBar 아래, 본문 위)에 가로 한 줄. 화면 아래 행동 버튼과 떨어뜨리고 탭처럼 누르게 두지 않는다 | — |
|
|
92
|
+
| 고정·스크롤 | — | — |
|
|
93
|
+
| 좁은 폭·큰 글자 | 라벨·설명은 줄바꿈된다(`overflow-wrap: anywhere`). 좁은 폭에서 라벨이 세 줄 이상이 되면 라벨을 줄이거나 [Progress](progress.md)로 바꾼다. 세로 배치 축은 없다 | `styles.css` `.hjm-steps__label` |
|
|
94
|
+
|
|
95
|
+
## 꼭 지킬 것
|
|
96
|
+
|
|
97
|
+
- `statusLabels` 네 개와 `composeAccessibleName`은 필수다. "3단계 중 2단계" 같은 어순은 제품이 i18n으로 조립한다.
|
|
98
|
+
- 이전 단계로 가는 행동은 Steps 밖의 [Button](button.md)으로 둔다.
|
|
99
|
+
- `StepPlayer`는 타이머가 없다. `progress`(0~1)·`playing`·커서를 호스트가 소유하고, `onPlayingChange`·`onReplay`에서
|
|
100
|
+
직접 상태를 바꾼다. `labels`(`play`·`pause`·`replay`·`progress`)는 비면 `TypeError`, `progress`가 범위를 벗어나면 `RangeError`다.
|
|
101
|
+
- 실제 비동기 작업의 완료를 `StepPlayer` 시계로 추정하지 않는다([계약](../../step-player.md)).
|
|
102
|
+
|
|
103
|
+
## 플랫폼 차이
|
|
104
|
+
|
|
105
|
+
| 항목 | Web | Native |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| 구조 | `<ol>`, 현재 단계 `aria-current="step"`, 상태 문구는 숨은 텍스트 | `accessibilityRole="summary"`, 단계마다 이름 + `accessibilityHint`에 상태 문구 |
|
|
108
|
+
| 스타일 | `className`·`style`(HTML 속성), 배치는 `layoutStyle` | 배치는 `layoutStyle`(`style`은 deprecated) |
|
|
109
|
+
| ref | `forwardRef`(`ol`) | 없음 |
|
|
110
|
+
| import 경로 | root, `/navigation`, `/steps` | root, `/steps`(`/navigation` 없음) |
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Surface
|
|
2
|
+
|
|
3
|
+
- 단계: 컴포넌트
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: `src/base-recipes.ts`(`surfaceRecipe`·`surfaceDefaults`·`surfaceGeometry`)
|
|
9
|
+
- 스토리북: `배포/컴포넌트/레이아웃/배경 영역`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
배경·테두리·radius를 가진 **의미 없는 상자**가 필요할 때 쓴다. 다른 컴포넌트로 표현되지 않는
|
|
14
|
+
패널, 요약 영역, 이미지를 둥근 모서리 안에 자르는 틀이 여기에 속한다.
|
|
15
|
+
|
|
16
|
+
## 쓰지 않을 때
|
|
17
|
+
|
|
18
|
+
| 상황 | 대신 쓸 것 |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| 제목·본문·행동이 있는 콘텐츠 카드, 누르는 카드 | [Card](card.md) |
|
|
21
|
+
| 간격만 필요하고 상자는 필요 없음 | [Stack](stack.md) |
|
|
22
|
+
| 상태·경고를 알리는 상자 | [Notice](notice.md) |
|
|
23
|
+
| 설정·목록 행 | [ListRow](list-row.md), [List](list.md) |
|
|
24
|
+
| 화면 위에 뜨는 층 | [Sheet](sheet.md), [Dialog](dialog.md), [Popover](popover.md) |
|
|
25
|
+
| 콘텐츠 최대 폭·좌우 여백 | [Container](container.md) |
|
|
26
|
+
|
|
27
|
+
## 공개 이름과 import
|
|
28
|
+
|
|
29
|
+
| 이름 | 역할 | Web | Native |
|
|
30
|
+
| --- | --- | --- | --- |
|
|
31
|
+
| `Surface` | 기본 | `@hjmds/react`, `/layout` | `@hjmds/react-native`, `/primitives` |
|
|
32
|
+
|
|
33
|
+
## 최소 사용 예
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
// Web
|
|
37
|
+
import { Surface } from "@hjmds/react/layout";
|
|
38
|
+
|
|
39
|
+
<Surface as="section" padding="md" aria-label={t("order.summary")}>
|
|
40
|
+
{children}
|
|
41
|
+
</Surface>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
// Native
|
|
46
|
+
import { Surface } from "@hjmds/react-native/primitives";
|
|
47
|
+
|
|
48
|
+
<Surface tone="raised" padding="md" layoutStyle={{ marginTop: 16 }}>
|
|
49
|
+
{children}
|
|
50
|
+
</Surface>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 축과 기본값
|
|
54
|
+
|
|
55
|
+
| prop | 값 | 기본값 | 설명 |
|
|
56
|
+
| --- | --- | --- | --- |
|
|
57
|
+
| `tone` | `default` · `raised` · `sunken` · `accent` · `subtle` | `default` | `raised`는 그림자, `accent`는 primary 색 테두리. 모든 tone의 배경은 테마 `bg`이고 회색 채움 대신 테두리·그림자로 위계를 나타낸다 |
|
|
58
|
+
| `padding` | `none` 또는 spacing 토큰(`xxs`~`xxxl`) | `none` | — |
|
|
59
|
+
| `radius` | `sm` · `md` · `lg` · `xl` · `full` | `lg`(16) | — |
|
|
60
|
+
| `bordered` | `boolean` | 생략 시 테두리 그림 | 모든 tone이 `borderAlways`. `bordered={false}`로만 끈다 |
|
|
61
|
+
| 자식 자르기 | — | `raised` 외 자름 | `raised`를 뺀 tone은 둥근 모서리 밖으로 넘친 자식을 자른다. `raised`는 그림자가 잘리지 않게 자르지 않는다 |
|
|
62
|
+
| `as`(Web) | `div` · `section` · `article` | `div` | — |
|
|
63
|
+
| `layoutStyle` | `HjmCompositionStyleProp` | — | Surface 자신의 바깥 여백·폭·flex·`alignSelf`. Web·Native 모두 |
|
|
64
|
+
|
|
65
|
+
## 배치
|
|
66
|
+
|
|
67
|
+
| 항목 | 값 | 근거 |
|
|
68
|
+
| --- | --- | --- |
|
|
69
|
+
| 크기 | 폭은 부모를 따르고 높이는 내용이 정한다. 기본 radius `lg` 16, 테두리 1(`border` 색) | `styles.css` `.hjm-surface`, `surfaceRecipe` |
|
|
70
|
+
| 간격 | 기본 `padding`이 `none`이므로 안쪽 여백을 꼭 준다. 카드 안 내용은 `md` 16, 촘촘한 묶음은 `sm` 12. 여러 Surface를 세로로 쌓을 때는 [Stack](stack.md)으로 `md` 16(같은 묶음) 또는 `xl` 24(섹션 사이) | `surfaceDefaults`, `foundations.ts` `layout` |
|
|
71
|
+
| 순서·정렬 | 화면 여백 안에 놓는 카드·묶음 상자다. Surface 안에 Surface를 다시 겹치지 않고 안쪽 구분은 [Divider](divider.md)나 간격으로 한다 | — |
|
|
72
|
+
| 고정·스크롤 | — | — |
|
|
73
|
+
| 좁은 폭·큰 글자 | 긴 글자는 줄바꿈된다(`overflow-wrap: anywhere`) | `styles.css` `.hjm-surface` |
|
|
74
|
+
|
|
75
|
+
## 꼭 지킬 것
|
|
76
|
+
|
|
77
|
+
- 배치는 `layoutStyle`로만 한다. 배경·테두리 색·radius·그림자를 덮지 않는다.
|
|
78
|
+
색은 제품 테마 토큰이 정하고, Surface마다 브랜드 색을 하드코딩하지 않는다.
|
|
79
|
+
- 안쪽 여백은 `padding` 토큰으로 정한다(기본이 `none`이라 내용이 테두리에 붙는다).
|
|
80
|
+
- Surface는 role을 갖지 않는다. 영역에 이름이 필요하면 Web은 `as="section"`과 라벨을 함께 준다.
|
|
81
|
+
- `raised` 안에 이미지를 넣으면 모서리가 잘리지 않는다. 모서리 자르기가 필요하면 다른 tone을 쓴다.
|
|
82
|
+
|
|
83
|
+
## 플랫폼 차이
|
|
84
|
+
|
|
85
|
+
| 항목 | Web | Native |
|
|
86
|
+
| --- | --- | --- |
|
|
87
|
+
| `style` | 받음(`layoutStyle`이 이김) | 타입에서 제외 |
|
|
88
|
+
| 요소 선택 | `as` | 없음(`View`) |
|
|
89
|
+
| 그림자(`raised`) | CSS | `elevation: 4`와 iOS shadow |
|
|
90
|
+
| ref | `forwardRef` | 없음 |
|
|
91
|
+
| import 경로 | `/layout` | `/primitives` |
|