@hjmds/design-contracts 1.12.0 โ 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,138 @@
|
|
|
1
|
+
# FloatingActionButton
|
|
2
|
+
|
|
3
|
+
- ๋จ๊ณ: ์ปดํฌ๋ํธ
|
|
4
|
+
- ์ํ: ๋ฐฐํฌ
|
|
5
|
+
- ์ง์: Web ยท Native
|
|
6
|
+
- ์ ์ฉ: 1.12.1
|
|
7
|
+
- ๊ฒํ ์ผ: 2026-10-06
|
|
8
|
+
- ๊ทผ๊ฑฐ: [FloatingActionButton](../../floating-action-button.md), recipe `floatingActionButtonRecipe`(`src/floating-action-button.ts`)
|
|
9
|
+
- ์คํ ๋ฆฌ๋ถ: `๋ฐฐํฌ/์ปดํฌ๋ํธ/๋์/ํ๋กํ
์คํ ๋ฒํผ`
|
|
10
|
+
|
|
11
|
+
## ์ธ์ ์ฐ๋
|
|
12
|
+
|
|
13
|
+
๋ชฉ๋กยทํผ๋์ฒ๋ผ ์คํฌ๋กค๋๋ ์ฝํ
์ธ ์์ ๋ ์๋ **๋จ์ผ ์์ฑ ํ๋**(์ ๊ธฐ๋ก ์ถ๊ฐ, ์ ๊ธ ์์ฑ)์ ์ด๋ค.
|
|
14
|
+
์คํฌ๋กค ๋ฐฉํฅ์ ๋ฐ๋ผ ์์ฝ(๋ผ๋ฒจ ๋ณด์)๊ณผ ์(์์ด์ฝ๋ง)์ผ๋ก ์ ํ์ง๋ง ๊ฐ์ ๋ฒํผ ์ธ์คํด์ค๋ฅผ ์ ์งํ๋ค.
|
|
15
|
+
|
|
16
|
+
## ์ฐ์ง ์์ ๋
|
|
17
|
+
|
|
18
|
+
| ์ํฉ | ๋์ ์ธ ๊ฒ |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| ํ๋ฉด์ ๊ฒฐ๋ก ์ด ๋๋ ํ๋จ ๊ณ ์ ์ฃผ ํ๋ | [BottomCTA](bottom-cta.md) ([ํ์ ](../../floating-action-button.md#bottomcta์-๊ฒน์น์ง-์๋๋ค-ํ์ )) |
|
|
21
|
+
| ์ฝํ
์ธ ์์ ์ผ๋ฐ ํ๋ | [Button](button.md), [IconButton](icon-button.md) |
|
|
22
|
+
| ํ๋จ ํญ ๊ฐ์ด๋ฐ์ ์์ฑ ๋ฒํผ | [BottomNavigation](bottom-navigation.md) ์์ Button/IconButton ํฉ์ฑ |
|
|
23
|
+
| ๋นํ์ฑยท๋ก๋ฉ ์ํ๊ฐ ํ์ํ ํ๋ | [Button](button.md) (FAB๋ `disabled`ยท`loading`์ด ์๋ค) |
|
|
24
|
+
|
|
25
|
+
## ๊ณต๊ฐ ์ด๋ฆ๊ณผ import
|
|
26
|
+
|
|
27
|
+
| ์ด๋ฆ | ์ญํ | Web | Native |
|
|
28
|
+
| --- | --- | --- | --- |
|
|
29
|
+
| `FloatingActionButton` | ๊ธฐ๋ณธ | `@hjmds/react`, `/floating-action-button` | `@hjmds/react-native`, `/floating-action-button` |
|
|
30
|
+
| `useFloatingActionButtonScroll` | ๋ณด์กฐ โ ์คํฌ๋กค๋ก `layoutMode` ๊ฒฐ์ | `/floating-action-button` | `/floating-action-button` |
|
|
31
|
+
| `resolveFloatingActionButtonContentClearance` | ๋ณด์กฐ โ ์ฒซ ํ๋จ ์ฌ๋ฐฑ ๊ณ์ฐ | `/floating-action-button` | `/floating-action-button` |
|
|
32
|
+
|
|
33
|
+
## ์ต์ ์ฌ์ฉ ์
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
// Web
|
|
37
|
+
import { useState } from "react";
|
|
38
|
+
import { FloatingActionButton, useFloatingActionButtonScroll } from "@hjmds/react/floating-action-button";
|
|
39
|
+
|
|
40
|
+
const layoutMode = useFloatingActionButtonScroll(); // window ์คํฌ๋กค. ์ปจํ
์ด๋๋ฉด element, ๋ง์ดํธ ์ ์ null
|
|
41
|
+
const [clearance, setClearance] = useState(0);
|
|
42
|
+
|
|
43
|
+
<ul style={{ paddingBottom: clearance }}>{items}</ul>
|
|
44
|
+
<FloatingActionButton
|
|
45
|
+
descriptor={{ icon: { name: "add" }, label: t("memo.new"), layoutMode }}
|
|
46
|
+
renderIcon={({ name, size, color }) => <ProductIcon name={name} size={size} color={color} />}
|
|
47
|
+
onContentClearanceChange={setClearance}
|
|
48
|
+
onClick={openComposer}
|
|
49
|
+
/>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// Native
|
|
54
|
+
import { useState } from "react";
|
|
55
|
+
import { FlatList, View } from "react-native";
|
|
56
|
+
import { FloatingActionButton, useFloatingActionButtonScroll } from "@hjmds/react-native/floating-action-button";
|
|
57
|
+
|
|
58
|
+
const { layoutMode, onScroll } = useFloatingActionButtonScroll();
|
|
59
|
+
const [clearance, setClearance] = useState(0);
|
|
60
|
+
|
|
61
|
+
<View style={{ flex: 1 }}>
|
|
62
|
+
<FlatList data={items} renderItem={renderItem} onScroll={onScroll} contentContainerStyle={{ paddingBottom: clearance }} />
|
|
63
|
+
<FloatingActionButton
|
|
64
|
+
descriptor={{ icon: { name: "add" }, label: t("memo.new"), layoutMode }}
|
|
65
|
+
renderIcon={({ name, size, color }) => <ProductIcon name={name} size={size} color={color} />}
|
|
66
|
+
safeAreaBottomInset={insets.bottom}
|
|
67
|
+
onContentClearanceChange={setClearance}
|
|
68
|
+
onPress={openComposer}
|
|
69
|
+
/>
|
|
70
|
+
</View>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## ์ถ๊ณผ ๊ธฐ๋ณธ๊ฐ
|
|
74
|
+
|
|
75
|
+
| prop | ๊ฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช
|
|
|
76
|
+
| --- | --- | --- | --- |
|
|
77
|
+
| `descriptor.layoutMode` | `expanded` ยท `collapsed` | `expanded` | ์ง์ ๊ณ ๋ฅด์ง ๋ง๊ณ `useFloatingActionButtonScroll` ๊ฒฐ๊ณผ๋ฅผ ๋๊ธด๋ค |
|
|
78
|
+
| `descriptor.label` | ๋ฌธ๊ตฌ(ํ์) | โ | `collapsed`์์๋ ์ ๊ทผ์ฑ ์ด๋ฆ์ ํญ์ ์ ์ฒด `label`์ด๋ค |
|
|
79
|
+
| `descriptor.icon` | `{ name }` | โ | `name`๋ง ๋ฐ๋๋ค(`size`ยท`tone`ยท์ ๊ทผ์ฑ ์ด๋ฆ์ ๋ฃ์ ์ ์๋ค). ์์ด์ฝ์ ํญ์ ์ฅ์์ด๋ค |
|
|
80
|
+
| `safeAreaBottomInset` | 0 ์ด์ ์ซ์ | `0` | โ |
|
|
81
|
+
| `renderIcon` | `(icon: { name: string; size: number; color: string; decorative: true }) => ReactNode` | ํ์ | ์ ํ ์์ด์ฝ ์์คํ
์ผ๋ก ๊ทธ๋ฆฐ๋ค |
|
|
82
|
+
| `onContentClearanceChange` | `(clearance: number) => void` | ํ์ | ์คํฌ๋กค ์ฝํ
์ธ ํ๋จ padding์ผ๋ก ์ด๋ค. ํฐ ๊ธ์ยทinset์ด ๋ฐ๋๋ฉด ๋ค์ ๋ถ๋ฆฐ๋ค |
|
|
83
|
+
| Web `onClick` ยท Native `onPress` | Button์ ๊ฐ์ ์ด๋ฒคํธ | โ | โ |
|
|
84
|
+
|
|
85
|
+
- ํคยทํฌ๊ธฐยท๋ชจ์์ `primary`ยท`large`ยท`pill`๋ก ๊ณ ์ ์ด๊ณ prop์ด ์๋ค.
|
|
86
|
+
|
|
87
|
+
## ๋ฐฐ์น
|
|
88
|
+
|
|
89
|
+
| ํญ๋ชฉ | ๊ฐ | ๊ทผ๊ฑฐ |
|
|
90
|
+
| --- | --- | --- |
|
|
91
|
+
| ํฌ๊ธฐ | ๋์ดยท์ง๋ฆ 52(IconButton `large`). `expanded`๋ ์์ฝ(์์ด์ฝ + ๋ผ๋ฒจ, ๋ผ๋ฒจ ์ข์ฐ ์ฌ๋ฐฑ `spacing.lg` 20), `collapsed`๋ ์ง๋ฆ 52 ์์ด๋ค. ์ต๋ ํญ์ ํ๋ฉด ํญ โ 32(์ฌ๋ฐฑ ์์ชฝ)๋ค | `floating-action-button.ts` `floatingActionButtonRecipe`, `styles.css` `.hjm-fab` |
|
|
92
|
+
| ๊ฐ๊ฒฉ | ๊ฐ์ฅ์๋ฆฌ ์ฌ๋ฐฑ `spacing.md`(16). ํ๋จ ๊ฑฐ๋ฆฌ๋ 16 + ํ๋จ ์์ ์์ญ์ด๋ค(Web์ `safeAreaBottomInset`๊ณผ `env(safe-area-inset-bottom)` ์ค ํฐ ๊ฐ, Native๋ `safeAreaBottomInset`). ์คํฌ๋กค ์ฝํ
์ธ ํ๋จ์ `onContentClearanceChange` ๊ฐ์ padding์ผ๋ก ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ 52 + 16ร2 + ํ๋จ inset(= 84 + inset)์ด๋ค | `floating-action-button.ts` `resolveFloatingActionButtonContentClearance`, `styles.css` `.hjm-fab`, `react-native/src/floating-action-button.tsx` |
|
|
93
|
+
| ์์ยท์ ๋ ฌ | ์์น๋ ์คํฌ๋กค ์์ญ์ **๋
ผ๋ฆฌ์ ๋ ์ชฝ ํ๋จ ๋ชจ์๋ฆฌ**๋ก ๊ณ ์ ์ด๋ค(RTL์ ์ผ์ชฝ) | `styles.css` `.hjm-fab`, `react-native/src/floating-action-button.tsx` |
|
|
94
|
+
| ๊ณ ์ ยท์คํฌ๋กค | Web์ `position: fixed`(z-index `layer.sticky` 100)๋ก ์ฐฝ์ ๋ถ๋๋ค. Native๋ positioned ๋ถ๋ชจ(`flex: 1` View) ์์์ `absolute`๋ค. [BottomNavigation](bottom-navigation.md)ยท๋๊ตฌ๋ง๋ ์์ ๋์ธ ๋๋ ๊ทธ ๋์ด๋ฅผ `safeAreaBottomInset`์ ๋ํ๋ค(Native๋ ์ ํ์ด ๋ํ๋ค) | `styles.css` `.hjm-fab`, `react-native/src/floating-action-button.tsx` |
|
|
95
|
+
| ์ข์ ํญยทํฐ ๊ธ์ | ํฐ ๊ธ์๋ก ๋ผ๋ฒจ์ด ์ฌ๋ฌ ์ค์ด ๋๋ฉด clearance ๊ฐ์ด ์ปค์ง๋ค | `react-native/src/floating-action-button.tsx` |
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
99
|
+
โ TopBar (๊ณ ์ ) โ
|
|
100
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
|
|
101
|
+
โ ๋ชฉ๋ก (์คํฌ๋กค) โ
|
|
102
|
+
โ ... โ
|
|
103
|
+
โ ๋ง์ง๋ง ํญ๋ชฉ โ
|
|
104
|
+
โ โ padding-bottom = clearanceโ
|
|
105
|
+
โ โโโโโโโโโโโโ โ
|
|
106
|
+
โ โ + ์ ๊ธฐ๋ก โ โ โ FAB expanded(52, primary pill)
|
|
107
|
+
โ โโโโโโโโโโโโ โ ๋ ์ฌ๋ฐฑ 16
|
|
108
|
+
โ 16 โ
|
|
109
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
|
|
110
|
+
โ ์์ ์์ญ โ
|
|
111
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
112
|
+
์คํฌ๋กคํด ๋ด๋ ค๊ฐ๋ฉด (+) ์ง๋ฆ 52 ์์ผ๋ก ์ ํ๋ค.
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## ๊ผญ ์งํฌ ๊ฒ
|
|
116
|
+
|
|
117
|
+
- `onContentClearanceChange`๊ฐ ์ค ๊ฐ์ ์คํฌ๋กค ์ฝํ
์ธ ํ๋จ padding์ ๋ฐ๋์ ์ ์ฉํ๋ค. ํฐ ๊ธ์์์
|
|
118
|
+
๋ผ๋ฒจ์ด ์ฌ๋ฌ ์ค์ด ๋๋ฉด ๊ฐ์ด ์ปค์ง๋ค. ์ง๋ฆ๋ง ์์ฝํ๋ฉด ๋ง์ง๋ง ํญ๋ชฉ์ ๊ฐ๋ฆฐ๋ค.
|
|
119
|
+
- ์คํฌ๋กค ์ฝํ
์ธ **๋ค**์ sibling์ผ๋ก ๋๋ค. ๋ชฉ๋ก ํญ๋ชฉ ์์ ๋ฃ์ง ์๋๋ค.
|
|
120
|
+
- ๋ผ๋ฒจ์ i18n ํค๋ก ๋ฃ๋๋ค. ์์ด์ฝ ๊ทธ๋ฆผ์ ์ ํ ์์ด์ฝ ์์คํ
์ด `renderIcon`์ผ๋ก ๊ทธ๋ฆฐ๋ค(HJM์ nameยทsizeยทcolor๋ง ์ค๋ค).
|
|
121
|
+
- ํ ํ๋ฉด์ FAB๋ ํ๋๋ง ๋๋ค. ๋ชจ์๋ฆฌ ์์น๋ ๋ฐ๊ฟ ์ ์๋ค(๋
ผ๋ฆฌ์ ๋ ์ชฝ ํ๋จ, RTL์ ์ผ์ชฝ).
|
|
122
|
+
|
|
123
|
+
## ํ๋ซํผ ์ฐจ์ด
|
|
124
|
+
|
|
125
|
+
| ํญ๋ชฉ | Web | Native |
|
|
126
|
+
| --- | --- | --- |
|
|
127
|
+
| ์ด๋ฒคํธ | `onClick` | `onPress` |
|
|
128
|
+
| ์์น | CSS(`.hjm-fab`) | positioned ๋ถ๋ชจ ์ `absolute` |
|
|
129
|
+
| ์คํฌ๋กค hook | `useFloatingActionButtonScroll(target?)` โ `layoutMode` | `useFloatingActionButtonScroll()` โ `{ layoutMode, onScroll }` |
|
|
130
|
+
| ์ฌ๋ฐฑ ์ธก์ | `ResizeObserver`ยท`resize` | `onLayout` |
|
|
131
|
+
| ๊ทธ ๋ฐ์ prop | `id`, `className`, `onFocus`, `onBlur` | `testID`, `onFocus`, `onBlur` |
|
|
132
|
+
| ref | `HTMLButtonElement` | `View` |
|
|
133
|
+
| ๋ฐฐ์น prop | ์์(`layoutStyle`์ ๋ฐ์ง ์๋๋ค. ์์น๋ CSS๊ฐ ์์ ) | ์์(`layoutStyle`ยท`style` ์์) |
|
|
134
|
+
|
|
135
|
+
## ํจ์
|
|
136
|
+
|
|
137
|
+
- ๋ชจ๋๋ง๋ค Button๊ณผ IconButton์ ๋ฐ๊ฟ ๋ผ์ฐ๋ฉด focus์ ref๋ฅผ ์๋๋ค. ์ด ์ปดํฌ๋ํธ ํ๋๋ก ์ ๊ณ ํธ๋ค.
|
|
138
|
+
- Native `safeAreaBottomInset`์ OS ํ๋จ inset๋ง์ด๋ค. ํ๋ฉด ์ ๋๊ตฌ๋ง๋ ๋์ด๋ ์ ํ์ด ๋ฐ๋ก ๋ํ๋ค.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Form
|
|
2
|
+
|
|
3
|
+
- ๋จ๊ณ: ์ปดํฌ๋ํธ
|
|
4
|
+
- ์ํ: ๋ฐฐํฌ
|
|
5
|
+
- ์ง์: Web ยท Native
|
|
6
|
+
- ์ ์ฉ: 1.12.1
|
|
7
|
+
- ๊ฒํ ์ผ: 2026-10-06
|
|
8
|
+
- ๊ทผ๊ฑฐ: [Form](../../form.md), recipe `formRecipe`(`src/form.ts`)
|
|
9
|
+
- ์คํ ๋ฆฌ๋ถ: `๋ฐฐํฌ/์ปดํฌ๋ํธ/์
๋ ฅ/์
๋ ฅ ์์`
|
|
10
|
+
|
|
11
|
+
## ์ธ์ ์ฐ๋
|
|
12
|
+
|
|
13
|
+
์ฌ๋ฌ [Field](field.md)๋ฅผ ํ ํ๋ฉด์ ์๊ณ ํ ๋ฒ์ ์ ์ถํ ๋ ์ด๋ค. Form์ ํ๋ ์ฌ์ด์ ์ธ๋ก ๋ฆฌ๋ฌ๊ณผ
|
|
14
|
+
**์ ์ถ ์ธ์
**(์ค๋ณต ์ ์ถ ์ฐจ๋จ, ์งํ ์ค ์ ๊ธ, ํผ ๋จ์ ์ค๋ฅ ํ์)๋ง ์์ ํ๋ค. ๊ฐยท๊ฒ์ฆยทdirty ํ๋จ์
|
|
15
|
+
์ ํ(React Hook Form ๋ฑ)์ด ์์ ํ๋ค.
|
|
16
|
+
|
|
17
|
+
## ์ฐ์ง ์์ ๋
|
|
18
|
+
|
|
19
|
+
| ์ํฉ | ๋์ ์ธ ๊ฒ |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| ์
๋ ฅ ํ๋์ ์ฆ์ ๋ฐ์(๊ฒ์, ํ ๊ธ) | [SearchField](search-field.md), [Switch](switch.md) |
|
|
22
|
+
| ํ ๋ฒ ํ์ธํ๊ณ ๋ซํ๋ ์ํ ํ๋ | [AlertDialog](alert-dialog.md) |
|
|
23
|
+
| ํ๋ ํ๋์ ๋ผ๋ฒจยท๋์๋งยท์ค๋ฅ ํ๋ ์ | [Field](field.md) |
|
|
24
|
+
| ์ฝ๊ด ๋์ ๋ฌถ์ | [Agreement](agreement.md) |
|
|
25
|
+
| ํ๋จ ๊ณ ์ ์ ์ถ ๋ฒํผ์ด ํ์ํ ๊ธด ํ๋ฉด | Web: Form ์์ ํ๋ + [BottomCTA](bottom-cta.md)(`actions`๋ ๋น์ด๋ค). Native: Form์ ๋ด์ฅ ์ ์ถ ๋ฒํผ์ ํญ์ ๊ทธ๋ฆฌ๋ฏ๋ก Form ์์ด ํ๋ + BottomCTA๋ก ์ ํ์ด ์ ์ถ์ ์์ ํ๋ค |
|
|
26
|
+
| Native์์ return ํค๋ก ์ ์ถํด์ผ ํ๋ ํผ(๋ก๊ทธ์ธยท์ฌ์ฌ์ ํผ) | Form ์์ด `TextField`(ref) + [Button](button.md)(`loading`). Native Form์ ๋ฐ์์ ์ ์ถ์ ๋ถ๋ฅผ ์ ์๋ค |
|
|
27
|
+
|
|
28
|
+
## ๊ณต๊ฐ ์ด๋ฆ๊ณผ import
|
|
29
|
+
|
|
30
|
+
| ์ด๋ฆ | ์ญํ | Web | Native |
|
|
31
|
+
| --- | --- | --- | --- |
|
|
32
|
+
| `Form` | ๊ธฐ๋ณธ | `@hjmds/react`, `/forms` | `@hjmds/react-native`, `/forms` |
|
|
33
|
+
| `createFormSubmitSession` | ๋ณด์กฐ โ ์ ์ถ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ผ๋ก ๋ฐ๊ฑฐ๋ ์ธ๋ง์ดํธ ๋ ์ ์ฐํด์ผ ํ ๋ ์ฐ๋ ์ธ์
| `@hjmds/design-contracts/components/form` | ๊ฐ์ |
|
|
34
|
+
| `resolveFirstInvalidFieldFocusTarget` | ๋ณด์กฐ โ ํ๋ ์์์ ๋ฌดํจ id๋ก ์ฒซ ์ค๋ฅ ํ๋๋ฅผ ๊ณ ๋ฅธ๋ค | `@hjmds/design-contracts/components/form` | ๊ฐ์ |
|
|
35
|
+
|
|
36
|
+
## ์ต์ ์ฌ์ฉ ์
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
// Web
|
|
40
|
+
import { useRef } from "react";
|
|
41
|
+
import { Form, TextField } from "@hjmds/react/forms";
|
|
42
|
+
import { Button } from "@hjmds/react/actions";
|
|
43
|
+
|
|
44
|
+
const nicknameInputRef = useRef<HTMLInputElement>(null);
|
|
45
|
+
|
|
46
|
+
<Form
|
|
47
|
+
aria-label={t("profile.formLabel")}
|
|
48
|
+
onSubmit={async () => {
|
|
49
|
+
setServerError(null);
|
|
50
|
+
if (nicknameError) { nicknameInputRef.current?.focus(); return; } // Web์ ์ฒซ ์ค๋ฅ ํฌ์ปค์ค๋ฅผ ์ ํ์ด ํ๋ค
|
|
51
|
+
try { await save(values); } catch { setServerError(t("profile.saveFailed")); }
|
|
52
|
+
}}
|
|
53
|
+
formError={serverError}
|
|
54
|
+
actions={<Button type="submit">{t("profile.save")}</Button>}
|
|
55
|
+
>
|
|
56
|
+
<TextField ref={nicknameInputRef} label={t("profile.nickname")} value={values.nickname}
|
|
57
|
+
onValueChange={setNickname} error={nicknameError ? t(nicknameError) : undefined} />
|
|
58
|
+
</Form>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
// Native
|
|
63
|
+
import { useEffect, useRef } from "react";
|
|
64
|
+
import type { TextInput } from "react-native";
|
|
65
|
+
import { Form } from "@hjmds/react-native/forms";
|
|
66
|
+
import { TextField } from "@hjmds/react-native/inputs";
|
|
67
|
+
|
|
68
|
+
const nicknameInputRef = useRef<TextInput>(null);
|
|
69
|
+
const firstInvalidRef = useRef<TextInput | null>(null);
|
|
70
|
+
// Form์ ๋ฒํผ์ ๋๋ฅธ ์๊ฐ current๋ฅผ ์ฝ๋๋ค. ๋ ๋ ์ค์ด ์๋๋ผ effect์์ ๊ฐฑ์ ํ๋ค(์ฒซ ๋ ๋์ ํ๋ ref๊ฐ null).
|
|
71
|
+
useEffect(() => { firstInvalidRef.current = nicknameError ? nicknameInputRef.current : null; }, [nicknameError]);
|
|
72
|
+
|
|
73
|
+
<Form
|
|
74
|
+
label={t("profile.formLabel")}
|
|
75
|
+
values={values}
|
|
76
|
+
onSubmit={save}
|
|
77
|
+
submitLabel={t("profile.save")}
|
|
78
|
+
fallbackErrorMessage={t("common.saveFailed")}
|
|
79
|
+
firstInvalidFieldRef={firstInvalidRef}
|
|
80
|
+
>
|
|
81
|
+
<TextField ref={nicknameInputRef} label={t("profile.nickname")} value={values.nickname}
|
|
82
|
+
onValueChange={setNickname} {...(nicknameError ? { error: t(nicknameError) } : {})} />
|
|
83
|
+
</Form>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## ์ถ๊ณผ ๊ธฐ๋ณธ๊ฐ
|
|
87
|
+
|
|
88
|
+
| prop | ๊ฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช
|
|
|
89
|
+
| --- | --- | --- | --- |
|
|
90
|
+
| `density` | `compact` ยท `comfortable` | `comfortable` | ํ๋ ์ฌ์ด ๊ฐ๊ฒฉ๋ง ๋ฐ๊พผ๋ค. ํ๋ ๋ด๋ถ ๋ฆฌ๋ฌ์ Field์ ๋ชซ์ด๋ค |
|
|
91
|
+
| Web `onSubmit` | `(event: FormEvent<HTMLFormElement>) => void \| Promise<void>` | ํ์ | ๊ธฐ๋ณธ ๋์์ ๋งํ ์๋ค. Promise๋ฉด ์ ์ฐ๊น์ง busy |
|
|
92
|
+
| Native `onSubmit` | `(values: Values) => void \| Promise<void>` | ํ์ | ๋๋ฅธ ์๊ฐ์ `values`๋ฅผ ๋ฐ๋๋ค. rejectํ๋ฉด `failed` |
|
|
93
|
+
| Native `status`/`defaultStatus` | `FormSubmitStatus` = `"idle"` ยท `"submitting"` ยท `"succeeded"` ยท `"failed"` | `idle` | ์ ์ดยท๋น์ ์ด ๋ ๋ค ๋๋ค |
|
|
94
|
+
| Native `onStatusChange` | `(status: FormSubmitStatus) => void` | โ | ๋ฌดํจ ์ ์ถ(`firstInvalidFieldRef` non-null)์์๋ ๋ถ๋ฆฌ์ง ์๋๋ค |
|
|
95
|
+
| Native `firstInvalidFieldRef` | `RefObject<TextInput \| null>` | โ | ๋๋ฅธ ์๊ฐ non-null์ด๋ฉด ๊ทธ ์นธ์ ํฌ์ปค์คํ๊ณ ์ ์ถํ์ง ์๋๋ค |
|
|
96
|
+
| Native `error` ยท Web `formError` | Native `string` ยท Web `ReactNode` | โ | ํผ ๋จ์ ์คํจ ๋ฌธ์ฅ |
|
|
97
|
+
| `busy`(Web) | `true` ยท `false` | `false` | `onSubmit`์ด Promise๋ฅผ ๋๋ ค์ฃผ๋ฉด ๋๋ ๋๊น์ง ์ค์ค๋ก busy๊ฐ ๋๋ค |
|
|
98
|
+
| `disabled`(Native) | `true` ยท `false` | `false` | โ |
|
|
99
|
+
|
|
100
|
+
- ์ธ์
(`createFormSubmitSession({ onSubmit, fallbackErrorMessage, resolveErrorMessage? })`)์ ์ํ๋
|
|
101
|
+
`getSnapshot()`์ด ์ฃผ๋ `{ status: "idle" | "submitting" | "succeeded" } | { status: "failed", message }`์ด๊ณ ,
|
|
102
|
+
`submit(values)`๋ `{ outcome: "succeeded" } | { outcome: "failed", message } | { outcome: "blocked", reason } | { outcome: "interrupted" }`๋ฅผ
|
|
103
|
+
๋๋ ค์ค๋ค. ์ธ์
์ ์ฐ๋ฉด Web์ `busy`ยท`formError`, Native๋ `status`ยท`error`๋ฅผ controlled๋ก ๋๊ธฐ๊ณ renderer ๋ด์ฅ ์ํ๋ฅผ ํจ๊ป ์ง์ค๋ก ๋์ง ์๋๋ค([๊ณ์ฝ](../../form.md)).
|
|
104
|
+
|
|
105
|
+
## ๋ฐฐ์น
|
|
106
|
+
|
|
107
|
+
| ํญ๋ชฉ | ๊ฐ | ๊ทผ๊ฑฐ |
|
|
108
|
+
| --- | --- | --- |
|
|
109
|
+
| ํฌ๊ธฐ | โ | โ |
|
|
110
|
+
| ๊ฐ๊ฒฉ | ํ๋ ์ฌ์ด ๊ฐ๊ฒฉ: `comfortable` `spacing.lg`(20), `compact` `spacing.sm`(12). Web์ ํ๋ ๋ฌถ์ ์๋ ํผ ์ค๋ฅ์ `actions`๊ฐ ๊ฐ๊ฐ `spacing.sm`(12) ์ ์ฌ๋ฐฑ์ ๊ฐ๊ณ , `actions` ์ ๋ฒํผ ๊ฐ๊ฒฉ์ `spacing.sm`(12)์ด๋ค. Native๋ ํ๋ยท์ค๋ฅยท๋ด์ฅ ์ ์ถ ๋ฒํผ์ ๊ฐ์ `fieldGap`์ผ๋ก ์ธ๋ก๋ก ์๋๋ค | `form.ts` `formRecipe`, `styles.css` `.hjm-form__*`, `react-native/src/forms.tsx` `Form` |
|
|
111
|
+
| ์์ยท์ ๋ ฌ | ์โ์๋ ์์๋ ํ๋๋ค โ ํผ ์ค๋ฅ โ ์ ์ถ ํ๋์ด๋ค. ํผ ์ค๋ฅ๋ ํญ์ ํ๋ ๋ฐ๋ก ์์ ์จ๋ค(`formRecipe.formError.position` `beforeActions`). Web `actions`๋ ์ค๋ฐ๊ฟ๋๋ ๊ฐ๋ก ์ค์ด๋ค. ๋ฒํผ ๋์ด๋ฉด [Button](button.md)์ ์์(๋ณด์กฐ โ ์ฃผ ํ๋)๋ฅผ ๋ฐ๋ฅธ๋ค. Native ๊ธฐ๋ณธ ์ ์ถ ๋ฒํผ์ ๋ง์ง๋ง์ ์์ผ๋ฉฐ actions ์ฌ๋กฏ์ผ๋ก ๊ต์ฒดํ๊ฑฐ๋ ์จ๊ธธ ์ ์๋ค | `form.ts` `formRecipe`, `styles.css` `.hjm-form__actions` |
|
|
112
|
+
| ๊ณ ์ ยท์คํฌ๋กค | ํผ ํ๋ฆ ์์ ๋๋ค. ๊ธด ํ๋ฉด์์ ์ ์ถ์ ํ๋จ์ ๊ณ ์ ํ๋ ค๋ฉด Web์ Form์ ์คํฌ๋กค ์์ญ์ ๋๊ณ `actions` ๋์ [BottomCTA](bottom-cta.md)๋ก ๋บ๋ค. Native๋ `actions={null}`๋ก ๋ด์ฅ ๋ฒํผ์ ์จ๊ธฐ๊ณ `FormHandle.submit()`์ผ๋ก ๊ฐ์ ์ ์ถ์ ํธ์ถํ๋ค | `react-native/src/forms.tsx` `Form` |
|
|
113
|
+
| ์ข์ ํญยทํฐ ๊ธ์ | โ | โ |
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
117
|
+
โ ๋ผ๋ฒจ โ
|
|
118
|
+
โ [ ์
๋ ฅ ] โ
|
|
119
|
+
โ ๋์๋ง โ โ fieldGap(comfortable lg 20 / compact sm 12)
|
|
120
|
+
โ ๋ผ๋ฒจ โ
|
|
121
|
+
โ [ ์
๋ ฅ ] โ
|
|
122
|
+
โ ํผ ์ค๋ฅ(์๋ฒ ๊ฑฐ๋ถ) โ โ Web sm 12
|
|
123
|
+
โ [์ทจ์] [์ ์ฅ] โ โ actions(Web) / ๋ด์ฅ ์ ์ถ ๋ฒํผ(Native)
|
|
124
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## ๊ผญ ์งํฌ ๊ฒ
|
|
128
|
+
|
|
129
|
+
- Native `FormHandle`์ `/forms`์์ importํ๋ค. `ref.current?.submit()`์ ํค๋ณด๋ returnยท์ํธ footer์์ ๋์ผ ์ ์ถ ๊ฒฝ๋ก๋ฅผ ํธ์ถํ๋ค. footer๋ฅผ ์ฐ๋ฉด `actions={null}`๋ก ๋ด์ฅ ๋ฒํผ์ ์จ๊ธฐ๊ณ , `onStatusChange`๋ก footer์ loading์ ์ฐ๊ฒฐํ๋ค. ์ด ๊ฒฝ๋ก๋ 2026-10-06 ๋ก๊ทธ์ธ/์ํธ ์ง์นจ์์ ์ค๋ณต ํผ ๊ตฌํ์ ํผํ๋ ค๊ณ ์ถ๊ฐํ๋ค.
|
|
130
|
+
|
|
131
|
+
- ํ๋ ์ค๋ฅ๋ ๊ฐ Field์ `error`์ ๋๋ค. Form์ ์ค๋ฅ(`formError`/`error`)๋ ์๋ฒ ๊ฑฐ๋ถยท๋คํธ์ํฌ ๊ฐ์
|
|
132
|
+
ํผ ๋จ์ ์คํจ์๋ง ์ด๋ค. ๊ฐ์ ๋ฌธ์ฅ์ ๋ ๊ณณ์ ๋ฃ์ง ์๋๋ค.
|
|
133
|
+
- ๋ฌธ๊ตฌ๋ ๋ชจ๋ i18n ํค๋ก ๋ฃ๋๋ค. Native๋ `fallbackErrorMessage`๊ฐ ํ์๋ค.
|
|
134
|
+
- ์งํ ์ค ํ์๋ Form์ ๋งก๊ธด๋ค. Web์ `fieldset`์ ๋นํ์ฑํํ๊ณ `aria-busy`๋ฅผ ๊ฑด๋ค. Native๋ ๋ด์ฅ ์ ์ถ
|
|
135
|
+
๋ฒํผ์ด `loading`์ด ๋๋ค. ๋ณ๋ Spinner๋ฅผ ๊ฒน์น์ง ์๋๋ค.
|
|
136
|
+
- ์ ์ถ ๋ฒํผ์ ํ๋๋ค. Web์ `actions`์ `type="submit"` Button์ ๋ฃ๊ณ , Native๋ ๋ด์ฅ ๋ฒํผ ๋๋ `actions` ์ฌ๋กฏ ์ค ํ๋๋ฅผ ์ด๋ค. Native Form
|
|
137
|
+
๋ด์ฅ ๋ฒํผ๊ณผ [BottomCTA](bottom-cta.md) ์ ์ถ์ ํจ๊ป ๋์ง ์๋๋ค.
|
|
138
|
+
- Native ํ๋ `error`๋ ์ ์ถ ์ ์(์
๋ ฅ ๋ณ๊ฒฝยทblur ๋) ์ด๋ฏธ ๋ณด์ด๊ณ ์์ด์ผ ํ๋ค. ๋ฌดํจ ์ ์ถ์ ํฌ์ปค์ค๋ง ์ฎ๊ธฐ๊ณ
|
|
139
|
+
์ด๋ค ์ฝ๋ฐฑ๋ ๋ถ๋ฅด์ง ์์ผ๋ฏ๋ก, ๊ทธ๋ ์ค๋ฅ๋ฅผ ์ผ๋ ์๋ฆฌ๊ฐ ์๋ค.
|
|
140
|
+
|
|
141
|
+
## ํ๋ซํผ ์ฐจ์ด
|
|
142
|
+
|
|
143
|
+
| ํญ๋ชฉ | Web | Native |
|
|
144
|
+
| --- | --- | --- |
|
|
145
|
+
| ์ ์ถ ๋ฒํผ | ์์(`actions` ์ฌ๋กฏ์ ์ ํ์ด ๋ฃ์) | ๋ด์ฅ(`submitLabel`); `actions`๋ก ๊ต์ฒด, `null`๋ก ์จ๊น |
|
|
146
|
+
| `onSubmit` ์ธ์ | `FormEvent`(๊ธฐ๋ณธ ๋์์ ๋งํ) | `values` |
|
|
147
|
+
| ์คํจ ์ฒ๋ฆฌ | Promise reject๋ฅผ ์ผํจ๋ค. ์ ํ์ด `formError`๋ฅผ ์ค์ | rejectํ๋ฉด `failed`๋ก ๋ฐ๊พธ๊ณ Error ๋ฉ์์ง ๋๋ `fallbackErrorMessage`๋ฅผ alert๋ก ํ์ |
|
|
148
|
+
| ์ฒซ ์ค๋ฅ ํ๋๋ก ์ด๋ | ์์. ์ ํ์ด `onSubmit` ์์์ ์ฒซ ๋ฌดํจ ์
๋ ฅ์ `.focus()`ํ๋ค | `firstInvalidFieldRef`: ๋ฒํผ์ ๋๋ฅธ ์๊ฐ `current`๋ฅผ ์ฝ๋๋ค. non-null์ด๋ฉด ๊ทธ ์
๋ ฅ์ focus์ ์คํฌ๋ฆฐ ๋ฆฌ๋ focus๋ฅผ ์ฎ๊ธฐ๊ณ `onSubmit`ยท`onStatusChange` ์์ด ๋ฉ์ถ๋ค(์ํ ๊ทธ๋๋ก). ref๋ `useEffect`์์ ๊ฐฑ์ ํ๊ณ , ์์ ๊ณ์ฐ์ `resolveFirstInvalidFieldFocusTarget(fieldOrder, invalidIds)`๋ฅผ ์ด๋ค |
|
|
149
|
+
| ์ ๊ทผ์ฑ ์ด๋ฆ | `<form>` ์์ฑ(`aria-label` ๋ฑ) | `label` ํ์ |
|
|
150
|
+
| ๋ฐฐ์น | `className`, `layoutStyle`(form ์์ฑ `style`๋ ๋ฐ์) | `layoutStyle`. `style`์ deprecated(๊ฐ๋ฐ ๋ชจ๋ 1ํ ๊ฒฝ๊ณ , ๋ค์ major ์ ๊ฑฐ) โ `layoutStyle` ๋๋ `density` |
|
|
151
|
+
| ๋ฐ์์ ์ ์ถ | `<form>`์ด๋ฏ๋ก `type="submit"` ๋ฒํผยทEnter๋ก ๋๋ค | `ref: Ref<FormHandle>`์ `submit()`์ผ๋ก ๊ฐ์ validationยท์ค๋ณต ์ ์ถ ๋ฐฉ์ง๋ฅผ ์คํ |
|
|
152
|
+
|
|
153
|
+
## ํจ์
|
|
154
|
+
|
|
155
|
+
- Native๋ reject๋ `Error.message`๋ฅผ ๊ทธ๋๋ก ํ๋ฉด์ ๋ณด์ธ๋ค. ์๋ฒ ์๋ฌธ์ด ๋
ธ์ถ๋์ง ์๋๋ก ์ ํ์ด ๋ฒ์ญ๋
|
|
156
|
+
๋ฉ์์ง๋ก ๊ฐ์ธ ๋์ง๊ฑฐ๋ ๋ฉ์์ง ์๋ ์คํจ๋ก ๋์ด `fallbackErrorMessage`๊ฐ ์ฐ์ด๊ฒ ํ๋ค.
|
|
157
|
+
- Web์ reject๋ฅผ ์ผํค๋ฏ๋ก `formError`๋ฅผ ์ค์ ํ์ง ์์ผ๋ฉด ์คํจ๊ฐ ํ๋ฉด์ ์๋ฌด๊ฒ๋ ๋จ๊ธฐ์ง ์๋๋ค.
|
|
158
|
+
- Native `firstInvalidFieldRef.current`๋ฅผ ๋ ๋ ์ค์ ๊ฐฑ์ ํ๋ฉด ์ฒซ ๋ ๋์๋ ํ๋ ref๊ฐ null์ด๋ผ ๋น ๊ฐ์ผ๋ก ๋ฐ๋ก ๋๋ฅด๋ฉด
|
|
159
|
+
๊ทธ๋๋ก `onSubmit`์ด ๋๊ฐ๋ค. ์ ์์ฒ๋ผ `useEffect`์์ ๊ฐฑ์ ํ๋ค.
|
|
160
|
+
- Native ๋ด์ฅ ์ ์ถ ๋ฒํผ์ tone์ด `primary`๋ก ๊ณ ์ ์ด๋ค. ์์
๋ก๊ทธ์ธ ์๋ ์ฌ์ฌ์ ํผ์ฒ๋ผ ๋ฎ์ tone์ด ํ์ํ๋ฉด Form ๋์
|
|
161
|
+
`actions`์ `Button tone="secondary"`๋ฅผ ๋ฃ๊ณ `ref.current?.submit()`์ ํธ์ถํ๋ค.
|
|
162
|
+
- Native `error`๋ `string`์ด๋ผ `exactOptionalPropertyTypes`์์ `undefined`๋ฅผ ๋ฐ์ง ์๋๋ค. ์กฐ๊ฑด๋ถ spread๋ก ๋๊ธด๋ค.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Grid
|
|
2
|
+
|
|
3
|
+
- ๋จ๊ณ: ์ปดํฌ๋ํธ
|
|
4
|
+
- ์ํ: ๋ฐฐํฌ
|
|
5
|
+
- ์ง์: Web ยท Native
|
|
6
|
+
- ์ ์ฉ: 1.12.1
|
|
7
|
+
- ๊ฒํ ์ผ: 2026-10-06
|
|
8
|
+
- ๊ทผ๊ฑฐ: [Responsive values์ Grid](../../responsive-grid.md), [๋ ์ด์์ primitive ํ์ ](../../layout-primitives.md), recipe `gridRecipe`(`src/grid.ts`)
|
|
9
|
+
- ์คํ ๋ฆฌ๋ถ: `๋ฐฐํฌ/์ปดํฌ๋ํธ/๋ ์ด์์/๊ฒฉ์`
|
|
10
|
+
|
|
11
|
+
## ์ธ์ ์ฐ๋
|
|
12
|
+
|
|
13
|
+
์นด๋ยทํ์ผ์ฒ๋ผ ๊ฐ์ ๋ชจ์์ ์์์ ์ฐฝ ํฌ๊ธฐ์ ๋ฐ๋ผ ์ด ์๋ฅผ ๋ฐ๊ฟ ๋ฐฐ์นํ ๋ ์ด๋ค. ์ด ์๋ ์ฐฝ ๋๋น๋ฅผ
|
|
14
|
+
`compact`ยท`medium`ยท`expanded`ยท`wide` ๋ค class๋ก ๋ฒ์ญํด ๊ณ ๋ฅด๊ณ , ์์ ์์๋ ๋ฐ๊พธ์ง ์๋๋ค(row-major).
|
|
15
|
+
|
|
16
|
+
## ์ฐ์ง ์์ ๋
|
|
17
|
+
|
|
18
|
+
| ์ํฉ | ๋์ ์ธ ๊ฒ |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| ํยท์ด ์๋ฏธ๊ฐ ์๋ ํ(ํค๋, ์ ๋ ฌ, ์ ํ) | [DataTable](data-table.md) |
|
|
21
|
+
| ๋์ด๊ฐ ์ ๊ฐ๊ฐ์ธ ์นด๋ ๋ฒฝ | [Masonry](masonry.md) |
|
|
22
|
+
| ํ ๋ฐฉํฅ์ผ๋ก ์๊ธฐยท๊ฐ๊ฒฉ | [Stack](stack.md) |
|
|
23
|
+
| ํ์ด์ง ํญยท์ฌ๋ฐฑ ์ ํ | [Container](container.md) |
|
|
24
|
+
| ๊ธด ๋ชฉ๋ก ๊ฐ์ํ | [VirtualList](virtual-list.md) |
|
|
25
|
+
|
|
26
|
+
## ๊ณต๊ฐ ์ด๋ฆ๊ณผ import
|
|
27
|
+
|
|
28
|
+
| ์ด๋ฆ | ์ญํ | Web | Native |
|
|
29
|
+
| --- | --- | --- | --- |
|
|
30
|
+
| `Grid` | ๊ธฐ๋ณธ | `@hjmds/react`, `/layout` | `@hjmds/react-native`, `/primitives` |
|
|
31
|
+
|
|
32
|
+
`GridReveal`์ ์ด๋ฆ์ด ๋น์ทํ์ง๋ง Image ๊ณ์ฝ์ ํ์ฅ์ด๋ฉฐ Grid์ ๋ฌด๊ดํ๋ค.
|
|
33
|
+
|
|
34
|
+
## ์ต์ ์ฌ์ฉ ์
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
// Web
|
|
38
|
+
import { Grid } from "@hjmds/react/layout";
|
|
39
|
+
|
|
40
|
+
<Grid columns={{ compact: 1, medium: 2, expanded: 3 }} gap={{ compact: "md" }} minColumnWidth={{ compact: 160 }}>
|
|
41
|
+
{items.map((item) => <ProductCard key={item.id} item={item} />)}
|
|
42
|
+
</Grid>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
// Native
|
|
47
|
+
import { Grid } from "@hjmds/react-native/primitives";
|
|
48
|
+
|
|
49
|
+
<Grid columns={{ compact: 2, medium: 3 }} gap={{ compact: { row: "lg", column: "sm" } }}>
|
|
50
|
+
{items.map((item) => <ProductCard key={item.id} item={item} />)}
|
|
51
|
+
</Grid>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## ์ถ๊ณผ ๊ธฐ๋ณธ๊ฐ
|
|
55
|
+
|
|
56
|
+
| prop | ๊ฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช
|
|
|
57
|
+
| --- | --- | --- | --- |
|
|
58
|
+
| `columns` | `ResponsiveValue<number>`(ํ์) | โ | `compact`๊ฐ ํ์์ด๊ณ ๋น class๋ ์ข์ ์ชฝ ๊ฐ์ ๋ฌผ๋ ค๋ฐ๋๋ค. ์์ ์ ์๋ง ๋ฐ๋๋ค |
|
|
59
|
+
| `gap` | `ResponsiveValue<GridGap>`. `GridGap`์ token(`none` ยท `xxs`~`xxxl`) ๋๋ `{ row, column }` | `md`(16) | ํญ์ ๊ตฌ๊ฐ ๊ฐ์ฒด๋ก ์ค๋ค: `{ compact: "md" }`, `{ compact: "sm", expanded: "lg" }`, `{ compact: { row: "lg", column: "sm" } }`. ๋งจ token `gap="md"`๋ ํ์
์ค๋ฅ๋ค |
|
|
60
|
+
| `minColumnWidth` | `ResponsiveValue<number>` | โ | ์ ํ. ์ด์ด ์ด ํญ๋ณด๋ค ์ข์์ง๊ธฐ ์ ์ ์ด ์๋ฅผ ์ค์ธ๋ค. ํ ์ด์ ๊ทธ๋๋ก ์ค์ด๋ ๋ค |
|
|
61
|
+
| `availableWidth` | ์ซ์ | โ | ์๋ตํ๋ฉด ๋ ๋๋ ์ปจํ
์ด๋๋ฅผ ์ฐ๋ค(Web `ResizeObserver`, Native `onLayout`) |
|
|
62
|
+
| Native `onLayoutResolved` | `(layout: ResolvedGridLayout) => void` | โ | `layout`์ `{ windowClass, flow: "row-major", requestedColumns, columns, rowGap, columnGap, columnWidth }`. ์ด ์๊ฐ ์ค์๋์ง(`columns < requestedColumns`) ํ์ธํ ๋ ์ด๋ค |
|
|
63
|
+
| Web `windowWidth` | ์ซ์ | `window.innerWidth`(SSR 320) | ํ
์คํธยทSSR์์ ์ฐฝ ๋๋น๋ฅผ ๋ฎ๋๋ค |
|
|
64
|
+
|
|
65
|
+
- `ResponsiveValue<V>`๋ `{ compact: V; medium?: V; expanded?: V; wide?: V }`๋ค(`@hjmds/design-contracts/responsive`).
|
|
66
|
+
|
|
67
|
+
## ๋ฐฐ์น
|
|
68
|
+
|
|
69
|
+
| ํญ๋ชฉ | ๊ฐ | ๊ทผ๊ฑฐ |
|
|
70
|
+
| --- | --- | --- |
|
|
71
|
+
| ํฌ๊ธฐ | Grid๋ ๋ถ๋ชจ ํญ์ ์ฑ์ฐ๊ณ ๋ฐ๊นฅ ์ฌ๋ฐฑ์ ๊ฐ์ง ์๋๋ค. ์ด ํญ์ Grid๊ฐ ์ค์ ๋ก ๋ฐ์ ํญ(`availableWidth`)์ผ๋ก ๊ณ์ฐํ๋ค | `grid.ts` `resolveGridLayout`, `styles.css` `.hjm-grid` |
|
|
72
|
+
| ๊ฐ๊ฒฉ | ๊ธฐ๋ณธ ๊ฐ๊ฒฉ `md`(16). ํ์ด์ง ์ข์ฐ ์ฌ๋ฐฑ์ [Container](container.md)ยทLayout์ด ์ค๋ค | `grid.ts` `gridDefaults` |
|
|
73
|
+
| ์์ยท์ ๋ ฌ | ์์ ์์๋ ํ ์ฐ์ (row-major)์ด๊ณ RTL์์๋ ์ค๋ฅธ์ชฝ์์ ์์ํ๋ค | `grid.ts` `gridDefaults` |
|
|
74
|
+
| ๊ณ ์ ยท์คํฌ๋กค | โ | โ |
|
|
75
|
+
| ์ข์ ํญยทํฐ ๊ธ์ | ์ฐฝ ๋๋น class๋ `breakpoint` ๊ธฐ์ค์ด๋ค: `compact` 0 ยท `medium` 600 ยท `expanded` 960 ยท `wide` 1280. ์ด ์๋ ์ด class๋ก ๊ณ ๋ฅธ๋ค. `columns`๋ `compact` ๊ฐ๋ถํฐ ์ ํ๊ณ ๋์ class์์ ๋๋ฆฐ๋ค(๋น class๋ ์ข์ ์ชฝ ๊ฐ์ ๋ฌผ๋ ค๋ฐ๋๋ค). `minColumnWidth`๋ฅผ ์ฃผ๋ฉด ์ด์ด ๊ทธ ํญ๋ณด๋ค ์ข์์ง๊ธฐ ์ ์ ์ด ์๊ฐ ์ค์ด๋ ๋ค. ํฐ ๊ธ์๋ก ์นด๋๊ฐ ์ข์์ง ๋ ์ด ๊ฐ์ผ๋ก ์ด์ ์ค์ธ๋ค | `responsive.ts` `resolveWindowClass`, `foundations.ts` `breakpoint` |
|
|
76
|
+
|
|
77
|
+
## ๊ผญ ์งํฌ ๊ฒ
|
|
78
|
+
|
|
79
|
+
- ๊ฐ๊ฒฉ์ gap token์ผ๋ก๋ง ์ค๋ค. ์ซ์ px๋ ์์ margin์ผ๋ก ๊ฐ๊ฒฉ์ ๋ง๋ค์ง ์๋๋ค.
|
|
80
|
+
- class ์ด๋ฆ ์คํยท์ ์ ์๋ keyยท0 ์ดํ ์ด ์๋ resolver๊ฐ ๋์ง๋ค. ์กฐ์ฉํ ๋ฌด์๋์ง ์๋๋ค.
|
|
81
|
+
- Grid๋ ์์์ ์๋ฏธ๋ฅผ ๋ง๋ค์ง ์๋๋ค. ๋ชฉ๋ก์ด๋ฉด ์์์ ์ ํ์ด ๋ชฉ๋ก ์๋ฏธ๋ฅผ ์ค๋ค.
|
|
82
|
+
- Web ๋ฐฐ์น๋ `layoutStyle`๋ก ํ๋ค(์ยทํ
๋๋ฆฌ ๋ฑ ์๊ฐ key๋ ๋ฐ์ง ์๋๋ค).
|
|
83
|
+
|
|
84
|
+
## ํ๋ซํผ ์ฐจ์ด
|
|
85
|
+
|
|
86
|
+
| ํญ๋ชฉ | Web | Native |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| ๊ตฌํ | CSS Grid(`repeat(n, minmax(0, 1fr))`) | `flexWrap` ํ + ์์๋ง๋ค ๊ณ ์ ํญ ์
View |
|
|
89
|
+
| ์ฐฝ ๋๋น | `window.innerWidth`, `windowWidth`๋ก ๋ฎ๊ธฐ(ํ
์คํธยทSSR) | `useWindowDimensions` |
|
|
90
|
+
| SSR ์ฒซ ๋ ๋ | ์ฐฝ ๋๋น๋ฅผ ๋ชจ๋ฅด๋ฉด 320์ผ๋ก ๊ณ์ฐ | ํด๋น ์์ |
|
|
91
|
+
| ์
์คํ์ผ | ์์(์์์ด ์ง์ ) | `itemStyle` |
|
|
92
|
+
| ๊ฒฐ๊ณผ ํต์ง | `data-columns`ยท`data-state` ์์ฑ | `onLayoutResolved(layout)` |
|
|
93
|
+
| ๋ฐฐ์น prop | `layoutStyle`, `className`, `style` | `style`(View). Grid๋ layout primitive๋ผ deprecated ๋์์ด ์๋๊ณ `layoutStyle`์ ์๋ค |
|
|
94
|
+
| RTL | CSS๊ฐ ์ฒ๋ฆฌ | Provider์ `environment.direction` ์ ์ฉ |
|
|
95
|
+
|
|
96
|
+
## ํจ์
|
|
97
|
+
|
|
98
|
+
- Native๋ ์์๋ง๋ค ์
View๋ฅผ ๊ฐ์ธ๊ณ ํญ์ ๊ธฐ๊ธฐ ํฝ์
๋จ์๋ก ๋ด๋ฆผํ๋ค. ์
์์ฒด๋ฅผ ๊พธ๋ฐ ๋๋ ์์์ด ์๋๋ผ `itemStyle`์ ์ด๋ค.
|
|
99
|
+
- Web SSR์์๋ 320 ๊ธฐ์ค ์ด ์๋ก ๊ทธ๋ ธ๋ค๊ฐ ํ์ด๋๋ ์ด์
๋ค ์ค์ ๋๋น๋ก ๋ฐ๋๋ค. ์ฒซ ํ๋ฉด ์ด ์๊ฐ ์ค์ํ๋ฉด `windowWidth`๋ฅผ ๋๊ธด๋ค.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Heading
|
|
2
|
+
|
|
3
|
+
- ๋จ๊ณ: ์ปดํฌ๋ํธ
|
|
4
|
+
- ์ํ: ๋ฐฐํฌ
|
|
5
|
+
- ์ง์: Web ยท Native
|
|
6
|
+
- ์ ์ฉ: 1.12.1
|
|
7
|
+
- ๊ฒํ ์ผ: 2026-10-06
|
|
8
|
+
- ๊ทผ๊ฑฐ: [Heading](../../heading.md), recipe `headingRecipe`(`src/heading.ts`)
|
|
9
|
+
- ์คํ ๋ฆฌ๋ถ: `๋ฐฐํฌ/์ปดํฌ๋ํธ/๊ธ์์ ์์ด์ฝ/์ ๋ชฉ`
|
|
10
|
+
|
|
11
|
+
## ์ธ์ ์ฐ๋
|
|
12
|
+
|
|
13
|
+
์๋ฆฌ๋ฅผ ๋ชจ๋ฅด๋ ํฐ ์ ๋ชฉ ๊ธ์ ํ๋๊ฐ ํ์ํ ๋ ์ด๋ค. ๋๋ฉ ํ์ด๋ก, ๊ฒฐ๊ณผ ํ๋ฉด์ ํฐ ์ ๋ชฉ,
|
|
14
|
+
์นด๋ยทํยท๋น ์ํ ์์ ์ ๋ชฉ์ฒ๋ผ `Text`์ ์ต๋ ํฌ๊ธฐ(24px)๋ก๋ ๋ชจ์๋ผ๊ฑฐ๋ ๋ฌธ์ ์ ๋ชฉ ๋จ๊ณ๋ฅผ
|
|
15
|
+
์ค์ ๋ก ๋ด์ผ ํ๋ ์๋ฆฌ๋ค. ์ฃผ๋ณ ์ฌ๋ฐฑ์ ๊ฐ์ง ์๋๋ค. ๋ด๋ ๋ธ๋ก์ด ์ ํ๋ค.
|
|
16
|
+
|
|
17
|
+
## ์ฐ์ง ์์ ๋
|
|
18
|
+
|
|
19
|
+
| ์ํฉ | ๋์ ์ธ ๊ฒ |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| ํ๋ฉด ์ฒซ ์ ๋ชฉ(์ฌ๋ฐฑยทeyebrowยท๋ณด์กฐ ๋ฌธ์ฅยท๋ณด์กฐ ํ๋ ํฌํจ) | [Top](top.md) |
|
|
22
|
+
| ๋ณธ๋ฌธ ์ค๊ฐ ๋ฌถ์์ ์ ๋ชฉ ํยท์ค๋ช
| [Section](section.md) |
|
|
23
|
+
| ๋ณธ๋ฌธยท๋ผ๋ฒจยท์บก์
ํฌ๊ธฐ์ ๊ธ์ | [Text](text.md) |
|
|
24
|
+
|
|
25
|
+
## ๊ณต๊ฐ ์ด๋ฆ๊ณผ import
|
|
26
|
+
|
|
27
|
+
| ์ด๋ฆ | ์ญํ | Web | Native |
|
|
28
|
+
| --- | --- | --- | --- |
|
|
29
|
+
| `Heading` | ๊ธฐ๋ณธ | `@hjmds/react`, `/heading` | `@hjmds/react-native`, `/heading` |
|
|
30
|
+
|
|
31
|
+
## ์ต์ ์ฌ์ฉ ์
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
// Web
|
|
35
|
+
import { Heading } from "@hjmds/react/heading";
|
|
36
|
+
|
|
37
|
+
<Heading level="level2">{t("result.title")}</Heading>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
// Native
|
|
42
|
+
import { Heading } from "@hjmds/react-native/heading";
|
|
43
|
+
|
|
44
|
+
<Heading level="level4" semanticLevel={3}>{t("card.title")}</Heading>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## ์ถ๊ณผ ๊ธฐ๋ณธ๊ฐ
|
|
48
|
+
|
|
49
|
+
| prop | ๊ฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช
|
|
|
50
|
+
| --- | --- | --- | --- |
|
|
51
|
+
| `level`(ํ์) | `level1` 40px ยท `level2` 32px ยท `level3` 24px ยท `level4` 20px ยท `level5` 18px | โ | recipe ๊ธฐ๋ณธ์ `level3`์ด์ง๋ง ๋ renderer ๋ชจ๋ prop์ ํ์๋ก ๋ฐ๋๋ค |
|
|
52
|
+
| `semanticLevel` | 1~6 | `level`์ ์ซ์ | ํฌ๊ฒ ๋ณด์ด์ง๋ง ๊ตฌ์กฐ์ h4์ธ ์นด๋ ์ ๋ชฉ์ฒ๋ผ ์๊ฐ ํฌ๊ธฐ์ ๋ฌธ์ ๋จ๊ณ๊ฐ ๋ค๋ฅด๋ฉด ๋์ ๋ฐ๋ก ์ ๋๋ค |
|
|
53
|
+
| `layoutStyle` | ๋ฐฐ์น key(marginยทํญยท์ ๋ ฌ ๋ฑ) | โ | ๋ฐ๊นฅ ๋ฐฐ์น๋ง. ํฌ๊ธฐยท์ค ๋์ดยท๊ตต๊ธฐยท์์ ๋ฐ์ง ์๋๋ค |
|
|
54
|
+
|
|
55
|
+
- ๋ฒ์ ๋ฐ ๊ฐ์ `TypeError`๋ก ๊ฑฐ๋ถ๋๋ค.
|
|
56
|
+
|
|
57
|
+
## ๋ฐฐ์น
|
|
58
|
+
|
|
59
|
+
| ํญ๋ชฉ | ๊ฐ | ๊ทผ๊ฑฐ |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| ํฌ๊ธฐ | ์ค ๋์ด: `level1` 48 ยท `level2` 40 ยท `level3` 32 ยท `level4` 28 ยท `level5` 26 | `foundations.ts` `heading`, `typography` |
|
|
62
|
+
| ๊ฐ๊ฒฉ | Heading์ margin์ด 0์ด๋ค(Web `.hjm-heading`). ์์๋ ๊ฐ๊ฒฉ์ ๊ฐ์ธ๋ StackยทSection์ `gap`์ด ์ ํ๋ค | `styles.css` `.hjm-heading` |
|
|
63
|
+
| ์์ยท์ ๋ ฌ | ํ ๋ธ๋ก์ Heading์ ํ๋๋ฅผ ๋งจ ์์ ๋๋ค | โ |
|
|
64
|
+
| ๊ณ ์ ยท์คํฌ๋กค | โ | โ |
|
|
65
|
+
| ์ข์ ํญยทํฐ ๊ธ์ | ํญ์ ๋ถ๋ชจ๋ฅผ ๋ฐ๋ฅด๊ณ ๊ธด ์ ๋ชฉ์ ์ค๋ฐ๊ฟํ๋ค. Web์ `--hjm-text-scale`๋ก ํฌ๊ธฐยท์ค ๋์ด๋ฅผ ํจ๊ป ํค์ด๋ค | `styles.css` `.hjm-heading` |
|
|
66
|
+
|
|
67
|
+
## ๊ผญ ์งํฌ ๊ฒ
|
|
68
|
+
|
|
69
|
+
- ์ ๋ชฉ ๋ฌธ๊ตฌ๋ i18n ํค๋ก ๋ฃ๋๋ค. Web์ ์ค๋ฐ๊ฟ์ `overflow-wrap: anywhere`๋ก ์ฒ๋ฆฌํ๋ฏ๋ก ์๋ฅด์ง ์๋๋ค.
|
|
70
|
+
- ํฌ๊ธฐ๋ฅผ ๋ง์ถ๋ ค๊ณ `semanticLevel`์ ๋ฐ๊พธ๊ฑฐ๋, ๊ตฌ์กฐ๋ฅผ ๋ง์ถ๋ ค๊ณ `level`์ ์ค์ด์ง ์๋๋ค.
|
|
71
|
+
- ์ ๋ชฉ ์๋ ๊ฐ๊ฒฉ์ Heading์ ์ฃผ์ง ์๊ณ ๊ฐ์ธ๋ StackยทSection์ gap์ผ๋ก ์ ํ๋ค.
|
|
72
|
+
- ์ยทํฌ๊ธฐยท๊ตต๊ธฐ๋ฅผ ๋ฎ์ง ์๋๋ค. ์์ ํ
๋ง์ `content.primary`๋ฅผ ๋ฐ๋ฅธ๋ค.
|
|
73
|
+
|
|
74
|
+
## ํ๋ซํผ ์ฐจ์ด
|
|
75
|
+
|
|
76
|
+
| ํญ๋ชฉ | Web | Native |
|
|
77
|
+
| --- | --- | --- |
|
|
78
|
+
| ์ถ๋ ฅ | `h1`~`h6` ์์ | `Text` + `accessibilityRole="header"` + `aria-level` |
|
|
79
|
+
| ์ถ๊ฐ prop | `HTMLAttributes`(`id`, `className`, `style` ๋ฑ), ref | ์์ |
|
|
80
|
+
| ๋ฐฐ์น | `layoutStyle`. `style`๋ ํฉ์ณ์ง๋ค(ํฌ๊ธฐ ๋ณ์๋ ๋ฎ์ด์ง ์์) | `layoutStyle`. `style`(TextStyle)์ deprecated(๊ฐ๋ฐ ๋ชจ๋ 1ํ ๊ฒฝ๊ณ , ๋ค์ major ์ ๊ฑฐ) โ `layoutStyle` ๋๋ `level` |
|
|
81
|
+
| ๊ธ์ ๋ฐฐ์จ | `--hjm-text-scale`๋ก ํ๋ | Native `Text`์ ๊ธ์ ๋ฐฐ์จ |
|
|
82
|
+
|
|
83
|
+
## ํจ์
|
|
84
|
+
|
|
85
|
+
- Web `style`์ ์ด์ ๋ฒ๋ ค์ง์ง ์๊ณ ํฉ์ณ์ง๋ค. ๋ค๋ง ํฌ๊ธฐยท์ค ๋์ดยท๊ตต๊ธฐ ๋ณ์(`--hjm-heading-*`)๋ recipe๊ฐ ๋ง์ง๋ง์ ๋ฎ์ผ๋ฏ๋ก
|
|
86
|
+
`style`๋ก ํฌ๊ธฐ๋ฅผ ๋ฐ๊ฟ ์ ์๋ค. ๋ฐฐ์น๋ `layoutStyle`๋ก ์ค๋ค.
|
|
87
|
+
- Native `style`(deprecated)์ ์์ง ๋ง์ง๋ง์ ํฉ์ณ์ ธ ์ยทํฌ๊ธฐ๊น์ง ๋ฐ๋๋ค. ์ฐ์ง ๋ง๊ณ `layoutStyle`ยท`level`๋ก ์ฎ๊ธด๋ค.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# IconButton
|
|
2
|
+
|
|
3
|
+
- ๋จ๊ณ: ์ปดํฌ๋ํธ
|
|
4
|
+
- ์ํ: ๋ฐฐํฌ
|
|
5
|
+
- ์ง์: Web ยท Native
|
|
6
|
+
- ์ ์ฉ: 1.12.1
|
|
7
|
+
- ๊ฒํ ์ผ: 2026-10-06
|
|
8
|
+
- ๊ทผ๊ฑฐ: recipe `iconButtonRecipe`(`src/icon-button-recipe.ts`), NotificationBell ๊ณ์ฝ: [Compound controls](../../compound-controls.md#notificationbell)
|
|
9
|
+
- ์คํ ๋ฆฌ๋ถ: `๋ฐฐํฌ/์ปดํฌ๋ํธ/๋์/์์ด์ฝ ๋ฒํผ` ยท `๋ฐฐํฌ/์ปดํฌ๋ํธ/์ํ์ ์๋ฆผ/์๋ฆผ ๋ฒจ`
|
|
10
|
+
|
|
11
|
+
## ์ธ์ ์ฐ๋
|
|
12
|
+
|
|
13
|
+
๋ณด์ด๋ ๊ธ์ ์์ด ์์ด์ฝ๋ง์ผ๋ก ํ์ํ๋ ํ๋์ ์ด๋ค. ๋ซ๊ธฐ, ๋ค๋ก, ๋๋ณด๊ธฐ, ๊ณต์ , ์ฆ๊ฒจ์ฐพ๊ธฐ ํ ๊ธ์ฒ๋ผ
|
|
14
|
+
๋ชจ์์ด ๋๋ฆฌ ํตํ๋ ํ๋์ด๋ค. ์ด๋ฆ์ ํ์งํํ `label`์ด ์ ๊ทผ์ฑ ์ด๋ฆ์ผ๋ก ์ฝํ๋ค.
|
|
15
|
+
๋ฏธํ์ธ ์๋ฆผ ์๊ฐ ๋ถ์ ์๋ฆผ ์ง์
์ ์ `NotificationBell`์ ์ด๋ค.
|
|
16
|
+
|
|
17
|
+
## ์ฐ์ง ์์ ๋
|
|
18
|
+
|
|
19
|
+
| ์ํฉ | ๋์ ์ธ ๊ฒ |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| ๊ธ์ ๋ผ๋ฒจ์ด ์๋ ํ๋ | [Button](button.md) |
|
|
22
|
+
| ๋ค๋ฅธ ํ์ด์งยทURL๋ก ์ด๋ | [Link](link.md) |
|
|
23
|
+
| ํ๋ฉด ์์ ๋ ์๋ ์ฃผ ํ๋ | [FloatingActionButton](floating-action-button.md) |
|
|
24
|
+
| ์ฌ๋ฌ ์์ด์ฝ ์ค ํ๋๋ฅผ ๊ณ ๋ฆ | [ToggleGroup](toggle-group.md), [SegmentedControl](segmented-control.md) |
|
|
25
|
+
| ๋๋ฅผ ์ ์๋ ๊ทธ๋ฆผ | [Icon](icon.md) |
|
|
26
|
+
|
|
27
|
+
## ๊ณต๊ฐ ์ด๋ฆ๊ณผ import
|
|
28
|
+
|
|
29
|
+
| ์ด๋ฆ | ์ญํ | Web | Native |
|
|
30
|
+
| --- | --- | --- | --- |
|
|
31
|
+
| `IconButton` | ๊ธฐ๋ณธ | `@hjmds/react`, `/actions` | `@hjmds/react-native`, `/actions`, `/bottom-cta` |
|
|
32
|
+
| `NotificationBell` | ํ์ฅ โ ๋ฏธํ์ธ ์ ๋ฐฐ์ง + ์ฆ๊ฐ ์ ํ ๋ฒ ํ๋ค๋ฆผ | `/notification-bell` | `/notification-bell` |
|
|
33
|
+
|
|
34
|
+
`NotificationBell`์ granular subpath๋ก๋ง import ๋๋ค. ์ถ๊ฐ peer๋ ์๋ค.
|
|
35
|
+
|
|
36
|
+
## ์ต์ ์ฌ์ฉ ์
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
// Web
|
|
40
|
+
import { IconButton } from "@hjmds/react/actions";
|
|
41
|
+
import { Icon } from "@hjmds/react/display";
|
|
42
|
+
|
|
43
|
+
<IconButton label={t("common.close")} onClick={close}>
|
|
44
|
+
<Icon name="close" />
|
|
45
|
+
</IconButton>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
// Native
|
|
50
|
+
import { IconButton } from "@hjmds/react-native/actions";
|
|
51
|
+
import { Icon } from "@hjmds/react-native/primitives";
|
|
52
|
+
import { NotificationBell } from "@hjmds/react-native/notification-bell";
|
|
53
|
+
// renderGlyph๋ createLucideGlyph๋ก ๋ง๋ ๋ค(icon.md ์ฐธ๊ณ ).
|
|
54
|
+
|
|
55
|
+
<IconButton label={t("common.more")} onPress={openMenu}>
|
|
56
|
+
<Icon descriptor={{ name: "more" }} renderGlyph={renderGlyph} />
|
|
57
|
+
</IconButton>
|
|
58
|
+
|
|
59
|
+
<NotificationBell
|
|
60
|
+
label={t("inbox.open", { count: unread })}
|
|
61
|
+
count={unread}
|
|
62
|
+
icon={<Icon descriptor={{ name: "notifications" }} renderGlyph={renderGlyph} />}
|
|
63
|
+
onPress={openInbox}
|
|
64
|
+
active={isFocused}
|
|
65
|
+
/>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## ์ถ๊ณผ ๊ธฐ๋ณธ๊ฐ
|
|
69
|
+
|
|
70
|
+
| prop | ๊ฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช
|
|
|
71
|
+
| --- | --- | --- | --- |
|
|
72
|
+
| `tone` | `ghost` ยท `primary` ยท `secondary` ยท `danger` | `ghost` | โ |
|
|
73
|
+
| `size` | `small` 36 ยท `medium` 44 ยท `large` 52 | `medium` | `small`์ ํฐ์น ์ฌ๋ฐฑ 4 ์ถ๊ฐ. ์์ด์ฝ ํฌ๊ธฐ๋ ๊ฐ๊ฐ `sm`ยท`md`ยท`lg` |
|
|
74
|
+
| `shape` | `rounded` ยท `circle` | `rounded` | โ |
|
|
75
|
+
| `selected` | `true` ยท `false` | โ | ์ฃผ๋ฉด ํ ๊ธ์ด ๋๋ค(Web `aria-pressed`, Native ์ ๊ทผ์ฑ state) |
|
|
76
|
+
| `loading` | `true` ยท `false` | โ | ๋๋ฆ์ ๋ง๊ณ ์คํผ๋๋ก ๋ฐ๊พผ๋ค. Native๋ ๊ธฐ๋ณธ์ผ๋ก ํฌ์ปค์ค๋ฅผ ์ ์งํ๋ค(`disableWhileLoading`์ผ๋ก๋ง ์ ๋์) |
|
|
77
|
+
| `loading` ์ค `renderLoadingIndicator`(Native) | `(props: { color: string; size: "small" }) => ReactNode` | ๊ธฐ๋ณธ ์คํผ๋ | ์คํผ๋๋ฅผ ๋ฐ๊ฟ ๋๋ง |
|
|
78
|
+
| Web `onClick` ยท Native `onPress` | Web `(event: MouseEvent<HTMLButtonElement>) => void` ยท Native `(event: GestureResponderEvent) => void` | โ | Native๋ `onLongPress`๋ ๋ฐ๋๋ค |
|
|
79
|
+
| `count`(NotificationBell) | 0 ์ด์์ ์ ์ | โ | ์๋๋ฉด `RangeError` |
|
|
80
|
+
| `onPress`(NotificationBell) | `() => void` | ํ์ | WebยทNative ๋ชจ๋ `onPress`๋ค(Web๋ `onClick`์ด ์๋๋ค) |
|
|
81
|
+
| `active`(NotificationBell) | `true` ยท `false` | `true` | โ |
|
|
82
|
+
| `disabled`(NotificationBell) | `true` ยท `false` | `false` | โ |
|
|
83
|
+
| Web `layoutStyle`(NotificationBell) | `HjmCompositionStyleProp` | โ | ๋ฃจํธ ๋ฐฐ์น. ๊ธฐ๋ณธ `alignSelf: "flex-start"`๋ณด๋ค ์ฐ์ ํ์ง๋ง `width`๋ ๋ฌด์๋๋ค(๋ฃจํธ๋ ํญ์ `max-content`). ๋ ์๋ ๋ฐฐ์ง๊ฐ ๋ฃจํธ์ ๋ ๋ชจ์๋ฆฌ์ ๋ถ์ด ์์ด ๋ฃจํธ๊ฐ ๋์ด์ง๋ฉด ์์ด์ฝ์์ ๋จ์ด์ง๊ธฐ ๋๋ฌธ์ด๋ค. marginยท`alignSelf`๋ก ๋ฐฐ์นํ๋ค. ๋ฏธ๊ฒ์(1.12.1 ์ดํ) |
|
|
84
|
+
|
|
85
|
+
## ๋ฐฐ์น
|
|
86
|
+
|
|
87
|
+
| ํญ๋ชฉ | ๊ฐ | ๊ทผ๊ฑฐ |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| ํฌ๊ธฐ | ์ง๋ฆ: `small` 36 ยท `medium` 44 ยท `large` 52. `small`์ hitSlop 4๋ฅผ ๋ํด ํฐ์น ์์ญ์ 44(`control.minTouchTarget`)์ ๋ง์ถ๋ค. ์์ด์ฝ ํฌ๊ธฐ๋ `glyph.sm` 20 ยท `glyph.md` 24 ยท `glyph.lg` 28์ด๋ค | `icon-button-recipe.ts` `iconButtonRecipe` |
|
|
90
|
+
| ๊ฐ๊ฒฉ | [TopBar](top-bar.md)๊ฐ ์ฌ๋กฏ ์ต์ ํญ 44, ์ฌ๋กฏ ์ ๊ฐ๊ฒฉ `spacing.xs`(8), ์ข์ฐ ์ฌ๋ฐฑ `spacing.md`(16)๋ฅผ ์ค๋ค. NotificationBell ๋ฐฐ์ง๊ฐ ๋ฒํผ ๋ฐ์ผ๋ก ๋์น ์ ์์ผ๋ ์ ์์์ ๊ฐ๊ฒฉ์ ๋๋ค | `component-recipes.ts` `topBarRecipe`, `styles.css` `.hjm-top-bar` |
|
|
91
|
+
| ์์ยท์ ๋ ฌ | ํ๋ฉด ๋งจ ์์์๋ TopBar์ `leading`(๋ค๋กยท๋ซ๊ธฐ)๊ณผ `actions`/`trailing`(๋๋ณด๊ธฐยท๊ณต์ ยท์๋ฆผ)์ ๋๋ค. ํ ๋(๋ชฉ๋กยท์นด๋)์๋ `size="small"` ๋๋ `medium`, `tone="ghost"`๋ฅผ ์ด๋ค. NotificationBell์ IconButton ์ค๋ฅธ์ชฝ ์ ๋ชจ์๋ฆฌ(`end: 0`, `top: 0`)์ [CounterBadge](counter-badge.md) `floating`์ ๊ฒน์น๋ค | `react-native/src/notification-bell.tsx` |
|
|
92
|
+
| ๊ณ ์ ยท์คํฌ๋กค | โ | โ |
|
|
93
|
+
| ์ข์ ํญยทํฐ ๊ธ์ | โ | โ |
|
|
94
|
+
|
|
95
|
+
```text
|
|
96
|
+
TopBar
|
|
97
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
98
|
+
โ [โ] ํ๋ฉด ์ ๋ชฉ [๋ฒจยณ][โฏ] โ โ leading 44 / trailing ๊ฐ๊ฒฉ xs 8, ์ข์ฐ ์ฌ๋ฐฑ md 16
|
|
99
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## ๊ผญ ์งํฌ ๊ฒ
|
|
103
|
+
|
|
104
|
+
- `label`์ ๋ชจ์์ด ์๋๋ผ ํ๋์ ๋งํ๋ค("๋๋ณด๊ธฐ"๊ฐ ์๋๋ผ "๊ฒ์"). Native๋ ๋น `label`ยท๋น children์ `TypeError`๋ก ๊ฑฐ๋ถํ๋ค.
|
|
105
|
+
- ์์ Icon์ ์ฅ์์ผ๋ก ๋๋ค. ์ด๋ฆ์ ๋ฒํผ์ด ์ด๋ฏธ ์ฝ๋๋ค.
|
|
106
|
+
- ๋ฐฐ์น๋ `layoutStyle`๋ก๋ง ํ๋ค. ์ง๋ฆยทradiusยท๋ฐฐ๊ฒฝ์ `style`/`className`์ผ๋ก ๋ฎ์ง ์๋๋ค.
|
|
107
|
+
- NotificationBell์ `label`์ ๋ฏธํ์ธ ์๋ฅผ ํฌํจํ ํ์งํ ๋ฌธ๊ตฌ๋ฅผ ๋ฃ๋๋ค. ๋ฐฐ์ง์ ๊ทธ๋ฆผ์ ์ ๊ทผ์ฑ์์ ์จ๊ฒจ์ง๋ค.
|
|
108
|
+
๋๋ฌ๋ ์ฝ์ ์ฒ๋ฆฌ๋ ํ์ง ์๋๋ค. ์ฝ์ ์ฒ๋ฆฌ๋ ์ ํ์ด ํ๋ค.
|
|
109
|
+
- Native์์ ํ๋ฉด์ด ๊ฐ๋ ค์ง ์ฑ mounted๋ฉด `active={false}`๋ฅผ ๋๊ฒจ ํ๋ค๋ฆผ์ ๋ฉ์ถ๋ค.
|
|
110
|
+
|
|
111
|
+
## ํ๋ซํผ ์ฐจ์ด
|
|
112
|
+
|
|
113
|
+
| ํญ๋ชฉ | Web | Native |
|
|
114
|
+
| --- | --- | --- |
|
|
115
|
+
| ์ด๋ฒคํธ | `onClick` | `onPress`, `onLongPress` |
|
|
116
|
+
| ๊ธฐ๋ณธ `type` | `"button"` | ํด๋น ์์ |
|
|
117
|
+
| ์คํผ๋ ๊ต์ฒด | ์์ | `renderLoadingIndicator` |
|
|
118
|
+
| ๋นํ์ฑ์ด์ง๋ง ํฌ์ปค์ค ์ ์ง | `aria-disabled` | `loading` ๊ธฐ๋ณธ ๋์ |
|
|
119
|
+
| NotificationBell ํ๋ค๋ฆผ ์ ์ง | ๋ฌธ์ ์จ๊นยทreduced motion | AppState ๋นํ์ฑยทreduced motion |
|
|
120
|
+
| ๋ฐฐ์น | `layoutStyle`(IconButtonยทNotificationBell, NotificationBell์ `width` ๋ฌด์) | IconButton `layoutStyle`. `style`์ deprecated(๊ฐ๋ฐ ๋ชจ๋ 1ํ ๊ฒฝ๊ณ , ๋ค์ major ์ ๊ฑฐ) โ `layoutStyle` ๋๋ `tone`ยท`size`ยท`shape`. NotificationBell์ ๋ฐฐ์น prop์ด ์์ด ๊ฐ์ธ๋ View๋ก ๋ฐฐ์นํ๋ค |
|
|
121
|
+
|
|
122
|
+
## ํจ์
|
|
123
|
+
|
|
124
|
+
- children์ ์์ ๋ฒํผ์ด ์น ํ์ง ์๋๋ค. HJM Icon์ ์๊ธฐ `tone` ์์ ์ฐ๋ฏ๋ก, `primary`ยท`danger` ๋ฒํผ ์์์๋
|
|
125
|
+
`tone="inverse"`๋ฅผ ์ค๋ค. ๋ค๋ฅธ ์์ด์ฝ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ปดํฌ๋ํธ๋ฅผ ์ง์ ๋ฃ์ผ๋ฉด(Native) ์์ ์ ํ์ด ๋๊ฒจ์ผ ํ๋ค.
|
|
126
|
+
- Native `IconButton` `style`์ deprecated์ง๋ง ๋ค์ major ์ ๊น์ง recipe ๋ค์ ํฉ์ณ์ ธ ์ง๋ฆยท๋ฐฐ๊ฒฝ๊น์ง ๋ฐ๋๋ค. ์ ์ฝ๋์์ ์ฐ์ง ์๋๋ค.
|