@hjmds/design-contracts 1.29.0 โ 1.31.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/behaviors.d.ts +1 -0
- package/dist/behaviors.d.ts.map +1 -1
- package/dist/bottom-navigation.d.ts +24 -0
- package/dist/bottom-navigation.d.ts.map +1 -1
- package/dist/bottom-navigation.js +34 -0
- package/dist/bottom-navigation.js.map +1 -1
- package/dist/carousel.d.ts +30 -0
- package/dist/carousel.d.ts.map +1 -1
- package/dist/carousel.js +32 -0
- package/dist/carousel.js.map +1 -1
- package/dist/catalog.d.ts +382 -64
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +147 -131
- package/dist/catalog.js.map +1 -1
- package/dist/component-definitions.d.ts +21 -131
- package/dist/component-definitions.d.ts.map +1 -1
- package/dist/component-definitions.js +9 -132
- package/dist/component-definitions.js.map +1 -1
- package/dist/component-recipes.d.ts +87 -0
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +44 -1
- package/dist/component-recipes.js.map +1 -1
- package/dist/icon.d.ts +58 -1
- package/dist/icon.d.ts.map +1 -1
- package/dist/icon.js +69 -0
- package/dist/icon.js.map +1 -1
- package/dist/image-choice.d.ts +7 -0
- package/dist/image-choice.d.ts.map +1 -1
- package/dist/image-choice.js +7 -0
- package/dist/image-choice.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/recipes.d.ts +1 -1
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js.map +1 -1
- package/dist/screen-patterns.d.ts +100 -0
- package/dist/screen-patterns.d.ts.map +1 -1
- package/dist/screen-patterns.js +98 -3
- package/dist/screen-patterns.js.map +1 -1
- package/dist/showcase.d.ts.map +1 -1
- package/dist/showcase.js +10 -0
- package/dist/showcase.js.map +1 -1
- package/dist/steps.d.ts +16 -7
- package/dist/steps.d.ts.map +1 -1
- package/dist/steps.js +6 -1
- package/dist/steps.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/architecture.md +18 -0
- package/docs/bottom-navigation.md +10 -0
- package/docs/carousel.md +8 -0
- package/docs/dialog.md +23 -0
- package/docs/generated/component-maturity.md +32 -29
- package/docs/generated/renderer-evidence.json +1551 -1118
- package/docs/generated/renderer-evidence.md +243 -237
- package/docs/generated/showcase-manifest.json +688 -69
- package/docs/icon.md +9 -0
- package/docs/screen-patterns.md +18 -0
- package/docs/stable-core.md +22 -0
- package/docs/steps.md +4 -3
- package/docs/usage/README.md +28 -25
- package/docs/usage/SELECTION.md +1 -1
- package/docs/usage/components/auth-screen-layout.md +15 -5
- package/docs/usage/components/bottom-navigation.md +65 -2
- package/docs/usage/components/card.md +36 -5
- package/docs/usage/components/carousel.md +1 -0
- package/docs/usage/components/chat-date-separator.md +86 -0
- package/docs/usage/components/chat-message.md +10 -1
- package/docs/usage/components/chat-screen.md +3 -2
- package/docs/usage/components/comment-thread-row.md +11 -3
- package/docs/usage/components/comment-thread-screen.md +1 -1
- package/docs/usage/components/dialog.md +69 -0
- package/docs/usage/components/editor-screen.md +6 -2
- package/docs/usage/components/field.md +13 -0
- package/docs/usage/components/icon-button.md +1 -0
- package/docs/usage/components/icon-tile.md +10 -1
- package/docs/usage/components/icon.md +45 -6
- package/docs/usage/components/image-choice-group.md +3 -1
- package/docs/usage/components/media-selection-screen.md +17 -0
- package/docs/usage/components/message-composer.md +4 -1
- package/docs/usage/components/moderation-screen.md +88 -2
- package/docs/usage/components/page-indicator.md +102 -0
- package/docs/usage/components/profile-screen.md +9 -3
- package/docs/usage/components/profile-summary.md +3 -3
- package/docs/usage/components/screen-layout.md +9 -0
- package/docs/usage/components/steps.md +14 -7
- package/docs/usage/components/tabs.md +21 -1
- package/docs/usage/components/text.md +30 -1
- package/docs/usage/components/typing-indicator.md +89 -0
- package/docs/usage/screens/common-login.md +3 -1
- package/docs/usage/screens/common-profile.md +3 -3
- package/docs/usage/screens/flow-editor.md +13 -2
- package/docs/usage/screens/flow-moderation.md +31 -3
- package/package.json +2 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# ChatDateSeparator
|
|
2
|
+
|
|
3
|
+
- ๋จ๊ณ: ์ปดํฌ๋ํธ
|
|
4
|
+
- ์ํ: ์คํ
|
|
5
|
+
- ์ง์: Web ยท Native
|
|
6
|
+
- ์ ์ฉ: ๋ฏธ๊ฒ์(1.30.0 ์ดํ)
|
|
7
|
+
- ๊ฒํ ์ผ: 2026-10-18
|
|
8
|
+
- ๊ทผ๊ฑฐ: [๋ฐ๋ณต ํ๋ฉด ์กฐํฉ](../../screen-patterns.md), `src/screen-patterns.ts`์ `chatDateSeparatorRecipe`; WebยทNative `src/screens.tsx`
|
|
9
|
+
- ์คํ ๋ฆฌ๋ถ: `์คํ/์ปดํฌ๋ํธ/๋ฐ์ดํฐ ํ์/๋ํ ๋ ์ง ๊ตฌ๋ถ`
|
|
10
|
+
|
|
11
|
+
## ์ธ์ ์ฐ๋
|
|
12
|
+
|
|
13
|
+
๋ํ ํ์๋ผ์ธ์์ ๋ ์ง(๋๋ ์ค๋ ์ฐ ๋ค์ ์๊ฐ)๊ฐ ๋ฐ๋๋ ์๋ฆฌ์ ๊ฐ์ด๋ฐ ์ ๋ ฌ ๊ตฌ๋ถ ์ค์ ๋ ๋ ์ด๋ค. ๋ฌธ๊ตฌ ํฌ๋งท("์ค๋", "10์ 18์ผ ํ ์์ผ", "์คํ 3:04")๊ณผ
|
|
14
|
+
๊ตฌ๋ถ์ ๋ฃ์ ์์น(๋ ์ง ๋ณ๊ฒฝยท๊ฐ๊ฒฉ ๊ธฐ์ค)๋ ์ ํ์ด ์ ํ๊ณ , HJM์ ์ค์ ๋ฐฐ์นยท๊ธ์ยท๊ฐ๊ฒฉ์ ๋งก๋๋ค.
|
|
15
|
+
|
|
16
|
+
## ์ฐ์ง ์์ ๋
|
|
17
|
+
|
|
18
|
+
| ์ํฉ | ๋์ ์ธ ๊ฒ |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| ์ผ๋ฐ ๋ชฉ๋ก์ ๊ตฌํ ์ ๋ชฉ | [Section](section.md), [List](list.md) ๋ฌถ์ ์ ๋ชฉ |
|
|
21
|
+
| ์ ๋ง ํ์ํ ๊ตฌ๋ถ | [Divider](divider.md) |
|
|
22
|
+
| ๋ฉ์์ง๋ง๋ค ๋ถ๋ ์๊ฐ | [ChatMessage](chat-message.md) `timestamp` |
|
|
23
|
+
|
|
24
|
+
## ๊ณต๊ฐ ์ด๋ฆ๊ณผ import
|
|
25
|
+
|
|
26
|
+
| ์ด๋ฆ | ์ญํ | Web | Native |
|
|
27
|
+
| --- | --- | --- | --- |
|
|
28
|
+
| `ChatDateSeparator` | ๊ธฐ๋ณธ | `@hjmds/react/screens` | `@hjmds/react-native/screens` |
|
|
29
|
+
|
|
30
|
+
๋ฃจํธ barrel์๋ ์๋ค.
|
|
31
|
+
|
|
32
|
+
## ์ต์ ์ฌ์ฉ ์
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
// Web
|
|
36
|
+
import { ChatDateSeparator, ChatMessage } from "@hjmds/react/screens";
|
|
37
|
+
|
|
38
|
+
{rows.map(row => <Fragment key={row.message.id}>
|
|
39
|
+
{row.dayChanged ? <ChatDateSeparator label={formatDay(row.message.sentAt)} dateTime={row.message.sentAt.slice(0, 10)} /> : null}
|
|
40
|
+
<ChatMessage {...toMessageProps(row.message)}>{row.message.text}</ChatMessage>
|
|
41
|
+
</Fragment>)}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
// Native
|
|
46
|
+
import { ChatDateSeparator, ChatMessage } from "@hjmds/react-native/screens";
|
|
47
|
+
|
|
48
|
+
const renderItem = ({ item }: { item: Row }) => <>
|
|
49
|
+
{item.dayChanged ? <ChatDateSeparator label={formatDay(item.message.sentAt)} /> : null}
|
|
50
|
+
<ChatMessage {...toMessageProps(item.message)}><Text>{item.message.text}</Text></ChatMessage>
|
|
51
|
+
</>;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## ์ถ๊ณผ ๊ธฐ๋ณธ๊ฐ
|
|
55
|
+
|
|
56
|
+
| prop | ๊ฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช
|
|
|
57
|
+
| --- | --- | --- | --- |
|
|
58
|
+
| `label` | `string` | ํ์ | ์ ํ์ด ํฌ๋งทํ ๋ ์งยท์๊ฐ. ๋น ๋ฌธ์์ด์ `TypeError` |
|
|
59
|
+
| `dateTime` | `string` | ์์ | Web `<time dateTime>` ๊ฐ(์: `2026-10-18`). Native๋ ์ฐ์ง ์๋๋ค |
|
|
60
|
+
| `layoutStyle` | `HjmCompositionStyleProp` | ์์ | ํ ๋ฐฐ์น ์ ์ฉ. ์ยท์ฌ๋ฐฑ์ ๋ฎ์ง ์๋๋ค |
|
|
61
|
+
|
|
62
|
+
## ๋ฐฐ์น
|
|
63
|
+
|
|
64
|
+
| ํญ๋ชฉ | ๊ฐ | ๊ทผ๊ฑฐ |
|
|
65
|
+
| --- | --- | --- |
|
|
66
|
+
| ํฌ๊ธฐ | ํ์๋ผ์ธ ํญ ์ ์ฒด ํ, ๊ธ์ `textVariant` caption ยท `tone` muted | `chatDateSeparatorRecipe` |
|
|
67
|
+
| ๊ฐ๊ฒฉ | ์ `paddingTop`(`spacing.sm`) 12, ์๋ `paddingBottom`(`spacing.xxs`) 4. ๋ค์ ๋ฉ์์ง๋ ์๊ธฐ `groupPosition` ์ ์ฌ๋ฐฑ(์ ๋ฌถ์ 12)์ ๊ทธ๋๋ก ๊ฐ์ง๋ค | `chatDateSeparatorRecipe`, `resolveChatMessageGroupDisplay` |
|
|
68
|
+
| ์์ยท์ ๋ ฌ | ๊ตฌ๋ถํ ๋ฉ์์ง ๋ฐ๋ก ์, ๊ฐ๋ก ๊ฐ์ด๋ฐ | WebยทNative `ChatDateSeparator` |
|
|
69
|
+
| ๊ณ ์ ยท์คํฌ๋กค | ์์ฒด ๊ณ ์ ์์. ํ์๋ผ์ธ๊ณผ ํจ๊ป ์คํฌ๋กคํ๋ค | โ |
|
|
70
|
+
| ์ข์ ํญยทํฐ ๊ธ์ | ๊ธ์๋ ์ค๋ฐ๊ฟ๋๊ณ ์๋ฅด์ง ์๋๋ค | WebยทNative `ChatDateSeparator` |
|
|
71
|
+
|
|
72
|
+
## ๊ผญ ์งํฌ ๊ฒ
|
|
73
|
+
|
|
74
|
+
- ๊ตฌ๋ถ ์ค ๋ค์ ๋ฉ์์ง๋ ์ ๋ฌถ์์ผ๋ก ์์ํ๋ค(`resolveChatMessageGroups`๊ฐ ๊ฐ์ ์๊ฐ ๋ฌธ์์ด์ด๋ฉด ๋ฌถ์ผ๋ฏ๋ก, ๋ ์ง๊ฐ ๋ฐ๋๋ฉด ์ ํ์ด ๋ฌถ์์ ๋๋๋ค).
|
|
75
|
+
- ๋ฌธ๊ตฌ๋ ์ ํ์ด ์ง์ญํยทํฌ๋งทํ๋ค. HJM์ ๋ ์ง๋ฅผ ํด์ํ์ง ์๋๋ค.
|
|
76
|
+
- ์ฝ๊ธฐ ์์์ ๋ฉ์์ง ์ฌ์ด ์ผ๋ฐ ๊ธ์๋ก ์ฝํ๋ค. ๋ฐ๋ก live region์ด๋ heading ์ญํ ์ ์ฃผ์ง ์๋๋ค.
|
|
77
|
+
|
|
78
|
+
## ํ๋ซํผ ์ฐจ์ด
|
|
79
|
+
|
|
80
|
+
| ํญ๋ชฉ | Web | Native |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| ์๊ฐ ์๋ฏธ | `dateTime`์ด ์์ผ๋ฉด `<time>` | ๊ธ์๋ง |
|
|
83
|
+
|
|
84
|
+
## ๊ทผ๊ฑฐ
|
|
85
|
+
|
|
86
|
+
- ๊ณตํต ๋ ์ง ๊ตฌ๋ถ: 2026-10-18 ์ ํธ๋ฒ์ค ์ฑํ
. ChatScreen์ ์๊ฐ ๊ตฌ๋ถ ์ค์ด ์์ด ๊ฐ์ด๋ฐ ํ๋ฆฐ caption์ ์ ํ์ด ์ง์ ๊ทธ๋ ธ๋ค(์ 12ยท์๋ 8 ์ฌ๋ฐฑ). ์๋ ์ฌ๋ฐฑ์ ๋ค์ ๋ฉ์์ง์ ๋ฌถ์ ์ฌ๋ฐฑ 12์ ๊ฒน์น์ง ์๋๋ก 4๋ก ์ ํ๋ค.
|
|
@@ -85,8 +85,11 @@ import { Text } from "@hjmds/react-native/primitives";
|
|
|
85
85
|
| `replyQuote` | `{ title, excerpt }` | ์์ | ๋ฏธ๊ฒ์. ๋งํ์ ๋งจ ์์ HJM์ด ์ธ์ฉ์ ๊ทธ๋ฆฐ๋ค: ์ ๋ชฉ(์: "์์ฐ์๊ฒ ๋ต์ฅ", ๋ณธ๋ฌธ ๊ธ์) โ ์๋ฌธ ํ ์ค(ํ๋ฆฐ ๊ธ์, ๋์น๋ฉด ๋ง์ค์) โ ๊ตฌ๋ถ์ 1 โ ๋ต์ฅ ๋ณธ๋ฌธ. ์์ ๋
ธ๋ `reply`๋ณด๋ค ์ฐ์ ์ฌ์ฉํ๋ค |
|
|
86
86
|
| `replyLink` | `{ label, onPress() }` | ์์ | ์ธ์ฉ๊ณผ ๊ฐ์ด ์ฃผ๋ฉด ๋งํ์ ์ ์ธ์ฉ์ ๋๋ฌ ์๋ฌธ์ผ๋ก ์ด๋ํ๋ค(Native๋ ๋งํ์ ์ "์๋ฌธ์ผ๋ก ์ด๋" ์ ๊ทผ์ฑ ๋์๋ ์ ๊ณต). ์ด์ ์๋ ๋ฒ๋ธ ๋ฐ ghost ๋ฒํผ์ด์๋ค |
|
|
87
87
|
| `media` | `{ id, label, preview, aspectRatio?, onPress? }[]` | ์์ | ๋ฏธ๊ฒ์. ๋ณด๋ธ ์ฌ์ง. ํญ `chatMessageRecipe.mediaWidth` 210, ํ ์ฅ์ `aspectRatio`(๊ธฐ๋ณธ 1), ์ฌ๋ฌ ์ฅ์ ์ ์ฌ๊ฐ ์นธ(`resolveChatMediaColumns`: 2ยท4์ฅ 2์ด, ๊ทธ ์ธ 3์ด, ๊ฐ๊ฒฉ 2), ๋ฐ๊นฅ radius 14. ๊ธ ์์ด ์ฌ์ง๋ง ๋ณด๋ด๋ฉด ๋งํ์ ํ๋ฉดยท๊ผฌ๋ฆฌ๋ฅผ ๊ทธ๋ฆฌ์ง ์๋๋ค. `onPress`๊ฐ ์์ผ๋ฉด ์ฌ์ง๋ง๋ค ๋ฒํผ(ํฌ๊ฒ ๋ณด๊ธฐ๋ ์ ํ) |
|
|
88
|
+
| `mediaAlbum` | `{ count, renderPage(index), pageLabel(index, count), previousLabel, nextLabel, aspectRatio?, onPress?(index), index?ยทonIndexChange?ยทdefaultIndex? }` | ์์ | ๋ฏธ๊ฒ์. ํ ์ฅ์ฉ ๋๊ธฐ๋ ์ฌ์ง ๋ฌถ์. `renderPage`๋ ํ๋ฉด์ ๋ณด์ด๋ ์ฅ์๋ง ํธ์ถ๋์ด ์ ํ์ด ๊ทธ ์ฌ์ง๋ง ์กฐํยท๋์ฝ๋ฉํ๋ค(์ด๋ฏธ์ง ์๋ณธยท๊ถํยท์บ์๋ ์ ํ). ํญ `mediaWidth` 210, ์ฌ์ง ์นธ `aspectRatio`(๊ธฐ๋ณธ 1)ยทradius 14, ๊ทธ ์๋ ์ด์ ์์ด์ฝ ยท ์์น(์: 2/5) ยท ๋ค์ ์์ด์ฝ ์ค(์์ ghost IconButton, `mediaAlbumRecipe`). ๊ธ ์์ด ์จ๋ฒ๋ง ๋ณด๋ด๋ฉด `media`์ฒ๋ผ ๋งํ์ ํ๋ฉดยท๊ผฌ๋ฆฌ๋ฅผ ๊ทธ๋ฆฌ์ง ์๋๋ค. ๋๊น ๋ฒํผ์ด ์์ด ๋งํ์ ์ `interactiveContent`๋ก ๋ค๋ค์ง๋ค. ๋๊ธด ์ฅ์ ๊ธธ๊ฒ ๋๋ฅด๊ธฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์์๋ ๊ฐ๋ค. `media`์ ํจ๊ป ์ฃผ๋ฉด `TypeError` |
|
|
88
89
|
| `replyAction` | `{ label, onPress(), disabled?, icon? }` | ์์ | `replySwipeDistance` 60px ์ด์์ด๊ณ ์ธ๋ก ์ด๋์ 2๋ฐฐ๋ฅผ ๋๋ ๊ฐ๋ก ์ค์์ดํ๋ก ์คํ(`isReplySwipe`). `reactions`๊ฐ ์์ผ๋ฉด ๊ธธ๊ฒ ๋๋ฅธ ๋ฉ๋ด์ ์ฒซ ํญ๋ชฉ์ผ๋ก๋ ๋ณด์ธ๋ค(๋ฏธ๊ฒ์) |
|
|
89
90
|
| `reactions` | `ReactionPickerProps & { closeLabel, menuActions?, reportAction?, menuAction? }` | ์์ | `reactionHoldMs` 450ms ๊ธธ๊ฒ ๋๋ฅด๋ฉด ๋ฉ๋ด๊ฐ ์ด๋ฆฐ๋ค: ๋ฐ์ ์ค(์ด๋ชจ์ง `typography.title` 18/26, `more`๋ฉด +) โ ๋ฉ์์ง ๋ฏธ๋ฆฌ๋ณด๊ธฐ โ ์นด๋(๋งจ ์ `timestamp` ์ ํํ ์๊ฐ โ `replyAction` โ `menuActions` โ `menuAction` โ `reportAction` ๋นจ๊ฐ ๊ธ์). ํญ๋ชฉ์ ๋์ด 44, ์์ด์ฝ 24 ์นธ. ๋ฐฐ์น๋ `holdMenuRecipe`(์ธ์คํ๊ทธ๋จ DM ์ค์ธก): ํ๋ฉด ์ข์ฐ 10 ์์ชฝ ์ ์ฒด ํญ ๋ฐ์ ์ค(๋์ด 60, 48 ์ํ ์นธ, ์ด๋ชจ์ง 28, ๋์น ๋๋ง ๊ฐ๋ก ์คํฌ๋กค) โ 12 โ ๋๋ฅธ ๋งํ์ ๊ทธ๋๋ก ๋ฏธ๋ฆฌ๋ณด๊ธฐ(์ ์ชฝ ์ ๋ ฌ) โ 12 โ ํญ 200ยทradius 18 ๋์ ์นด๋(ํ 56, ์์ด์ฝ 24, `submenu` ํ์ โบ)๋ฅผ ๋งํ์ ์ชฝ์ ๋ถ์ธ๋ค. ๋ณด์ด๋ ร๋ ์๊ณ ๋ฐฐ๊ฒฝ ํญยทEscapeยทVoiceOver ๋ซ๊ธฐ๋ก ๋ซ๋๋ค(๋ณด์กฐ๊ธฐ์ ์ฒซ ์ด์ ์ฉ ๋ซ๊ธฐ ๋์๋ง ๋๋ค). ๊ฐ์ ๋ฐ์ ์ฌ์ ํ์ `null`. `label`์ Web์์ ๋งํ์ ์ด๋ฆยทPopover ์ ๋ชฉ, Native์์ ์ ๊ทผ์ฑ ํํธยท๋์ ์ด๋ฆ. ์๊ฐยท๋์ ์นด๋์ `reportAction`ยท`menuActions`๋ ๋ฏธ๊ฒ์(2026-10-16 ์ธ์คํ๊ทธ๋จ DM ๊ธธ๊ฒ ๋๋ฅด๊ธฐ ์ค์ธก ๋ฐ์) |
|
|
91
|
+
| `reactions.menuActions[].tone` | `"neutral"` ยท `"danger"` | `"neutral"` | ๋ฏธ๊ฒ์(1.30.0 ์ดํ). ๊ธธ๊ฒ ๋๋ฅธ ๋ฉ๋ด ํ์ ์. ์ญ์ ์ฒ๋ผ ๋๋๋ฆด ์ ์๋ ํ์ `danger`(๋นจ๊ฐ ๊ธ์). [Menu](menu.md) ํญ๋ชฉ `tone`๊ณผ ๊ฐ์ ์ดํ(`MenuItemTone`). `reportAction`์ ํญ์ `danger`์ด๊ณ ๋งจ ๋์ด๋ค. ๋๊ธ(CommentThreadRow) ๋ฉ๋ด๋ ๊ฐ๋ค |
|
|
92
|
+
| `reactions.summary` | `{ label, items: { id, emoji, count }[] }` | ์์ | ๋ฏธ๊ฒ์. ํ์ฌ ๋ฐ์ ์ง๊ณ๋ฅผ ์๊ฐ ์ ์์ฝ ๋ฒํผ ํ๋๋ก ๊ทธ๋ฆฌ๊ณ , ๋๋ฅด๋ฉด ๊ธธ๊ฒ ๋๋ฅธ ๊ฒ๊ณผ ๊ฐ์ ๋ฉ๋ด๋ฅผ ์ฐ๋ค. `label`์ ๋ฒํผ์ ์ง์ญํ ์ ๊ทผ์ฑ ์ด๋ฆ(์: "๋ฐ์ โค๏ธ 2๊ฐ, ๐ 1๊ฐ. ๋ฐ์ ๋ฉ๋ด ์ด๊ธฐ"), `items`๋ ํ์ ์์. `count`๊ฐ `chatMessageRecipe.reactionSummary.minCount` 2 ๋ฏธ๋ง์ด๋ฉด ์ด๋ชจ์ง๋ง ๋ณด์ธ๋ค. ๋ด ๋ฐ์(`value`)์ด ๋ค์ด ์์ผ๋ฉด brand ํ
๋๋ฆฌ. ๋์ด 24(Native๋ hitSlop์ผ๋ก 44 ํฐ์น), ์ข์ฐ 8, ํญ๋ชฉ ์ฌ์ด 4. ๋น `label`ยท์ค๋ณต idยท๋น ์ด๋ชจ์ง๋ `TypeError`, ์์ ์ ์๊ฐ ์๋ count๋ `RangeError`(`resolveChatReactionSummary`). `reactions.disabled`๋ฉด ๋ฒํผ๋ ๋นํ์ฑ. ๋ฉ๋ด๊ฐ ๋ซํ๋ฉด Web ์ด์ ์ ๋งํ์ ์ผ๋ก ๋์๊ฐ๋ค |
|
|
90
93
|
| `interactiveContent` | `boolean` | `false` | ์ฌ์ง ์จ๋ฒยท๋งํฌ์ฒ๋ผ ์์์ด ๋ฒํผ์ ๊ฐ์ง๋ฉด `true`. ์์ชฝ ๋ฒํผยท๋งํฌ์ ๋๋ฆ๊ณผ Web Enter/Space๋ ๊ทธ ์ปจํธ๋กค์ด ๋ฐ๋๋ค |
|
|
91
94
|
| `actions` | `ReactNode` | ์์ | ์๊ฐ ์ ์ฌ๋กฏ. ๋ฐ์ ์ง๊ณ ๋ฐฐ์งยท์ฌ์๋๋ ์ ํ์ด ๋ฃ๋๋ค |
|
|
92
95
|
| `children` | `ReactNode` | ํ์ | ๋ฉ์์ง ๋ณธ๋ฌธ. Native๋ ๋งํ์ ์ด ์ด ํ
์คํธ๋ก ์ฝํ๋ค |
|
|
@@ -111,7 +114,8 @@ import { Text } from "@hjmds/react-native/primitives";
|
|
|
111
114
|
๋ต์ฅยท๋ฐ์์ ์ ๊ทผ์ฑ ๋์(`reply`ยท`activate`)์ผ๋ก ๋จ๋๋ค. 2026-10-06 ๋ฆฌ๋ทฐ์์ ํ ์ด๋ฆ(`author`)๊ณผ ๋ฐ์ ์ด๋ฆ(`picker.label`)์ด
|
|
112
115
|
๋ณธ๋ฌธ์ ๊ฐ๋ ค ํ๋ฉด ๋ญ๋
๊ธฐ๊ฐ ๋ฉ์์ง๋ฅผ ์ฝ์ง ๋ชปํ๋ค.
|
|
113
116
|
- ํ์๋ผ์ธ ์ ์ฒด๋ฅผ live region์ผ๋ก ๊ฐ์ธ์ง ์๋๋ค(๊ณผ๊ฑฐ ๋ฉ์์ง ๋ก๋ฉ ๋ ์ฝ๊ธฐ ์์๋ฅผ ๊ฐ๋ก์ฑ๋ค).
|
|
114
|
-
- ๋ฐ์ **์ ํ ๋ฉ๋ด**์ ํ์ฌ ๋ฐ์ **์ง๊ณ ๋ฒํผ**์ ๋ค๋ฅธ ํ๋์ด๋ค. ์ง๊ณ๋ฅผ ๋๋ฌ
|
|
117
|
+
- ๋ฐ์ **์ ํ ๋ฉ๋ด**์ ํ์ฌ ๋ฐ์ **์ง๊ณ ๋ฒํผ**์ ๋ค๋ฅธ ํ๋์ด๋ค. ์ง๊ณ๋ฅผ ๋๋ฌ ๊ฐ์ ๋ฉ๋ด๋ฅผ ์ด ๋๋
|
|
118
|
+
`reactions.summary`๋ฅผ ์ด๋ค(๋ฏธ๊ฒ์). ์ง๊ณ๋ฅผ ์ํ ํ์์ฉ Tag๋ก `actions`์ ์ง์ ๊ทธ๋ฆฌ์ง ์๋๋ค. ๋ค๋ฅธ ํ๋(์: ๋ฐ์ํ ์ฌ๋ ๋ชฉ๋ก)์ ์ฌ๋ ๋ฒํผ์
|
|
115
119
|
`actions` ์ฌ๋กฏ์ Button์ ๊ณต๊ธํ ์ ์๋ค. ๋ณธ๋ฌธ ์์ ์ง๊ณ ๋ฒํผ์ ๋ฃ๋๋ค๋ฉด ์ฌ์งยท๋งํฌ๊ฐ ์์ด๋
|
|
116
120
|
`interactiveContent=true`๊ฐ ํ์ํ๋ค. 2026-10-07 Utilverse ์์ค ๋์กฐ์์ ์ฌ์ง/๋๊ตฌ๋ง ๊ฒ์ฌํด
|
|
117
121
|
์ง๊ณ ๋ฒํผ์ด ๋ถ๋ชจ ์ ๊ทผ์ฑ ๊ทธ๋ฃน์ ๋ค์ด๊ฐ ์ ์๋ ๊ฒฝ๋ก๋ฅผ ๋ฐ๊ฒฌํ๋ค. `actions`์ ๋ ๋๋ ๊ธด ์ง๊ณ์
|
|
@@ -129,6 +133,8 @@ import { Text } from "@hjmds/react-native/primitives";
|
|
|
129
133
|
|
|
130
134
|
## ํจ์
|
|
131
135
|
|
|
136
|
+
- ์ฌ์ง์ด ์ฌ๋ฌ ์ฅ์ด๊ณ ์ฅ๋ง๋ค ๊ถํ ํ์ธยท์กฐํ๊ฐ ํ์ํ๋ฉด `media`์ ๋ชจ๋ `preview`๋ฅผ ๋๊ธฐ์ง ๋ง๊ณ `mediaAlbum`์ ์ด๋ค. `media`์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ ํ ๋ฒ์ ๋ชจ๋ ๋ง์ดํธ๋๋ค(2026-10-18 Utilverse ์์
์ฌ์ง: ๋ํ ํ๋ง๋ค ์๋ณธ 10์ฅ์ ๋ฐ์ง ์์ผ๋ ค๊ณ ์ ํ ์จ๋ฒ์ ๋ฐ๋ก ๋ง๋ค์๋ค).
|
|
137
|
+
|
|
132
138
|
- Native๋ ๋ฐ์ ๋ชจ๋ฌ ์์์ `children`์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ก ํ ๋ฒ ๋ ๋ ๋ํ๋ค. ์์์ ํ์์ฉ ์ฝํ
์ธ ๋ก ์ค๋ค
|
|
133
139
|
(๋ถ์์ฉยท๊ณ ์ ref๋ฅผ ๋์ง ์๋๋ค).
|
|
134
140
|
- `reactions`์ `options`์ `more.options`๋ id๊ฐ ํฉ์ณ์ ์ ์ผํ๊ณ emojiยทlabel์ด ๋น์ด ์์ง ์์์ผ ํ๋ค. `value`๊ฐ
|
|
@@ -147,4 +153,7 @@ import { Text } from "@hjmds/react-native/primitives";
|
|
|
147
153
|
|
|
148
154
|
## ๊ทผ๊ฑฐ
|
|
149
155
|
|
|
156
|
+
- `reactions.summary`: 2026-10-18 ๋ฒ๋ DM. ๋ฉ๋ด๋ฅผ ๋ฐ์์ ์ด ์ ์์ด ์ง๊ณ๋ฅผ ์ํ์ฉ Tag๋ก๋ง `actions`์ ๊ทธ๋ ธ๋ค(์ ํ docs/DESIGN.md์ ๊ฒฉ์ฐจ๋ก ๊ธฐ๋ก). ์ ์ดํ `open` ๋์ HJM์ด ์ง๊ณ ๋ฒํผ๊น์ง ๊ทธ๋ ค ์์นยท์ ๊ทผ์ฑ ์ด๋ฆยท๋ฉ๋ด ์ฐ๊ฒฐ์ ๊ณตํต์ผ๋ก ๋งก๋๋ค.
|
|
157
|
+
- `menuActions[].tone`: 2026-10-18 ์ ํธ๋ฒ์ค ์ฑํ
๊ธธ๊ฒ ๋๋ฅด๊ธฐ ๋ฉ๋ด์ ์ญ์ ํ. ๋นจ๊ฐ ๊ธ์๊ฐ `reportAction`์๋ง ์์ด ์ญ์ ๊ฐ ์ผ๋ฐ ํ์ผ๋ก ๋ณด์๋ค.
|
|
158
|
+
|
|
150
159
|
- ์๋ฐํ 32: 2026-10-17 ์ ๋ ฌ ์ค์ธก. recipeยท๋ฌธ์๋ 36์ด์์ง๋ง Web Avatar ๋จ๊ณ(32/40/48/64)์ 36์ด ์์ด Web์ 36 ์นธ์ 32 ์๋ฐํ๋ฅผ, Native๋ 36 ์๋ฐํ๋ฅผ ๊ทธ๋ ธ๋ค. ์นด์นด์คํก ์ฐธ๊ณ ๊ฐ ์ฝ 34์ ๊ฐ์ฅ ๊ฐ๊น์ด Avatar ๋จ๊ณ `small` 32๋ฅผ recipe๊ฐ ์ง์ ์ฝ๊ฒ ํด ๋ ํ๋ซํผ์ด ๊ฐ์ ๊ฐ์ ์ด๋ค. ์์ฑ์ ์ฒซ ์ค๊ณผ์ ์์ชฝ ๋ง์ถค(์นด์นด์คํก ๋ฐฉ์)์ ์ ์งํ๋ค.
|
|
@@ -29,6 +29,7 @@ DMยท๋ํ๋ฐฉ์ฒ๋ผ ํค๋, ๋ฉ์์ง ํ์๋ผ์ธ, ํ๋จ ์์ฑ์ฐฝ์ผ๋ก ์ด
|
|
|
29
29
|
| --- | --- | --- | --- |
|
|
30
30
|
| `ChatScreen` | ํ๋ฉด ์กฐํฉ | `/screens` | `/screens` |
|
|
31
31
|
| `ChatMessage`, `MessageComposer`, `ScreenLayout` | ํจ๊ป ์ฐ๋ ์กฐ๊ฐ | `/screens` | `/screens` |
|
|
32
|
+
| [`TypingIndicator`](typing-indicator.md), [`ChatDateSeparator`](chat-date-separator.md) | ํ์๋ผ์ธ ์กฐ๊ฐ(์
๋ ฅ ์ค ํ์, ๋ ์ง ๊ตฌ๋ถ). ๋ฏธ๊ฒ์(1.30.0 ์ดํ) | `/screens` | `/screens` |
|
|
32
33
|
|
|
33
34
|
๋ฃจํธ barrel์๋ ์๋ค. `/screens`๋ optional native peer๋ฅผ ์๊ตฌํ์ง ์๋๋ค.
|
|
34
35
|
|
|
@@ -68,11 +69,11 @@ import { ChatScreen, MessageComposer } from "@hjmds/react-native/screens";
|
|
|
68
69
|
| --- | --- | --- |
|
|
69
70
|
| `title`(ํ์) | ์ง์ญํํ ๋ํ ์ ๋ชฉ | `header`๋ฅผ ์ฃผ๋ฉด Web์ `aria-label`๋ก๋ง ์ด๋ค |
|
|
70
71
|
| `header` / `exit` / `actions` / `description` | ๊ธฐ์กด navigation ํค๋, ๋๋ ํค๋ ์์ ์ชฝ ํ๋ฉด ๋๊ฐ๊ธฐ ์์ด์ฝ(`exit`: ๋ค๋กยท๋ซ๊ธฐ)ยท๋๊ตฌ ์ฌ๋กฏ | `header`๋ฅผ ์ฃผ๋ฉด ๊ธฐ๋ณธ ํค๋๋ฅผ ๊ทธ๋ฆฌ์ง ์๋๋ค |
|
|
71
|
-
| `children` | ํ์๋ผ์ธ(๊ฐ์ํ ๋ชฉ๋ก + `ChatMessage`) | ์๋ ์คํฌ๋กคยท์ ๋ฉ์์ง ๋ฐฐ์งยท์ด์ ๋ฉ์์ง ์์น๋ ์ ํ |
|
|
72
|
+
| `children` | ํ์๋ผ์ธ(๊ฐ์ํ ๋ชฉ๋ก + `ChatMessage`) | ์๋ ์คํฌ๋กคยท์ ๋ฉ์์ง ๋ฐฐ์งยท์ด์ ๋ฉ์์ง ์์น๋ ์ ํ. ๋ ์งยท์๊ฐ์ด ๋ฐ๋๋ ์๋ฆฌ์๋ `ChatDateSeparator`, ์๋๊ฐ ์
๋ ฅ ์ค์ด๋ฉด ๋ง์ง๋ง ๋ฉ์์ง ๋ค์ `TypingIndicator`๋ฅผ ๋๋ค. ๋์ ์ ํ captionยท์ ํ์๋ก ๋ง๋ค์ง ์๋๋ค |
|
|
72
73
|
| `composer`(ํ์) | `MessageComposer` ๋ฑ ์์ฑ์ฐฝ | `ready`์ `empty`์์ ๋ณด์ธ๋ค(๋น ๋ํ์ ์ฒซ ๋ฉ์์ง ์
๋ ฅ) |
|
|
73
74
|
| `state` / `stateAction` | `loading`ยท`empty`ยท`error`ยท`restricted` + ์ง์ญํ `title` | ready ์ธ์๋ ๋ณธ๋ฌธ์ ๊ต์ฒดํ๋ค |
|
|
74
75
|
| `notice` | ์๋ก๊ณ ์นจ ์คํจ ๊ฐ์ ๋น์ฐจ๋จ ์๋ด | ์ด์ ์ ์ง๊ฐ ํ์ํ๋ฉด `state` ๋์ ์ด๊ฒ |
|
|
75
|
-
| `avatar` | ์๋ยท๋ฐฉ ์ฌ์ง ๋
ธ๋ | ๋ฏธ๊ฒ์. ๊ฐ์ด๋ฐ ์ ๋ ฌ ์ ๋ชฉ ์์ ๋๋๋ค. ๋จํก๋ฐฉ์ ์ธ์ ์๋ฅผ `titleBadge`(ํ๋ฆฐ ๊ธ์)๋ก
|
|
76
|
+
| `avatar` | ์๋ยท๋ฐฉ ์ฌ์ง ๋
ธ๋ | ๋ฏธ๊ฒ์. ๊ฐ์ด๋ฐ ์ ๋ ฌ ์ ๋ชฉ ์์ ๋๋๋ค. ๋จํก๋ฐฉ์ ์ธ์ ์๋ฅผ `titleBadge`(ํ๋ฆฐ ๊ธ์)๋ก ๋๊ธด๋ค. ScreenLayout `titleLeading`๊ณผ ๊ฐ์ ์๋ฆฌ๋ผ ChatScreen์ `titleLeading`์ ๋ฐ๋ก ๋ฐ์ง ์๋๋ค. ์ฅ์์ด๋ผ ๋ณด์กฐ๊ธฐ์ ์์ ์จ๋๋ค(์ ๋ชฉ์ด ์ด๋ฆ) |
|
|
76
77
|
| `jumpToLatest` | `{ label, icon, onPress() }` | ๋ฏธ๊ฒ์. ๋
์๊ฐ ์ต์ ๋ฉ์์ง์์ ๋ฒ์ด๋ฌ์ ๋๋ง ๋๊ธด๋ค. ์์ฑ์ฐฝ ์ ๋ ์ชฝ์ ์ํ `IconButton size="small"`(secondary)๋ก ๋ ์๊ณ , ์คํฌ๋กค ์์น ํ๋จ์ ์ ํ์ด ํ๋ค |
|
|
77
78
|
|
|
78
79
|
## ์ถ๊ณผ ๊ธฐ๋ณธ๊ฐ
|
|
@@ -64,6 +64,9 @@ import { CommentThreadRow } from "@hjmds/react-native/screen-flows";
|
|
|
64
64
|
| --- | --- | --- |
|
|
65
65
|
| `id`ยท`parentId`ยท`author`ยท`body`ยท`timeLabel`ยท`likeCountLabel`ยท`likeIcon`ยท`likeLabel` | ํ์ | `parentId`๊ฐ `null`์ด๋ฉด ์ต์์ ๋๊ธ |
|
|
66
66
|
| `bodyText` | ์์ | ์ฃผ๋ฉด `author`์ ๋ณธ๋ฌธ์ ํ ๋ฌธ์ฅ์ผ๋ก, ์๊ฐ์ ์๋ ํ๋ ์ค๋ก ์ฎ๊ธด๋ค |
|
|
67
|
+
| `timePlacement` | `"auto"` | ๋ฏธ๊ฒ์. `"auto"`๋ `bodyText`๊ฐ ์์ผ๋ฉด ์๊ฐ์ ์์ฑ์ ์์ ๋๋ค. `"actions"`๋ ๊ธ์ด ์๋ ๋ณธ๋ฌธ(๊ฝ ๋์ฅยท์นด๋)์์๋ ์๊ฐ์ ๋ณธ๋ฌธ ์๋ ๋ฐ์ ์์ฝยท๋ต๊ธ๊ณผ ๊ฐ์ ์ค ๋งจ ์์ผ๋ก ์ฎ๊ธด๋ค |
|
|
68
|
+
| `panel` | ์์ | ๋ฏธ๊ฒ์. ๋๊ธ๋ณ ์ธ๋ผ์ธ ํจ๋(์์ ยท์ ๊ณ ยท์ฐจ๋จ ์์). ๋ต๊ธ ์ค ์๋, ๊ธธ๊ฒ ๋๋ฅด๊ธฐ ๋์ ๋ฐ์ `panelGap` 8 ๊ฐ๊ฒฉ์ผ๋ก ๋์ธ๋ค. ์ด๋ฆผ ์ํยท์ด์ ยท์ ์ถยท๋ซ๊ธฐ๋ ์ ํ์ด ์์ ํ๋ค |
|
|
69
|
+
| `mediaAlbum` | ์์ | ๋ฏธ๊ฒ์. ํ ์ฅ์ฉ ๋๊ธฐ๋ ์ฌ์ง ๋ฌถ์ `{ count, renderPage(index), pageLabel(index, count), previousLabel, nextLabel, aspectRatio?, onPress?(index), index?ยทonIndexChange?ยทdefaultIndex? }`. `renderPage`๋ ํ๋ฉด์ ๋ณด์ด๋ ์ฅ์๋ง ํธ์ถ๋์ด ์ ํ์ด ๊ทธ ์ฌ์ง๋ง ์กฐํยท๋์ฝ๋ฉํ๋ค. `media`์ ํจ๊ป ์ฃผ๋ฉด `TypeError`. ๋ณธ๋ฌธ ์๋, ๊ธธ๊ฒ ๋๋ฅด๊ธฐ ๋์ ๋ฐ์ ๋์ธ๋ค |
|
|
67
70
|
| `avatar` | ์์ | ์ ํ ์๋ฐํ. ์ต์์ 32ยท๋ต๊ธ 24 ํญ ์นธ์ ๋๋ค |
|
|
68
71
|
| `canReply` | `parentId === null` | ๋ต๊ธ ํ๋ ํ์ ์ฌ๋ถ |
|
|
69
72
|
| `replyDisabled` | false | ๋ต๊ธ ํ๋ ๋นํ์ฑ |
|
|
@@ -75,9 +78,9 @@ import { CommentThreadRow } from "@hjmds/react-native/screen-flows";
|
|
|
75
78
|
|
|
76
79
|
| ํญ๋ชฉ | ๊ฐ | ๊ทผ๊ฑฐ |
|
|
77
80
|
| --- | --- | --- |
|
|
78
|
-
| ํฌ๊ธฐ | ์๋ฐํ ์นธ ์ต์์ 32ยท๋ต๊ธ 24, ์ข์์ ์ ์นธ ํญ 44, `media` ์ต๋ ํญ 160ยทradius `radius.md` | `commentThreadRecipe` |
|
|
81
|
+
| ํฌ๊ธฐ | ์๋ฐํ ์นธ ์ต์์ 32ยท๋ต๊ธ 24, ์ข์์ ์ ์นธ ํญ 44, `media`ยท`mediaAlbum` ์ต๋ ํญ 160ยทradius `radius.md`(์จ๋ฒ์ ์ฌ์ง ์นธ์๋ง radius, ๊ทธ ์๋ ์ด์ ์์ด์ฝ ยท ์์น(์: 2/5) ยท ๋ค์ ์์ด์ฝ ์ค, ์์ ghost IconButton) | `commentThreadRecipe`, `mediaAlbumRecipe` |
|
|
79
82
|
| ๊ฐ๊ฒฉ | ์๋ฐํยท๋ณธ๋ฌธยท์ข์์ ์ด ์ฌ์ด `spacing.xs` 8, ๋ณธ๋ฌธ ์ค ์ฌ์ด 2, ํ๋ ์ค ํญ๋ชฉ ์ฌ์ด `spacing.sm` 12, ์์ฑ์โ์๊ฐ `spacing.xxs` 4, `media` ์ 4 | `commentThreadRecipe.lineGap`, `Stack gap` |
|
|
80
|
-
| ์์ยท์ ๋ ฌ | ์๋ฐํ โ (์์ฑ์ยท์๊ฐ โ ๋ณธ๋ฌธ โ media โ ์๊ฐ/๋ฐ์ ์์ฝ/๋ต๊ธ ํ๋) โ ์ข์์ ๋ฒํผยท`actions` ์๋ ์ข์์ ์. ์ ์ ๋ ฌ ๋จ, ์ค๋ฅธ์ชฝ ์ข์์ยท`actions`ยท๋๋ณด๊ธฐ ์ด์ 44 ํฐ์น ์์ญ์ ์ฒซ ์ค(์์ฑ์ caption, `bodyText`๋ฉด ๋ณธ๋ฌธ ์ค)์ ๊ฐ์ด๋ฐ์ ๋ง์ถ๋ค: ์ด์ (์ฒซ ์ค ๋์ด โ 44) / 2๋งํผ ์ฌ๋ฆฐ๋ค(`commentThreadRecipe.actionAnchor`). HJM์ ์ข์์ยท๋๋ณด๊ธฐ๊ฐ ๋ง์ง๋ง์ด๋ฉด ์ด ์ ์ฒด๊ฐ IconButton ๊ฐ์ฅ์๋ฆฌ ์ค๋ฒํ(10)๋งํผ ๋์ผ๋ก ๋๊ฐ ์์ด์ฝ ์นธ์ด ํ ๋ ์ฌ๋ฐฑ ์ (์๋ฐํ ์์๊ณผ ๊ฐ์ ์ )์ ์จ๋ค. ์ ํ `actions`๊ฐ ๋ง์ง๋ง์ด๋ฉด ๊ทธ ๋ฒํผ์ `edge="end"`๋ฅผ ์ค๋ค. ์ธก์ ์ ํ(Web 402): ์๋ฐํ 168 / ์์ฑ์ ์ค 162 / ์์ด์ฝ 174 โ ์์ด์ฝ 160 = ์์ฑ์ ์ค, ๋๋ณด๊ธฐ ์นธ ๋ 28 โ 16(2026-10-17 ์ค์ธก ๊ฐ์ฌ, [IconButton `edge`](icon-button.md#๋ฐฐ์น)) | ์ renderer |
|
|
83
|
+
| ์์ยท์ ๋ ฌ | ์๋ฐํ โ (์์ฑ์ยท์๊ฐ โ ๋ณธ๋ฌธ โ media ๋๋ mediaAlbum โ ์๊ฐ/๋ฐ์ ์์ฝ/๋ต๊ธ ํ๋ โ panel) โ ์ข์์ ๋ฒํผยท`actions` ์๋ ์ข์์ ์. ์ ์ ๋ ฌ ๋จ, ์ค๋ฅธ์ชฝ ์ข์์ยท`actions`ยท๋๋ณด๊ธฐ ์ด์ 44 ํฐ์น ์์ญ์ ์ฒซ ์ค(์์ฑ์ caption, `bodyText`๋ฉด ๋ณธ๋ฌธ ์ค)์ ๊ฐ์ด๋ฐ์ ๋ง์ถ๋ค: ์ด์ (์ฒซ ์ค ๋์ด โ 44) / 2๋งํผ ์ฌ๋ฆฐ๋ค(`commentThreadRecipe.actionAnchor`). HJM์ ์ข์์ยท๋๋ณด๊ธฐ๊ฐ ๋ง์ง๋ง์ด๋ฉด ์ด ์ ์ฒด๊ฐ IconButton ๊ฐ์ฅ์๋ฆฌ ์ค๋ฒํ(10)๋งํผ ๋์ผ๋ก ๋๊ฐ ์์ด์ฝ ์นธ์ด ํ ๋ ์ฌ๋ฐฑ ์ (์๋ฐํ ์์๊ณผ ๊ฐ์ ์ )์ ์จ๋ค. ์ ํ `actions`๊ฐ ๋ง์ง๋ง์ด๋ฉด ๊ทธ ๋ฒํผ์ `edge="end"`๋ฅผ ์ค๋ค. ์ธก์ ์ ํ(Web 402): ์๋ฐํ 168 / ์์ฑ์ ์ค 162 / ์์ด์ฝ 174 โ ์์ด์ฝ 160 = ์์ฑ์ ์ค, ๋๋ณด๊ธฐ ์นธ ๋ 28 โ 16(2026-10-17 ์ค์ธก ๊ฐ์ฌ, [IconButton `edge`](icon-button.md#๋ฐฐ์น)) | ์ renderer |
|
|
81
84
|
| ๊ณ ์ ยท์คํฌ๋กค | ํ์ ์คํฌ๋กคํ์ง ์๋๋ค. ๋ชฉ๋ก ์คํฌ๋กคยทํ ๊ฐ๊ฒฉ(`rowGap` 12)ยท๋ต๊ธ ๋ค์ฌ์ฐ๊ธฐ(`replyIndent` 40)๋ host๊ฐ ์ค๋ค | `CommentThreadScreen` |
|
|
82
85
|
| ์ข์ ํญยทํฐ ๊ธ์ | ๋ณธ๋ฌธ ์ด์ด `min-width: 0`์ผ๋ก ์ค๋ฉฐ ์ค๋ฐ๊ฟ๋๊ณ ์์ฑ์ยทํ๋ ์ค์ `flexWrap`. ์ข์์ ์ด์ ์ค์ง ์๋๋ค | `.hjm-comment-row__text`, `flexShrink: 0` |
|
|
83
86
|
|
|
@@ -85,7 +88,8 @@ import { CommentThreadRow } from "@hjmds/react-native/screen-flows";
|
|
|
85
88
|
|
|
86
89
|
- ๊ฐ์ํ host์์ ํ ์ฌ์ด ๊ฐ๊ฒฉ(`commentThreadRecipe.rowGap`)๊ณผ ๋ต๊ธ ๋ค์ฌ์ฐ๊ธฐ(`replyIndent`)๋ฅผ ์ง์ ์ค ๋๋ recipe ๊ฐ์ ์ด๋ค. ์ ํ ์ซ์๋ก ๋ค์ ์ ํ์ง ์๋๋ค.
|
|
87
90
|
- ๋ต๊ธ ํผ์น๊ธฐยท์ ๋ ฌยท๊ถํยท์ข์์ ์ ์ฅ์ ์ ํ์ด ์์ ํ๋ค. ํ์ ์ฝ๋ฐฑ๋ง ํธ์ถํ๋ค.
|
|
88
|
-
- ํ ์์ ๋ ๋ฒ์งธ ๋ ์ด์์์ ๋ง๋ค์ง ๋ง๊ณ `actions`ยท`likeAction`ยท`media` ์ฌ๋กฏ์ผ๋ก ์ถ๊ฐํ๋ค.
|
|
91
|
+
- ํ ์์ ๋ ๋ฒ์งธ ๋ ์ด์์์ ๋ง๋ค์ง ๋ง๊ณ `actions`ยท`likeAction`ยท`media`ยท`mediaAlbum`ยท`panel` ์ฌ๋กฏ์ผ๋ก ์ถ๊ฐํ๋ค. ์๊ฐยท๋ต๊ธ ์ค์ ๋ณธ๋ฌธ ๋
ธ๋ ์์ ๋ค์ ๊ทธ๋ฆฌ์ง ๋ง๊ณ `timePlacement: "actions"`์ `canReply`๋ฅผ ์ด๋ค.
|
|
92
|
+
- `mediaAlbum`์ ์ฌ์ง ์กฐํยท๊ถํ ํ์ธยท์บ์ ์๋ช
์ ์ ํ์ด `renderPage` ์์์ ์์ ํ๋ค. HJM์ ๋ณด์ด๋ ์ฅ๋ง ๋ง์ดํธํ๊ณ ๋๊น ์ํ๋ฅผ ํ ์์ ๋๋ค(์ ์ดํ์ `index`+`onIndexChange`).
|
|
89
93
|
|
|
90
94
|
## ์ํ์ ์ํธ์์ฉ
|
|
91
95
|
|
|
@@ -93,6 +97,10 @@ import { CommentThreadRow } from "@hjmds/react-native/screen-flows";
|
|
|
93
97
|
`reactions`๋ฅผ ์ฃผ๋ฉด ์์ฑ์ยท๋ณธ๋ฌธ๋ง ๊ธธ๊ฒ ๋๋ฅด๊ธฐ ๋์์ด ๋๊ณ , ๋ต๊ธ ๊ฐ๋ฅํ๋ฉด ๋ฉ๋ด์ ๋ต๊ธ ํญ๋ชฉ์ด ์๋์ผ๋ก ๋ถ๋๋ค.
|
|
94
98
|
๋ต๊ธ ํ๋ ๋ฒํผ์ ๊ธธ๊ฒ ๋๋ฅด๊ธฐ ๋์ ๋ฐ์ ๋จ์ ์๊ธฐ ๋ฒํผ ์๋ฏธ๋ฅผ ์ ์งํ๋ค.
|
|
95
99
|
|
|
100
|
+
## ๊ทผ๊ฑฐ
|
|
101
|
+
|
|
102
|
+
- `timePlacement`ยท`panel`ยท`mediaAlbum`: 2026-10-18 HJM 1.30.0 ์ฑํ ๊ฐ์ฌ. Spint๋ ๊ฝ ๋์ฅ ๋ณธ๋ฌธ์์ ์๊ฐ์ ๋ต๊ธ ์ค์ ๋๋ ค๊ณ ์๊ฐยท๋ต๊ธ ํ์ ๋ณธ๋ฌธ ์์ ๋ค์ ๊ทธ๋ ธ๊ณ , Utilverse๋ ์์ ยท์ ๊ณ ยท์ฐจ๋จ ์์์ ๋ณธ๋ฌธ ๋
ธ๋ ์(๊ธธ๊ฒ ๋๋ฅด๊ธฐ ๋์ ์)์ ๋ฃ์์ผ๋ฉฐ, ์ฌ์ง ๋ฌถ์์ ์ ํํ ์ฅ๋ง ์กฐํํ๋ ์ ํ ์จ๋ฒ์ ๋ฐ๋ก ๋ง๋ค์๋ค.
|
|
103
|
+
|
|
96
104
|
## ํ๋ซํผ ์ฐจ์ด
|
|
97
105
|
|
|
98
106
|
| ํญ๋ชฉ | Web | Native |
|
|
@@ -93,7 +93,7 @@ import { MessageComposer } from "@hjmds/react-native/screens";
|
|
|
93
93
|
`likeIcon`, `likeLabel`์ ํ์. ์ ํ์ `bodyText`(์์ฑ์์ ๋ณธ๋ฌธ์ ํ ์ค ํ๋ฆ์ผ๋ก), `avatar`, `likeAction`(๊ธฐ๋ณธ
|
|
94
94
|
์ข์์ ๋ฒํผ ๊ต์ฒด, `null`์ด๋ฉด ์๋ต), `actions`(ํํธ ๋ฐ๋ก ์์ ๋๋ณด๊ธฐ ๋ฉ๋ด ๋ฑ), `canReply`(๊ธฐ๋ณธ: ์ต์์๋ง true), `replyDisabled`.
|
|
95
95
|
`reactions`(๋ฏธ๊ฒ์)๋ฅผ ์ฃผ๋ฉด ์์ฑ์ยท๋ณธ๋ฌธ์ ๊ธธ๊ฒ ๋๋ฌ ChatMessage์ ๊ฐ์ ๋ฉ๋ด(๋ฐ์ ์ค โ ๋ฏธ๋ฆฌ๋ณด๊ธฐ โ ์๊ฐ โ ๋ต๊ธ ๋ฌ๊ธฐ โ `menuActions` โ `reportAction` ์ ๊ณ )๊ฐ ์ด๋ฆฐ๋ค. ๋ต๊ธ ๋ฌ๊ธฐ๋ `canReply`์ผ ๋ `onReply`๋ก ์๋ ์ฐ๊ฒฐ๋๊ณ , ์์ด์ฝ์ `reactions.replyIcon`์ผ๋ก ๋๊ธด๋ค. `reactionSummary`๋ ๋ต๊ธ ๋ฌ๊ธฐ ์์ ๋๋ ์ ํ๋ ๋ฐ์ ํ์ ์ฌ๋กฏ์ด๋ค. ๋๊ธ์ ๊ธฐ๋ณธ์ ๋ฐ์ ์ต์
์ ๋น์ด(`options: []`) ๋์ ๋ชฉ๋ก ์ ์ฉ ๋ฉ๋ด๋ค(๋ต๊ธ ๋ฌ๊ธฐ โ `menuActions` โ ์ ๊ณ , ์ธ์คํ๊ทธ๋จ ๋๊ธ ๊ธธ๊ฒ ๋๋ฅด๊ธฐ ์ค์ธก). ๋๊ธ ์ด๋ชจ์ง๋ ๊ธธ๊ฒ ๋๋ฅด๊ธฐ๊ฐ ์๋๋ผ ์์ฑ์ฐฝ์ `quickInserts` ์ด๋ชจ์ง ์ค๋ก ์
๋ ฅํ๋ค. ๋ฐ์ ์ค์ ์ ํ์ด ๋ช
์์ ์ผ๋ก ๋๊ธ ๋ฐ์์ ์ด์ํ ๋๋ง ์ต์
์ ๋ฃ๋๋ค. ๋๊ธ ๋ฉ๋ด์๋ ์๊ฐ ์ ๋ชฉ์ ๋์ง ์๋๋ค(ํ์ ์ด๋ฏธ ๋ณด์ธ๋ค).
|
|
96
|
-
`media`(๋ฏธ๊ฒ์, ์ ํ)๋ ๋ณธ๋ฌธ ์๋ ์ฌ์งยท์คํฐ์ปค์ด๋ฉฐ ์ต๋ ํญ `commentThreadRecipe.mediaMaxWidth` 160, radius 12๋ก ๊ฐ์ผ๋ค. `likeCountLabel`์ ํํธ ์๋ ์ซ์๋ง(์: "15") ๋ฃ๊ณ 0์ด๋ฉด ๋น ๋ฌธ์์ด๋ก ์จ๊ธด๋ค. ๊ฐ์๋ฅผ ํฌํจํ ์ ์ฒด ๋ฌธ์ฅ์ `likeLabel`์ ๋๋ค.
|
|
96
|
+
`media`(๋ฏธ๊ฒ์, ์ ํ)๋ ๋ณธ๋ฌธ ์๋ ์ฌ์งยท์คํฐ์ปค์ด๋ฉฐ ์ต๋ ํญ `commentThreadRecipe.mediaMaxWidth` 160, radius 12๋ก ๊ฐ์ผ๋ค. ์ฌ์ง ์ฌ๋ฌ ์ฅ์ ํ ์ฅ์ฉ ๋ถ๋ฌ์์ผ ํ๋ฉด `media` ๋์ `mediaAlbum`, ์๊ฐ์ ๋ต๊ธ ์ค๋ก ์ฎ๊ธฐ๋ ค๋ฉด `timePlacement: "actions"`, ์์ ยท์ ๊ณ ยท์ฐจ๋จ ์์์ `panel`์ ์ด๋ค(๋ฏธ๊ฒ์, [CommentThreadRow](comment-thread-row.md#์ถ๊ณผ-๊ธฐ๋ณธ๊ฐ)). `likeCountLabel`์ ํํธ ์๋ ์ซ์๋ง(์: "15") ๋ฃ๊ณ 0์ด๋ฉด ๋น ๋ฌธ์์ด๋ก ์จ๊ธด๋ค. ๊ฐ์๋ฅผ ํฌํจํ ์ ์ฒด ๋ฌธ์ฅ์ `likeLabel`์ ๋๋ค.
|
|
97
97
|
|
|
98
98
|
## ๋ฐฐ์น
|
|
99
99
|
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
| --- | --- | --- | --- |
|
|
39
39
|
| `Dialog` | ๊ธฐ๋ณธ | `@hjmds/react`, `/overlays` | `@hjmds/react-native`, `/overlays` |
|
|
40
40
|
| `OverlayStackProvider` | ํ์ฅ โ ๋ช
๋ นํ ์ด๊ธฐ(`useDialog`ยท`useSheet`ยท`useOverlayStack`) | `@hjmds/react`, `/overlay-stack` | ์์ |
|
|
41
|
+
| `ImmersiveOverlay` | ํ์ฅ โ ์นด๋ยทscrimยท๋จธ๋ฆฌ ์์ด ํ๋ฉด ์ ์ฒด๋ฅผ ์ฑ์ฐ๋ ํํ(1.31.0). ์๋ [์ ์ฒด ํ๋ฉด ํํ](#์ ์ฒด-ํ๋ฉด-ํํ-immersiveoverlay) | `@hjmds/react`, `/overlays` | `@hjmds/react-native`, `/overlays` |
|
|
41
42
|
|
|
42
43
|
## ์ต์ ์ฌ์ฉ ์
|
|
43
44
|
|
|
@@ -127,6 +128,8 @@ await handle.closed; // portal ์ ๊ฑฐยท์ด์ ๋ณต๊ตฌ ๋ค. ๋ค์ ์ค๋ฒ๋ ์ด๋
|
|
|
127
128
|
| `onActionError`(Native) | `(error: unknown) => void` | โ | action ์คํจ๋ฅผ ๋ณธ๋ฌธ ์ค๋ฅยท์ฌ์๋๋ก ์ฐ๊ฒฐํ๋ค. ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ๋ ๋์ ์ด์์ ๋ณด์กดํ๋ค. ์์ผ๋ฉด ๊ฐ๋ฐ ๋น๋์์ ์คํจ๋ง๋ค `console.error`๋ฅผ ๋จ๊ธด๋ค(์ค๋ณต ์ ๊ฑฐ ์์). 1.13.0 ๋ณ๊ฒฝ |
|
|
128
129
|
| `children`(Native) | `ReactNode` | โ | ๊ณ ์ ๋ ์ ๋ชฉ ์ค ์๋ ์คํฌ๋กค ๋ณธ๋ฌธ(`ScrollView`) ์์ ๋ค์ด๊ฐ๋ค. `FlatList`ยท`SectionList`๋ฅผ ๋ฃ์ง ์๋๋ค |
|
|
129
130
|
| `contentStyle`(Native) | ๋ฐฐ์น key๋ง(`HjmCompositionStyleProp`) | โ | ๋ํ์์ ์์์ ๋ฐฐ์น. ๋์ด๋ `size`๋ก |
|
|
131
|
+
| `overlayLayer` | `ReactNode` | ์์ | 1.31.0. ์นด๋ **๋ฐ** ํ๋ฉด ์ ์ฒด ์ธต์ ๊ทธ๋ฆฌ๋ ์ฅ์(๋๊ธฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐยทํน์ ์์น ๊ฐ์กฐ). ์นด๋๋ณด๋ค ์์ ๊ทธ๋ ค์ง๊ณ Web viewport/Native window ์ขํ(`clientX`ยท`pageX`ยท`measureInWindow`)๋ฅผ ๊ทธ๋๋ก ์ด๋ค. ๋๋ฆ์ ๋ฐ์ง ์๊ณ ๋ณด์กฐ๊ธฐ์ ์์ ์จ๊ธด๋ค(Web `inert`ยท`aria-hidden`, Native `pointerEvents="none"`ยท`no-hide-descendants`). ์กฐ์ํ ๋ด์ฉ์ `children`์ ๋๋ค |
|
|
132
|
+
| `scrollEnabled` | `boolean` | `true` | 1.31.0. `false`๋ฉด ์ฌ์ฉ์๊ฐ ๋ํ์์๋ฅผ ์คํฌ๋กคํ์ง ๋ชปํ๋ค(ํ์ฌ ์์น ์ ์ง). Web์ ์นด๋ `overflow: hidden`, Native๋ ๋ณธ๋ฌธ ScrollView `scrollEnabled`. ๋ณธ๋ฌธ ์ ๋๊ธฐ ์ ์ค์ฒ๊ฐ ์งํ๋๋ ๋์๋ง ๋๊ณ ๋๋๋ฉด ๋๋๋ฆฐ๋ค. ๋ด์ฉ์ ์๋ฅด๋ ์ฉ๋๋ก ์ฐ์ง ์๋๋ค |
|
|
130
133
|
| `useDialog()`(Web) | `(request) => { closed: Promise<void>, close(): void }` | โ | request๋ `open`ยท`defaultOpen`ยท`onOpenChange`ยท`trigger`๋ฅผ ๋บ DialogProps. `OverlayStackProvider` ์์์๋ง |
|
|
131
134
|
|
|
132
135
|
- Dialog๋ `layoutStyle`์ ๋ฐ์ง ์๋๋ค(Web ์ ์ธ 15๊ฐ ์ค ํ๋ โ ํฌํธ๋ก ๊ฐ์ด๋ฐ ๊ทธ๋ ค์ ธ ๋ฐฐ์นํ ํ๋ฆ ์ ๋ฃจํธ๊ฐ ์๋ค).
|
|
@@ -178,6 +181,66 @@ Native, ํฐ ๊ธ์ ๋๋ ํญ < 480: [ ์ทจ์ ]
|
|
|
178
181
|
- Native์์ ์ ์ฅ์ฒ๋ผ ์คํจํ ์ ์๋ action์ `onActionError`๋ฅผ ๋๊ธด๋ค. ๋จ๋ฐ์ฑ ์์
์ Promise๋ฅผ ๋ฐํํ๋ฉด ์๋ฃ๊น์ง ๋ซํ์ง ์์ผ๋ฏ๋ก, ์ฆ์ ๋ซ์ผ๋ ค๋ฉด ์๋ฌด๊ฒ๋ ๋ฐํํ์ง ์๋๋ค.
|
|
179
182
|
- ๋ฐฐ์น ๋ณ๊ฒฝ์ Native `contentStyle`(layout ์ ์ฉ)๋ง ์ด๋ค. ๋์ด๋ `size`๋ก ์ ํ๋ค. Web์ `className`๋ง ๋ฐ์ผ๋ฉฐ ์๊ฐ override์ ์ฐ์ง ์๋๋ค.
|
|
180
183
|
|
|
184
|
+
### ์นด๋ ๋ฐ ๋ ์ด์ด (`overlayLayer`)
|
|
185
|
+
|
|
186
|
+
ํด๋ ์ ์์ด์ฝ์ ๋ ๋์ฒ๋ผ ์๊ฐ๋ฝยทํฌ์ธํฐ๋ฅผ ๋ฐ๋ผ๊ฐ๋ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ ์นด๋ ์์ ๊ทธ๋ฆฌ๋ฉด ์นด๋์ ์์ชฝ ์ฌ๋ฐฑยท์คํฌ๋กคยท์๋ฆผยท์ด๋ฆผ
|
|
187
|
+
๋ณํ ๋๋ฌธ์ ์ขํ๊ฐ ์ด๊ธ๋๊ณ ์นด๋์ ๊ฐํ๋ค. `overlayLayer`๋ ์นด๋์ ๊ฐ์ ๋ชจ๋ฌ ์์ด์ง๋ง ์นด๋์ ํ์ ์ธต์ด๋ผ ์ด ๋ฌธ์ ๊ฐ ์๋ค.
|
|
188
|
+
๋๊ธฐ ์ ์ค์ฒ์ ๋๋กญ ๋์ ์ธก์ ์ ์นด๋ ์ `children`์ด ์์ ํ๊ณ , ์ธต์๋ ํ์ฌ ์ขํ์ ๊ทธ๋ฆผ๋ง ๋๊ธด๋ค.
|
|
189
|
+
Native ์
๋ ฅ ๋ณธ๋ฌธ์ ํค๋ณด๋ ํํผ๋ Dialog๊ฐ ์ด๋ฏธ ๋งก๋๋ค(์ ํค๋ณด๋ ์ค๋ช
). ์ ํ์ด `KeyboardAvoidingView`๋ฅผ ๋ค์ ๊ฐ์ธ์ง ์๋๋ค.
|
|
190
|
+
๋๊ธฐ ์ค์๋ `scrollEnabled={!dragging}`์ผ๋ก ๋ณธ๋ฌธ ์คํฌ๋กค์ ์ ๊ทผ๋ค. ์ ๊ทธ์ง ์์ผ๋ฉด Native ScrollView๊ฐ ์ธ๋ก ์์ง์์ ๊ฐ์ ธ๊ฐ ๋๊ธฐ๊ฐ ๋๊ธฐ๊ณ , Web์ ์นด๋๊ฐ ํจ๊ป ์คํฌ๋กค๋๋ค.
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
// Native
|
|
194
|
+
<Dialog open={open} onOpenChange={setOpen} title={folder.name} closeLabel={t("common.close")}
|
|
195
|
+
scrollEnabled={drag === null}
|
|
196
|
+
overlayLayer={drag ? <DragGhost x={drag.pageX} y={drag.pageY} item={drag.item} /> : null}>
|
|
197
|
+
{grid /* ๋๊ธฐ responder์ measureInWindow ๋ฑ๋ก์ ์ฌ๊ธฐ */}
|
|
198
|
+
</Dialog>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
<!-- usage-example: Web ../../../../../showcase/web/src/components/dialog-overlay-examples.tsx -->
|
|
202
|
+
- [Web ์คํ ์์ ](https://github.com/jim1286/hjm-design-system/blob/8afe62bcf44b4ced063998bc9669b55704265f2d/showcase/web/src/components/dialog-overlay-examples.tsx): `DialogOverlayLayerPreview`ยท`ImmersiveOverlayPreview`(`๋ฐฐํฌ/์ปดํฌ๋ํธ/์ค๋ฒ๋ ์ด/๋ํ์์`์ ์นด๋ ๋ฐ ๋ ์ด์ดยท์ ์ฒด ํ๋ฉด ํํ).
|
|
203
|
+
<!-- usage-example: Native ../../../../../showcase/native/src/dialog-overlay-preview.tsx -->
|
|
204
|
+
- [Native ์คํ ์์ ](https://github.com/jim1286/hjm-design-system/blob/8afe62bcf44b4ced063998bc9669b55704265f2d/showcase/native/src/dialog-overlay-preview.tsx): ๊ฐ์ ์ด๋ฆ์ Native preview.
|
|
205
|
+
|
|
206
|
+
### ์ ์ฒด ํ๋ฉด ํํ (`ImmersiveOverlay`)
|
|
207
|
+
|
|
208
|
+
์ ๊ดํยท๋ฐํ ํ๋ฉด์ฒ๋ผ **ํ๋ฉด ์์ฒด๊ฐ ๋ด์ฉ**์ธ ํํ์ ์ด๋ค. ์นด๋ยทscrimยท๋จธ๋ฆฌยท์์ชฝ ์ฌ๋ฐฑ์ด ์๊ณ , ์ ํ์ด ๊ทธ๋ฆฐ ๋ฌด๋๊ฐ ํ๋ฉด ๋๊น์ง
|
|
209
|
+
์ฑ์ด๋ค. ์๋์๋ HJM ํ๋ ์ค(์ํ ๋ฌธ๊ตฌ โ ๋๊ตฌ ํ๋ โ ๋๊ฐ๊ธฐ ํ๋)์ด safe area ์์ ๋ถ๋๋ค. ๋ชจ๋ฌ ์คํยทํฌ์ปค์ค ๊ฐ๋๊ธฐยท
|
|
210
|
+
๋ฐฐ๊ฒฝ ๊ฒฉ๋ฆฌยท๋ซํ ๋ค ํฌ์ปค์ค ๋ณต๊ทยท์๋ ํ๋ฉด์ ์ง์
ํฌ์ปค์ค ๋๊ธฐ๋ Dialog์ ๊ฐ๋ค.
|
|
211
|
+
|
|
212
|
+
| prop | ๊ฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช
|
|
|
213
|
+
| --- | --- | --- | --- |
|
|
214
|
+
| `open`ยท`onOpenChange` | `boolean`, `(open, { reason }) => void` | โ | ์ ์ดํ๋ง. Web `reason`: `"exit-action"`ยท`"escape"`. Native: `"exit-action"`ยท`"back"` |
|
|
215
|
+
| `label` | ํ์งํ ๋ฌธ์์ด | โ | ํํ ์ ์ฒด์ ์ ๊ทผ์ฑ ์ด๋ฆ |
|
|
216
|
+
| `exitLabel` | ํ์งํ ๋ฌธ์์ด | โ | ๋๊ฐ๊ธฐ ๋ฒํผ์ ๋ณด์ด๋ ๋ฌธ๊ตฌ(์ ํ ๋ฌธ๊ตฌ) |
|
|
217
|
+
| `children` | `ReactNode` | โ | ๋ฌด๋. ๋จ์ ๋์ด ์ ์ฒด๋ฅผ ์ฑ์ฐ๊ณ ๋์น๋ ๋ถ๋ถ์ ์๋ฆฐ๋ค |
|
|
218
|
+
| `tools` | `ReactNode` | ์์ | ๋๊ฐ๊ธฐ ์์ ๋๋ ๋๊ตฌ ํ๋(๋ฉ์ถค ๋ฑ `IconButton`) |
|
|
219
|
+
| `status` | `ReactNode` | ์์ | ํ๋ ์ค ์ ์ํ ๋ฌธ๊ตฌยท`Notice` |
|
|
220
|
+
| `returnFocusRef` | ref | ์์ | ๋ซํ ๋ค ๋์๊ฐ ๋์ |
|
|
221
|
+
| `initialFocusRef`(Web) | ref | ๋๊ฐ๊ธฐ ๋ฒํผ | ์ด๋ฆด ๋ ํฌ์ปค์ค |
|
|
222
|
+
| `modalPriority`ยท`portalContainer`ยท`className`(Web) | โ | โ | Dialog์ ๊ฐ๋ค |
|
|
223
|
+
| `onShow`(Native) | RN `Modal` `onShow` | โ | ํ์ ์๋ฃ |
|
|
224
|
+
|
|
225
|
+
- Native๋ `presentationStyle="fullScreen"` Modal์์ ์ํ ํ์์ค์ ์จ๊ธฐ๊ณ ์ธ๋กยท๊ฐ๋ก ํ์ ์ ํ์ฉํ๋ค(`dialogRecipe.immersive.orientations`, ์ฑ์ด ์ธ๋ก ๊ณ ์ ์ด์ด๋ ์ด ํํ์ ํ์ ํ๋ค). ๋ฌด๋๋ ํ๋ฉด ๋๊น์ง ๊ทธ๋ ค์ง๋ฏ๋ก ์ฝ์ด์ผ ํ ๋ด์ฉ์ ๊ฐ์ด๋ฐ ๋๊ฑฐ๋ `useHjmNativeSafeAreaInsets`๋ก ๊ฐ์ฅ์๋ฆฌ ์ ๊ฐ๋ฅผ ํผํ๋ค.
|
|
226
|
+
- Web์ viewport ์ ์ฒด๋ฅผ ๋ฎ๋ ๊ณ ์ ์ธต์ด๋ค. ๋ธ๋ผ์ฐ์ Fullscreen API๋ ์์ฒญํ์ง ์๋๋ค. ์ ์ฌ์ฉ์ ํ์ฑํ๊ฐ ํ์ํ๊ณ , ๊ทธ Escape๋ ํ์ด์ง๊ฐ ๋ฐ๊ธฐ ์ ์ ์ ์ฒด ํ๋ฉด๋ง ๋๋ด ์ค๋ฒ๋ ์ด๊ฐ ์ฐฝ ์์ ๋จ์ผ๋ฉฐ, iOS Safari๋ ์์ ์ ์ฒด ํ๋ฉด์ ์ง์ํ์ง ์๋๋ค.
|
|
227
|
+
- ํ๋ ์ค ๊ฐ๊ฒฉ: ์์ชฝ `spacing.md` 16(์๋ยท์ข์ฐ๋ safe area์ ํฐ ๊ฐ), ์ํโํ๋ `spacing.xs` 8, ํ๋ ์ฌ์ด `spacing.sm` 12. ๋๊ฐ๊ธฐ ๋ฒํผ์ `size="large"`๋ก ๋จ์ ํญ์ ์ฑ์ด๋ค.
|
|
228
|
+
- ํ๋ฉด ๊บผ์ง ๋ฐฉ์งยท์์ง์ ๊ฐ์ ๋ฌด๋ ๋์์ ์ ํ ์์ ๋ค.
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
// Native
|
|
232
|
+
import { ImmersiveOverlay } from "@hjmds/react-native/overlays";
|
|
233
|
+
|
|
234
|
+
<ImmersiveOverlay open={presenting} onOpenChange={(next) => { if (!next) setPresenting(false); }}
|
|
235
|
+
label={t("display.title")} exitLabel={t("displayClose")}
|
|
236
|
+
status={<Text variant="caption" tone="muted">{t("displayWakeActive")}</Text>}
|
|
237
|
+
tools={<IconButton tone="secondary" size="large" label={t("displayPause")} onPress={togglePause}><PauseGlyph /></IconButton>}>
|
|
238
|
+
{sign /* ์ ํ์ด ๊ทธ๋ฆฐ ์ ๊ดํ */}
|
|
239
|
+
</ImmersiveOverlay>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
์คํ ์์ : ์ ๊ฐ์ ํ์ผ์ `ImmersiveOverlayPreview`(`๋ฐฐํฌ/์ปดํฌ๋ํธ/์ค๋ฒ๋ ์ด/๋ํ์์` ์ ์ฒด ํ๋ฉด ํํ).
|
|
243
|
+
|
|
181
244
|
## ํ๋ซํผ ์ฐจ์ด
|
|
182
245
|
|
|
183
246
|
| ํญ๋ชฉ | Web | Native |
|
|
@@ -255,3 +318,9 @@ function RecordDetail() {
|
|
|
255
318
|
์ค์ View์ `measureInWindow`๋ก ํ์ฑํ ์์ ์ ์์ญ์ ์ป๊ณ , ์ขํ๋ฅผ ์ป์ง ๋ชปํด๋ ์ผ๋ฐ Dialog๋ฅผ ์ฐ๋ค.
|
|
256
319
|
์ธก์ callback๋ง ๋ฌด๊ธฐํ ๊ธฐ๋ค๋ฆฌ๋ฉด์ ์ด๊ธฐ ํ๋์ ๋ง์ง ์๋๋ค. ๋ฒํผ์ ์ ๊ทผ์ฑ ์ด๋ฆ๊ณผ ์ด์ ๋ณต๊ท ๋์์
|
|
257
320
|
์ ์งํ๊ณ , ์นด๋ ์ ์ฒด๋ฅผ ๋๋ฆ ๊ฐ๋ฅํ ๋น์๋ฏธ์ View/div๋ก ๋ฐ๊พธ์ง ์๋๋ค.
|
|
321
|
+
|
|
322
|
+
## ๊ทผ๊ฑฐ
|
|
323
|
+
|
|
324
|
+
- `overlayLayer`: 2026-10-18 ๋ฒ๋ ํด๋ ํ์
. ๋๊ธฐ ์ ๋ น ์์ด์ฝ์ ์ฐฝ ์ขํ๋ก ์นด๋ ๋ฐ์ ๊ทธ๋ ค์ผ ํด์ Dialog ๋์ raw RN `Modal`๊ณผ `KeyboardAvoidingView`๋ฅผ ์ผ๋ค. ํค๋ณด๋ ํํผ๋ 2026-10-13๋ถํฐ Dialog๊ฐ ์ด๋ฏธ ๊ฐ๊ณ ์์๊ณ , ์นด๋ ๋ฐ ์ธต๋ง ์์๋ค.
|
|
325
|
+
- `scrollEnabled`: 2026-10-18 ๋ฒ๋ ํด๋ ํ์
. ์์ด์ฝ์ ๋๋ ๋์ ๋ณธ๋ฌธ ์คํฌ๋กค์ ๊บผ์ผ ํด์(`scrollEnabled={!touchingIcon && !draggingId}`) raw `Modal`์ ScrollView๋ฅผ ์ ์งํ๋ค.
|
|
326
|
+
- `ImmersiveOverlay`: 2026-10-18 utilverse ์ ๊ดํ. DialogยทSheet๋ ํญ์ ์นด๋ยทscrim(`full` Sheet๋ ์ safe areaยท๋ชจ์๋ฆฌยท๋จธ๋ฆฌ)์ ๊ทธ๋ ค ์ํ ํ์์ค์ ์จ๊ธด ๊ฐ๋ก ์ ๊ดํ์ ํํํ์ง ๋ชปํด raw RN `Modal`์ ์ผ๋ค. ํํ๋ง ๋ค๋ฅด๊ณ ๋ชจ๋ฌ ์คํยทํฌ์ปค์คยท๋ซ๊ธฐ ๊ณ์ฝ์ Dialog์ ๊ฐ์ Dialog์ ํ์ฅ์ผ๋ก ๋๋ค.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- ์ํ: ๋ฐฐํฌ
|
|
5
5
|
- ์ง์: Web ยท Native
|
|
6
6
|
- ์ ์ฉ: 1.17.1
|
|
7
|
-
- ๊ฒํ ์ผ: 2026-10-
|
|
7
|
+
- ๊ฒํ ์ผ: 2026-10-18
|
|
8
8
|
- ๊ทผ๊ฑฐ: [๋ฐ๋ณต ํ๋ฉด ์กฐํฉ](../../screen-patterns.md), WebยทNative `src/screens.tsx`ยท`src/screen-flows.tsx`; ๊ธฐ์กด ๊ฐ๋ณ ์ง์นจ์ ์ ๊ท๊ฒฉ์ผ๋ก ํตํฉ. ์์ ์คํ ๋ฆฌ๋ 2026-10-06 ์ฌ์ฉ์ ์น์ธ์ผ๋ก ์คํ ๋ฆฌ๋ถ ๋ฐฐํฌ([์น์ธ ๊ธฐ๋ก](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-์ ์ฒด-์น๊ฒฉ๊ณผ-๊ท๊ฒฉ-ํ์ )). ์คํ ๋ฆฌ๋ถ ๋ฐฐํฌ๋ API ๊ฒ์๊ฐ ์๋๋ค(`์ ์ฉ` ์ฐธ๊ณ )
|
|
9
9
|
- ์คํ ๋ฆฌ๋ถ: `๋ฐฐํฌ/ํ๋ฉด/์ฝํ
์ธ /์์ฑ๊ณผ ์์ `, `๋ฐฐํฌ/ํ๋ฉด/๊ณ์ /ํ๋กํ`
|
|
10
10
|
|
|
@@ -95,6 +95,7 @@ import { TextField } from "@hjmds/react-native/inputs";
|
|
|
95
95
|
| `dirty` | `boolean` | ํ์ | `true`์์ ๋ซ๊ธฐ๋ฅผ ๋๋ฅด๋ฉด `discard` ํ์ธ์ฐฝ์ ๋์ฐ๊ณ , ํ์ธํ๋ฉด `cancel.onAction`์ ๋ถ๋ฅธ๋ค. `false`๋ฉด ๋ฐ๋ก ๋ถ๋ฅธ๋ค |
|
|
96
96
|
| `submit` | `ScreenFlowAction`(`{ label, onAction(), disabled?, pending? }`) | ํ์ | `pending`์ด๋ฉด loadingยท๋นํ์ฑ. ๊ทธ๋์ ๋ซ๊ธฐ ๋ฒํผ๋ ๋นํ์ฑ. ๋ณธ๋ฌธ `state`๊ฐ `ready`๊ฐ ์๋๋ฉด ์ ์ฅ ๋นํ์ฑ |
|
|
97
97
|
| `cancel` | `ScreenFlowAction` | ํ์ | ํค๋ ์์ ์ชฝ ร ์์ด์ฝ ๋ซ๊ธฐ ๋ฒํผ(`ScreenLayout` `exit` `close`). `label`์ ๋ณด์กฐ๊ธฐ์ ์ด๋ฆ์ด๋ค. 2026-10-17๋ถํฐ ๊ธ์ ๋ฒํผ ๋์ ์์ด์ฝ |
|
|
98
|
+
| `stepBack` | `ScreenFlowAction` | ์์ | 1.31.0. ๋จ๊ณํ ์์ฑ์์ ์ฃผ๋ ๋์ ํค๋ ์์ ์ชฝ์ด ๋ค๋ก ํ์ดํ(`exit` `back`)๊ฐ ๋๊ณ ๋๋ฅด๋ฉด `stepBack.onAction`(์ ๋จ๊ณ๋ก)์ ๋ถ๋ฅธ๋ค. ๋จ๊ณ๋ฅผ ๋๋๋ฆฌ๋ ์ผ์ด๋ผ `dirty`์ฌ๋ `discard` ํ์ธ์ ๋์ฐ์ง ์๊ณ , ์ด๋ฏธ ์ด๋ฆฐ ๋ฒ๋ฆฌ๊ธฐ ํ์ธ๋ ๋ซ๋๋ค. `disabled`ยท`pending` ๋๋ `submit.pending`์ด๋ฉด ์ ๊ธด๋ค. ์ฒซ ๋จ๊ณ์์๋ ๋นผ์ ร ๋ซ๊ธฐ๋ก ๋์๊ฐ๋ค |
|
|
98
99
|
| `discard` | AlertDialog confirm ์์ฒญ์์ `onConfirm`์ ๋บ ๊ฒ + `fallbackErrorMessage: string` | ํ์ | `onConfirm`์ EditorScreen์ด ์ฑ์ด๋ค |
|
|
99
100
|
| `submitPlacement` | `"footer"` ยท `"header"` | `"footer"` | `header`๋ฉด ์ ์ฅ์ด ์๋จ `actions` ์๋ฆฌ๋ก ๊ฐ๊ณ footer์๋ `draftStatus`๋ง ๋จ๋๋ค |
|
|
100
101
|
| `draftStatus` | `ReactNode` | ์์ | ์ด์ ์ ์ฅ ์ํ ์๋ด. footer์ ๋์ธ๋ค |
|
|
@@ -107,7 +108,7 @@ import { TextField } from "@hjmds/react-native/inputs";
|
|
|
107
108
|
| --- | --- | --- |
|
|
108
109
|
| ํฌ๊ธฐ | ScreenLayout ํญ(์ต๋ 720) ์์ ์
๋ ฅ ๋ณธ๋ฌธ; ์ ์ฅ์ `Button` ๊ธฐ๋ณธ ํฌ๊ธฐ, ๋ซ๊ธฐ๋ `IconButton` | `ScreenLayout`, `screen-flows.tsx` `Action` |
|
|
109
110
|
| ๊ฐ๊ฒฉ | ํ๋ฉด padding `spacing.md` 16; footer ์ `draftStatus`โ์ ์ฅ `spacing.sm` 12(`Stack gap="sm"`); ๋ณธ๋ฌธ ์ ์
๋ ฅ ๊ฐ๊ฒฉ์ ์ ํ(Form ๋ฑ) ์์ | WebยทNative `EditorScreen` |
|
|
110
|
-
| ์์ยท์ ๋ ฌ | ํค๋(๋ซ๊ธฐ โ ์ ๋ชฉ โ [์ ์ฅ: header ๋ฐฐ์น]) โ ๋ณธ๋ฌธ โ footer(`draftStatus` โ ์ ์ฅ) | `EditorScreen` ๋ ๋ ์์ |
|
|
111
|
+
| ์์ยท์ ๋ ฌ | ํค๋(๋ซ๊ธฐ ๋๋ `stepBack` ๋ค๋ก โ ์ ๋ชฉ โ [์ ์ฅ: header ๋ฐฐ์น]) โ ๋ณธ๋ฌธ โ footer(`draftStatus` โ ์ ์ฅ) | `EditorScreen` ๋ ๋ ์์ |
|
|
111
112
|
| ๊ณ ์ ยท์คํฌ๋กค | ํค๋ยทfooter ๊ณ ์ , ๋ณธ๋ฌธ ์คํฌ๋กค(`scroll` ๊ธฐ๋ณธ `"screen"`); ์ดํ ํ์ธ์ AlertDialog ์ค๋ฒ๋ ์ด | `ScreenLayout`, `AlertDialog` |
|
|
112
113
|
| ์ข์ ํญยทํฐ ๊ธ์ | ์ ๋ชฉ ์ด ์ต์ ํญ 120 ร ๊ธ์ ๋ฐฐ์จ, ๋ชจ์๋ผ๋ฉด header ์ ์ฅ ๋ฒํผ์ด ๋ค์ ์ค๋ก ๋ด๋ ค๊ฐ๋ค; ํค๋ณด๋ยทsafe area๋ host | `screenPatternRecipe.headerMinWidth` |
|
|
113
114
|
|
|
@@ -119,6 +120,8 @@ import { TextField } from "@hjmds/react-native/inputs";
|
|
|
119
120
|
- ์ ์ฅ ์์ ๋๋ `cancel.disabled`/`cancel.pending` ์ ํ์ ์ด๋ฏธ ์ด๋ฆฐ ์ด์ ๋ฒ๋ฆฌ๊ธฐ ํ์ธ๋ ์ข
๋ฃํ๋ค. ์ ๊ธ์ด ํ๋ ธ๋ค๊ณ ํ์ธ์ฐฝ์ด ๋ค์ ์ด๋ฆฌ์ง๋ ์๋๋ค.
|
|
120
121
|
- ์ต์ด ์กฐํยท์ค๋ฅยท๊ถํ ์ ํ ๋ฑ ๋ณธ๋ฌธ ๋์ฒด ์ํ์์๋ ์ ์ฅ์ ์ ๊ทผ๋ค. ์ ๋ฌธ์์ ๋น ์ด์์ `state="empty"`๊ฐ ์๋๋ผ `ready`์ ๋น ์
๋ ฅ์ผ๋ก ํํํ๊ณ ์ ํ validation์ `submit.disabled`์ ์ฐ๊ฒฐํ๋ค.
|
|
121
122
|
- ๋ผ์ฐํฐ ๋ค๋ก๊ฐ๊ธฐยทAndroid backยท๋ธ๋ผ์ฐ์ ์ดํ์ ์ด ์ปดํฌ๋ํธ๊ฐ ๋ง์ง ์๋๋ค. ์ ํ์ด `dirty`๋ก guard๋ฅผ ๊ฑด๋ค.
|
|
123
|
+
- ๋จ๊ณํ ์์ฑ์ ํค๋ ๋ค๋ก๋ `header`์ TopBar๋ฅผ ๋ฃ์ด ๋ง๋ค์ง ์๊ณ `stepBack`์ ์ด๋ค. ์ง๋ ๋จ๊ณ๋ฅผ ๋๋ฌ ๋์๊ฐ๋ ํ์๋ [Steps](steps.md) `onStepPress`๋ค([๋จ๊ณํ ์์ฑ](../screens/flow-editor.md#๋จ๊ณํ-์์ฑ1310)).
|
|
124
|
+
- ์ฒซ ๋จ๊ณ์์ ์ดํํ๋ ๊ธธ์ `cancel`์ด๋ค. `stepBack`์ผ๋ก ํ๋ฉด์ ๋ ๋๊ฒ ๋ง๋ค๋ฉด `discard` ํ์ธ์ ๊ฑด๋๋ด๋ค.
|
|
122
125
|
- ์ ์ฅ ์ฑ๊ณต ํ์ ยท์ค๋ฅ ํ์๋ ์ ํ ์ํ(`notice`, Toast ๋ฑ)๋ก ํ๋ค.
|
|
123
126
|
|
|
124
127
|
## ํ๋ซํผ ์ฐจ์ด
|
|
@@ -130,4 +133,5 @@ import { TextField } from "@hjmds/react-native/inputs";
|
|
|
130
133
|
|
|
131
134
|
## ๊ทผ๊ฑฐ
|
|
132
135
|
|
|
136
|
+
- `stepBack`(1.31.0): 2026-10-18 spint ์์ฑ(์ โ ์ด๋ชจ์ง โ ๊ธ)์ ํค๋ ์์ ์ชฝ์ด ์ ๋จ๊ณ๋ก ๊ฐ์ผ ํ๋๋ฐ EditorScreen์ ร ๋ซ๊ธฐ๋ง ์๊ณ `exit`๋ฅผ ๋ฐ์ง ์์, ์ ํ์ด `header`์ TopBar๋ฅผ ๋ฃ์ด ๋จธ๋ฆฌ๋ฅผ ํต์งธ๋ก ๋์ฒดํ๋ค.
|
|
133
137
|
- ์ ์ฅ ์์ ์ ์ด๋ฆฐ ๋ฒ๋ฆฌ๊ธฐ ํ์ธ ์ข
๋ฃ: 2026-10-13 ์ด์ ํ์ธ์ฐฝ์ผ๋ก ์ ์ฅ ์ค ์ดํ ์ ๊ธ์ ์ฐํํ ์ ์๋ ๊ฒฝ๋ก๋ฅผ ์์ ํ๋ค.
|
|
@@ -81,6 +81,19 @@ import { Field } from "@hjmds/react-native/forms";
|
|
|
81
81
|
- ์ํ ํ
๋๋ฆฌ๋ recipe๊ฐ ์ ํ๋ค: ๊ธฐ๋ณธ `borderControl`, ํฌ์ปค์ค `contentBrand`, ์ค๋ฅ `danger`.
|
|
82
82
|
- ๋์๋ง์ ์ค๋ฅ๊ฐ ์๊ฒจ๋ ์ฌ๋ผ์ง์ง ์๊ณ ๋ ๋ค ๋ณด์ธ๋ค(Web์ `aria-describedby`๋ก ๋ ๋ค ์ฐ๊ฒฐ).
|
|
83
83
|
- ๋นํ์ฑ(`disabled`)์ ๋ผ๋ฒจ๊ณผ ์ปจํธ๋กค๋ง ํ๋ฆฌ๊ณ ๋์๋งยท์ค๋ฅ๋ ์๋ ๋๋น ๊ทธ๋๋ก ๋๋ค(`fieldRecipe.disabledScope`).
|
|
84
|
+
|
|
85
|
+
### ์ค๋ฅ ์๋ฆผ
|
|
86
|
+
|
|
87
|
+
`error`๊ฐ ๋ํ๋๊ฑฐ๋ ๋ฐ๋๋ฉด ์ฆ์(assertive) ํ ๋ฒ ์๋ฆฐ๋ค. FieldยทTextFieldยทTextAreaยทSelectยทComboboxยทNumberFieldยท
|
|
88
|
+
CheckboxGroupยทRadioGroup๊ณผ ๊ทธ ์์ ๋ง๋ ์ปจํธ๋กค(MessageComposer ๋ฑ)์ด ๊ฐ์ ๊ณ์ฝ์ ์ด๋ค(1.30.0 ์ดํ).
|
|
89
|
+
|
|
90
|
+
- Web: ์ค๋ฅ ์ ์ ๋ฏธ๋ฆฌ mount๋ `role="alert"` ์์ญ ํ๋๊ฐ ์ค๋ฅ id๋ฅผ ๊ฐ๊ณ , ์ปจํธ๋กค์ `aria-describedby`๊ฐ ๊ทธ ์์ญ์ ๊ฐ๋ฆฌํจ๋ค.
|
|
91
|
+
๋ณด์ด๋ ์ค๋ฅ ๋ฌธ๊ตฌ๋ `aria-hidden`์ด๋ผ ํ์ ๋ชจ๋์์๋ ํ ๋ฒ๋ง ์ฝํ๋ค. ์์ญ์ ์๊ฐ์ ์ผ๋ก ์จ๊ฒจ์ ธ ์นธ ๊ฐ๊ฒฉ์ ๋ฐ๊พธ์ง ์๋๋ค.
|
|
92
|
+
- Native: ์ค๋ฅ ๋ฌธ๊ตฌ๊ฐ assertive live region(Android)์ด๊ณ , live region์ ์ฝ์ง ์๋ iOS๋ ์ ์ค๋ฅ๋ง๋ค
|
|
93
|
+
`announceForAccessibility`๋ก ํ ๋ฒ ์๋ฆฐ๋ค.
|
|
94
|
+
- ์ ํ์ ๊ฐ์ ๋ฌธ๊ตฌ๋ฅผ Notice๋ ๋ณ๋ `role="alert"`๋ก ๋ค์ ๊ทธ๋ฆฌ์ง ์๋๋ค.
|
|
95
|
+
|
|
96
|
+
๊ทผ๊ฑฐ: 2026-10-18 ๋ฒ๋ DM์์ Web ํ๋ ์ค๋ฅ๊ฐ describedby๋ก๋ง ์ฐ๊ฒฐ๋ผ ๋ํ๋ ๋ ๋ญ๋
๋์ง ์์๊ณ , Native iOS๋ ์์ฑ์ฐฝยทFieldGroup ์ธ ํ๋ ์ค๋ฅ๋ฅผ ์๋ฆฌ์ง ์์๋ค. ์์ฑ์ฐฝ์๋ง ์จ๊ธด alert๋ฅผ ๋ํ ์ฒซ ์์ ์ ๊ฐ์ ๋ฌธ๊ตฌ๋ฅผ ๋ ๋ฒ ์ฝ๊ฒ ํด ๊ณต์ฉ ๊ณ์ฝ(`react/src/internal/field-error.tsx`, `react-native/src/internal/field-frame.tsx`)์ผ๋ก ์ฎ๊ฒผ๋ค.
|
|
84
97
|
ํ๋ฆผ ์ ๋๋ ์ปดํฌ๋ํธ recipe์ `states.disabledOpacity`๊ฐ ์์ผ๋ฉด ๊ทธ ๊ฐ(SearchFieldยทPasswordFieldยทOtpFieldยทNumberFieldยทSelect
|
|
85
98
|
0.5), ์์ผ๋ฉด `fieldRecipe.disabledOpacity` 0.6(TextFieldยทTextAreaยทMentionsยทFieldยทNativeSelectยทComboboxยทDatePickerยทTagsInput)์ด๋ค.
|
|
86
99
|
2026-10-06 ์ ์๋ ํ ์ ์ฒด๊ฐ ํ๋ ค์ ธ ์ ๊ธด ์ด์ ๋ฅผ ์ ์ ๋์๋ง๊น์ง ๋๋น๊ฐ ๋จ์ด์ก๋ค.
|
|
@@ -39,6 +39,7 @@ DialogยทSheet footer ํ๋, ๋ชฉ๋ก ๋ "๋ ๋ณด๊ธฐ", ์จ๋ณด๋ฉ ๋ค์ยท์ด์ ,
|
|
|
39
39
|
| --- | --- | --- | --- |
|
|
40
40
|
| `IconButton` | ๊ธฐ๋ณธ | `@hjmds/react`, `/actions` | `@hjmds/react-native`, `/actions`, `/bottom-cta` |
|
|
41
41
|
| `NotificationBell` | ํ์ฅ โ ๋ฏธํ์ธ ์ ๋ฐฐ์ง + ์ฆ๊ฐ ์ ํ ๋ฒ ํ๋ค๋ฆผ | `/notification-bell` | `/notification-bell` |
|
|
42
|
+
| `ClipboardIconButton` | ๋๋ฐ(๋ณต์ฌ) โ ์์ด์ฝ๋ง ์๋ ๋ณต์ฌ ๋๊ตฌ ํ๋. `value`, `labels: { idle; copied }`(๋ ์ํ์ ์ ๊ทผ์ฑ ์ด๋ฆ), `onCopy?`, `onCopyError?`, `feedbackDuration?`(ms), `tone` ๊ธฐ๋ณธ `ghost` | `@hjmds/react`, `/clipboard` | โ |
|
|
42
43
|
|
|
43
44
|
`NotificationBell`์ granular subpath๋ก๋ง import ๋๋ค. ์ถ๊ฐ peer๋ ์๋ค.
|
|
44
45
|
|
|
@@ -54,6 +54,9 @@ import { IconTile } from "@hjmds/react-native/screens";
|
|
|
54
54
|
| overlay | ReactNode | ์์ | ์ ํ ๋ฐฐ์ง |
|
|
55
55
|
| labelLines | 1 ๋๋ 2 | 1 | ์ ๋ชฉ ์ค ์ |
|
|
56
56
|
| ghost | boolean | false | ์ด๋ ๋์ ํ์ |
|
|
57
|
+
| frame | `"surface"` ยท `"none"` | `"surface"` | ๋ฏธ๊ฒ์(1.30.0 ์ดํ). `surface`๋ ๊ณตํต ํ(`surfaceAlt` ์ฑ์ + ํ
๋๋ฆฌ 1), `none`์ ํ์ ๊ทธ๋ฆฌ์ง ์๋๋ค. ์์ด์ฝ ์๋ณธ์ด ์ด๋ฏธ ์์ฑ๋ ๋ฐ์ฒ ์์ด์ฝ(๋ชจ์ยท๋ฐฐ๊ฒฝ ํฌํจ)์ผ ๋ `none` |
|
|
58
|
+
| shape | `"squircle"` ยท `"square"` ยท `"circle"` | `"squircle"` | ๋ฏธ๊ฒ์. ํ(๊ณผ ghost ์ ์ )์ ๋ชจ์๋ฆฌ. ํฌ๊ธฐ ๋๋น ๋น์จ `iconTileRecipe.shapes`(squircle 0.22 ยท square 0 ยท circle 0.5). ์ ํ ์์ด์ฝ ํ
๋ง(์์ดํฐ ์
ธ squircle, WindowsยทLinux ์
ธ square)๋ฅผ ๋ฐ๋ฅธ๋ค |
|
|
59
|
+
| size | `"fixed"` ยท `"fill"` | `"fixed"` | ๋ฏธ๊ฒ์. `fixed`๋ 64ร64, `fill`์ ๊ฒฉ์ ์นธ ํญ์ ์ฑ์ฐ๋ `fillMinSize` 48 ~ `fillMaxSize` 68 ์ฌ์ด๋ก ์ ํํ ์ ์ฌ๊ฐํ. ์ด ์ยท์นธ ํญ์ host Grid๊ฐ ์ ํ๊ณ ์ ํ์ด ์นธ ํฌ๊ธฐ๋ฅผ ๊ณ์ฐํด ๋๊ธฐ์ง ์๋๋ค |
|
|
57
60
|
|
|
58
61
|
`icon: ReactNode`, `label: string`, `overlay?: ReactNode`, `labelLines?: 1 | 2`(๊ธฐ๋ณธ1), `ghost?: boolean`(๊ธฐ๋ณธfalse). ๋ ์ค์ ๋ฐ์ฒ ์ ๋ชฉ ๋์ด44๋ฅผ ์์ฝํ๊ณ ghost๋ ์ด๋ ๋์์ ์ ์ /๋ฐํฌ๋ช
ํํ์ด๋ค. ์์ด์ฝ 64, ์ ๋ชฉ ์ต๋ ํญ72, ๊ฐ๊ฒฉ6์ ๊ณต์ recipe๊ฐ ์ ํ๋ค. overlay๋ ์์ด์ฝ ๊ธฐ์ค ๋ฐฐ์ง๋ฅผ ์ํ ์ฌ๋กฏ์ด๋ฉฐ ๋์นจ์ ์๋ฅด์ง ์๋๋ค.
|
|
59
62
|
|
|
@@ -61,7 +64,7 @@ import { IconTile } from "@hjmds/react-native/screens";
|
|
|
61
64
|
|
|
62
65
|
| ํญ๋ชฉ | ๊ฐ | ๊ทผ๊ฑฐ |
|
|
63
66
|
| --- | --- | --- |
|
|
64
|
-
| ํฌ๊ธฐ | ์์ด์ฝ 64ร64(radius 22%), ์ ๋ชฉ ์ต๋ ํญ 72 ยท ๋ ์ค์ด๋ฉด 84์ ์ต์ ๋์ด 44 | `iconTileRecipe` |
|
|
67
|
+
| ํฌ๊ธฐ | ์์ด์ฝ 64ร64(radius 22%, `shape`์ ๋ฐ๋ผ 0%ยท50%), `size="fill"`์ด๋ฉด ์นธ ํญ 48~68 ์ ์ฌ๊ฐํ; ์ ๋ชฉ ์ต๋ ํญ 72 ยท ๋ ์ค์ด๋ฉด 84์ ์ต์ ๋์ด 44 | `iconTileRecipe` |
|
|
65
68
|
| ๊ฐ๊ฒฉ | ์์ด์ฝ๊ณผ ์ ๋ชฉ ์ฌ์ด 6 | `iconTileRecipe.gap` |
|
|
66
69
|
| ์์ยท์ ๋ ฌ | ์์ด์ฝ(overlay ํฌํจ) ์ โ ์ ๋ชฉ ์๋, ๊ฐ๋ก ๊ฐ์ด๋ฐ, ์ ๋ชฉ ๊ฐ์ด๋ฐ ์ ๋ ฌ | ์ renderer |
|
|
67
70
|
| ๊ณ ์ ยท์คํฌ๋กค | Grid๋ ๊ฐ์ ๋ชฉ๋ก์ host๊ฐ ์ด ์ยท์ธ๋ถ ์ฌ๋ฐฑยท์คํฌ๋กค์ ์ ํ๋ค | ๋ณ๋ scroller ์์ |
|
|
@@ -73,6 +76,8 @@ import { IconTile } from "@hjmds/react-native/screens";
|
|
|
73
76
|
|
|
74
77
|
์์ด์ฝ ์๋ณธ๊ณผ ์ ํ ๋ฐฐ์ง๋ง ์ ๋ฌํ๋ค. ๊ฐ์ ๋ฐฐ์น๋ฅผ ๋ณ๋ View/div๋ก ๋ค์ ๋ง๋ค์ง ์๋๋ค.
|
|
75
78
|
|
|
79
|
+
- ์์ฑ๋ ์์ด์ฝ ์ํธ์ํฌ ์์ ํ์ด ๋ ๊ฒน์ผ๋ก ๊ทธ๋ ค์ง๋ฉด `frame="none"`, ์์ด์ฝ ๋ชจ์์ด ํ
๋ง๋ง๋ค ๋ค๋ฅด๋ฉด `shape`, ์นธ ํฌ๊ธฐ๋ฅผ ๋ฐ๋ผ ์ปค์ ธ์ผ ํ๋ฉด `size="fill"`์ ์ด๋ค. ์ด ์ด์ ๋ก ๋ฐ์ฒ ํญ๋ชฉ์ ์ง์ ๋ง๋ค์ง ์๋๋ค.
|
|
80
|
+
|
|
76
81
|
## ์ํ์ ์ํธ์์ฉ
|
|
77
82
|
|
|
78
83
|
์ด ๊ตฌ์ฑ์ ํ์ ์ ์ฉ์ด๋ค. ๋งํฌ/๋ฒํผ/๋๋๊ทธ host๊ฐ disabledยทํฌ์ปค์คยทํค๋ณด๋ยท๊ธธ๊ฒ ๋๋ฅด๊ธฐ์ ๋ผ์ฐํ
์ ์์ ํ๋ฏ๋ก ๊ธฐ์กด ์ฑ์ drag engine์ ๊ต์ฒดํ์ง ์๋๋ค.
|
|
@@ -88,3 +93,7 @@ host์ ์ ์ฒด ํ๋์ ํ์งํ ์ด๋ฆ์ ์ ๊ณตํ๋ค. ์์ด์ฝ ๋ด๋ถ ์ฅ
|
|
|
88
93
|
## ๊ด๋ จ ์ง์นจ
|
|
89
94
|
|
|
90
95
|
[์ ํ ๊ธฐ์ค](../SELECTION.md), [Grid](grid.md), [Card](card.md).
|
|
96
|
+
|
|
97
|
+
## ๊ทผ๊ฑฐ
|
|
98
|
+
|
|
99
|
+
- `frame`ยท`shape`ยท`size`: 2026-10-18 HJM 1.30.0 ๊ฐ์ฌ. ์ ํธ๋ฒ์ค ๋ฐ์ฒ๋ ๋๊ตฌ ์ํธ์ํฌ๊ฐ ์์ฑ๋ ์์ด์ฝ(์
ธ์ ๋ฐ๋ผ iPhone squircle, WindowsยทLinux square)์ด๊ณ ํฌ๊ธฐ๊ฐ ๊ฒฉ์๋ฅผ ๋ฐ๋ผ 48~68๋ก ๋ณํด, ๊ณ ์ 64 ํ์ ๊ทธ๋ฆฌ๋ IconTile ๋์ ์์ฒด ํ์ผ์ ์ ์งํ๋ค. ๋ฒ์ 48~68์ ๊ทธ ๋ฐ์ฒ์ ์ค์ ์นธ ๊ณ์ฐ๊ฐ์ด๋ค.
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
## ์ธ์ ์ฐ๋
|
|
12
12
|
|
|
13
|
-
HJM semantic ์ด๋ฆ(`search`, `back`, `chevronEnd`, `notifications` ๋ฑ
|
|
14
|
-
ํฌ๊ธฐยท์ยท์ ๊ตต๊ธฐยทRTL ๋ฐ์ ์ HJM์ด ์ ํ๊ณ ,
|
|
13
|
+
HJM semantic ์ด๋ฆ(`search`, `back`, `chevronEnd`, `notifications` ๋ฑ 47๊ฐ)์ผ๋ก ๊ณ ๋ฅด๋ ๊ทธ๋ฆผ ๊ธฐํธ์ ์ด๋ค.
|
|
14
|
+
ํฌ๊ธฐยท์ยท์ ๊ตต๊ธฐยทRTL ๋ฐ์ ์ HJM์ด ์ ํ๊ณ , ๊ทธ๋ฆผ์ ๊ณ์ฝ์ ๊ณต์ฉ ๊ฒฝ๋ก(Web ๋ด์ฅ, Native `/icon-svg`)๋ ์ ํ์ด ๋๊ธด glyph๊ฐ ๊ทธ๋ฆฐ๋ค.
|
|
15
15
|
|
|
16
16
|
## ์ฐ์ง ์์ ๋
|
|
17
17
|
|
|
@@ -29,14 +29,17 @@ HJM semantic ์ด๋ฆ(`search`, `back`, `chevronEnd`, `notifications` ๋ฑ 43๊ฐ)
|
|
|
29
29
|
| --- | --- | --- | --- |
|
|
30
30
|
| `Icon` | ๊ธฐ๋ณธ | `@hjmds/react`, `/display` | `@hjmds/react-native`, `/primitives` |
|
|
31
31
|
| `createLucideGlyph` | ๋ณด์กฐ โ Lucide glyph ์ฐ๊ฒฐ helper(์ปดํฌ๋ํธ ์๋) | `/icon-lucide` | `/icon-lucide` |
|
|
32
|
+
| `renderSemanticGlyph` | ๋ณด์กฐ โ HJM ๊ณต์ฉ ๊ฒฝ๋ก๋ฅผ ๊ทธ๋ฆฌ๋ `renderGlyph`(์ปดํฌ๋ํธ ์๋, 1.31.0) | โ (๋ด์ฅ) | `/icon-svg` |
|
|
33
|
+
| `semanticIconPaths` ยท `isSemanticIconName` | ๊ณต์ฉ 24 ๊ฒฉ์ ๊ฒฝ๋ก ๋ฐ์ดํฐ์ ์ด๋ฆ ํ๋ณ | `@hjmds/design-contracts/components/icon` | ๊ฐ์ |
|
|
32
34
|
|
|
33
35
|
`/icon-lucide`๋ optional peer๊ฐ ํ์ํ๋ค. Web `lucide-react` 1.49.0,
|
|
34
|
-
Native `lucide-react-native` 1.49.0.
|
|
36
|
+
Native `lucide-react-native` 1.49.0. Native `/icon-svg`๋ optional peer `react-native-svg` 15.15.5๊ฐ ํ์ํ๋ค.
|
|
37
|
+
๋ ๋ค ๋ฃจํธ entry์์๋ ๋์ค์ง ์๋๋ค.
|
|
35
38
|
|
|
36
39
|
## ์ต์ ์ฌ์ฉ ์
|
|
37
40
|
|
|
38
41
|
```tsx
|
|
39
|
-
// Web โ
|
|
42
|
+
// Web โ built-in glyph
|
|
40
43
|
import { Icon } from "@hjmds/react/display";
|
|
41
44
|
|
|
42
45
|
<Icon name="search" />
|
|
@@ -54,6 +57,42 @@ const renderGlyph = createLucideGlyph({ search: Search, back: ArrowLeft });
|
|
|
54
57
|
<Icon descriptor={{ name: "search" }} renderGlyph={renderGlyph} />
|
|
55
58
|
```
|
|
56
59
|
|
|
60
|
+
```tsx
|
|
61
|
+
// Native โ Web๊ณผ ๊ฐ์ HJM ๊ณต์ฉ ๊ทธ๋ฆผ(react-native-svg ํ์)
|
|
62
|
+
import { renderSemanticGlyph } from "@hjmds/react-native/icon-svg";
|
|
63
|
+
|
|
64
|
+
<Icon descriptor={{ name: "arrowUp" }} renderGlyph={renderSemanticGlyph} />
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### ๊ณต์ฉ ๊ทธ๋ฆผ์ Native ์ ๊ณต ๋ฐฉ์(1.31.0 ์ถ๊ฐ)
|
|
68
|
+
|
|
69
|
+
2026-10-18 utilverse๊ฐ ์ฌ์ง ์์ ๋ณ๊ฒฝยทํ์ IconButton์ ์ํด `arrowUp`ยท`arrowDown`ยท`rotate` ๊ฒฝ๋ก๋ฅผ ์ฑ adapter์ ์ง์
|
|
70
|
+
๊ทธ๋ ธ๋ค. Web๋ง HJM ๊ฒฝ๋ก๋ฅผ ๋ด์ฅํ๊ณ ๊ทธ ๊ฒฝ๋ก๊ฐ renderer ๋ด๋ถ์ ์์ด Native๋ ๊ฐ์ ๊ทธ๋ฆผ์ ๋ฐ์ ์ ์์๊ธฐ ๋๋ฌธ์ด๋ค.
|
|
71
|
+
๊ฒฝ๋ก๋ ์ธํ ๊ฐ์ด๋ฏ๋ก ๊ณ์ฝ `semanticIconPaths`๋ก ์ฎ๊ฒจ ๋ ํ๋ซํผ์ด ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๋๋ค.
|
|
72
|
+
|
|
73
|
+
Native Icon์ `renderGlyph`๋ฅผ ํ์๋ก ์ ์งํ๋ค. core ์ปจํธ๋กค์ด `react-native-svg` ์์ด ๋์ํ๋๋ก SVG๋ optional peer์ด๊ณ ,
|
|
74
|
+
๋ด์ฅ ์ปจํธ๋กค์ ๋ช ๊ธฐํธ(์
ฐ๋ธ๋ฐยท๋ฌ๋ ฅยท์ญ์ ๋ฑ)๋ ๋ด๋ถ์์ View๋ก ๊ทธ๋ฆฐ๋ค. ๊ทธ๋์ ๊ณต์ฉ ๊ทธ๋ฆผ์ `/icon-lucide`์ ๊ฐ์
|
|
75
|
+
์ ํ entry `/icon-svg`์ `renderSemanticGlyph`๋ก ์ฐ๋ค. ์ฑ ๊ณ ์ ์ด๋ฆ์ ํจ๊ป ์ฐ๋ฉด ํ๋ณ ํ ๋๊ธด๋ค.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
// Native โ app glyph names mixed with HJM names
|
|
79
|
+
import { isSemanticIconName } from "@hjmds/design-contracts/components/icon";
|
|
80
|
+
|
|
81
|
+
const renderGlyph = (props: NativeIconRenderProps<AppGlyphName>) =>
|
|
82
|
+
isSemanticIconName(props.name) ? renderSemanticGlyph(props) : renderAppGlyph(props);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`react-native-svg`๋ฅผ ์ค์นํ์ง ์๋ ์ฑ์ ์๊ธฐ adapter์์ `semanticIconPaths[name]`์ ์ฝ์ด ๊ฐ์ ๊ฒฝ๋ก๋ฅผ ๊ทธ๋ฆฐ๋ค.
|
|
86
|
+
|
|
87
|
+
### ์ด๋ฆ ๊ณ ๋ฅด๊ธฐ
|
|
88
|
+
|
|
89
|
+
| ๋ชฉ์ | ์ด๋ฆ | ๋น๊ณ |
|
|
90
|
+
| --- | --- | --- |
|
|
91
|
+
| ์์๋ฅผ ์/์๋๋ก ์ฎ๊ธฐ๊ธฐ | `arrowUp` ยท `arrowDown` | 1.31.0 ์ถ๊ฐ. ์์๋๋ ๋ฌผ๋ฆฌ ๋ฐฉํฅ์ด๋ผ RTL์์ ๋ฐ์ ํ์ง ์๋๋ค |
|
|
92
|
+
| ์ด๋ฏธ์งยทํ์ด์ง 90ยฐ ํ์ | `rotate` | 1.31.0 ์ถ๊ฐ. ์๊ณ ๋ฐฉํฅ. RTL์์ ๋ฐ์ ํ์ง ์๋๋ค. ๋ค์ ๋ถ๋ฌ์ค๊ธฐ๋ `refresh` |
|
|
93
|
+
| ์ฐจ๋จยท์ ๊ธ ํด์ | `unblock` | 1.31.0 ์ถ๊ฐ. `lock`์ ๊ณ ๋ฆฌ๊ฐ ์ด๋ฆฐ ๋ชจ์ |
|
|
94
|
+
| ๋น๋ฐ๋ฒํธ ๋ณด๊ธฐ/์จ๊ธฐ๊ธฐ(๋/๊ฐ๋ฆฐ ๋) | `visibility` ยท `visibilityOff` | ์ด๋ฏธ ์๋ ์ด๋ฆ์ด๋ค. `eye`๋ผ๋ ์ด๋ฆ์ ์๋ค. ์จ๊น ์ํ์์ `visibility`, ๋ณด์ ์ํ์์ `visibilityOff`(๋๋ฅด๋ฉด ๋ ์ํ). ๋น๋ฐ๋ฒํธ ์
๋ ฅ ์์ฒด๋ ์ด ์ง์ ์ด๋ฏธ ๋ด์ฅํ [PasswordField](password-field.md)๋ฅผ ์ด๋ค |
|
|
95
|
+
|
|
57
96
|
## ์ถ๊ณผ ๊ธฐ๋ณธ๊ฐ
|
|
58
97
|
|
|
59
98
|
| prop | ๊ฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช
|
|
|
@@ -93,7 +132,7 @@ const renderGlyph = createLucideGlyph({ search: Search, back: ArrowLeft });
|
|
|
93
132
|
| ํญ๋ชฉ | Web | Native |
|
|
94
133
|
| --- | --- | --- |
|
|
95
134
|
| API ๋ชจ์ | ํํํ prop(`name`, `size`, `tone` โฆ) | `descriptor` ๊ฐ์ฒด ํ๋ |
|
|
96
|
-
| glyph | ๋ด์ฅ SVG
|
|
135
|
+
| glyph | ๋ด์ฅ SVG ๊ฒฝ๋ก(`semanticIconPaths`), `renderGlyph`๋ ์ ํ | `renderGlyph` ํ์. ๊ณต์ฉ ๊ฒฝ๋ก๋ `/icon-svg`์ `renderSemanticGlyph` |
|
|
97
136
|
| ์ฌ์ฉ์ ์ ์ ์ด๋ฆ | `SemanticIconName`๋ง | `Icon<Name>` ์ ๋ค๋ฆญ์ผ๋ก ์ ํ ์ด๋ฆ ํ์ฉ |
|
|
98
137
|
| ์ถ๋ ฅ | `<svg>`(์ฅ์์ด๋ฉด `aria-hidden`) | `View` ํ๋ ์(์ฅ์์ด๋ฉด `accessible={false}`) |
|
|
99
138
|
| ๋ฐฐ์น | `layoutStyle` | `layoutStyle`. `style`์ ์ ๊ฑฐ๋ ์์ฑ โ `layoutStyle` ๋๋ descriptor `size`ยท`tone` |
|
|
@@ -102,4 +141,4 @@ const renderGlyph = createLucideGlyph({ search: Search, back: ArrowLeft });
|
|
|
102
141
|
|
|
103
142
|
- ์์ด์ฝ์ ์๊ธฐ `tone` ์์ ์ง์ ์น ํ๋ค. Web CSS๊ฐ `data-tone`์ผ๋ก ์์ ์ง์ ํ๋ฏ๋ก ๋ถ๋ชจ ์์ ์์ํ์ง ์๋๋ค.
|
|
104
143
|
primaryยทdanger IconButton ์์์ ๊ธฐ๋ณธ tone(`secondary`)์ ์ฐ๋ฉด ์ฑ์์ง ๋ฉด ์์ ํ์์ด ๋๋ค. ์ด๋๋ `tone="inverse"`.
|
|
105
|
-
- `createLucideGlyph` ๋งต์ ์๋ ์ด๋ฆ์ ๊ทธ๋ฆฌ๋ฉด ์คํ ์ค `TypeError`๊ฐ ๋๋ค.
|
|
144
|
+
- `createLucideGlyph` ๋งต์ ์๋ ์ด๋ฆ์ ๊ทธ๋ฆฌ๋ฉด ์คํ ์ค `TypeError`๊ฐ ๋๋ค. `renderSemanticGlyph`๋ HJM ์ด๋ฆ์ด ์๋๋ฉด `TypeError`๋ฅผ ๋ธ๋ค.
|
|
@@ -52,6 +52,7 @@ import { ImageChoiceGroup } from "@hjmds/react-native/image-choice";
|
|
|
52
52
|
| --- | --- | --- | --- |
|
|
53
53
|
| `label` | `string` | ํ์ | ๋ฌถ์์ ์ ๊ทผ์ฑ ์ด๋ฆ |
|
|
54
54
|
| `showLabel` | `boolean` | `false` | ํ์ผ ์์ `label`์ ๋ณด์ด๋ ๊ธ์๋ก ๋๋ค |
|
|
55
|
+
| `showItemLabels` | `boolean` | `false` | ๋ฏธ๊ฒ์. ํ์ผ๋ง๋ค ํญ๋ชฉ `label`์ ๊ทธ๋ฆผ ์๋ ํ ์ค(caption, ๋์น๋ฉด ๋ง์ค์)๋ก ๋ณด์ธ๋ค. ๊ฐ์ ๋๋ฆ ์์ญ ์์ ์๊ณ ๋ณด์กฐ๊ธฐ์ ์ ๊ธฐ์กด์ฒ๋ผ ํ์ผ ์ด๋ฆ ํ๋๋ง ์ฝ๋๋ค. ์ฌ๋์ ์ด๋ฆ์ผ๋ก ์์๋ด์ผ ํ๋ ๋ฐ๋ ์ฌ๋ ์ ํ์ ์ด๋ค |
|
|
55
56
|
| `items` | `{ id, label, image, disabled? }[]` | ํ์ | id ์ค๋ณตยท๋น ์ด๋ฆ์ด๋ฉด `TypeError`. `image`๋ ์นธ์ ์ฑ์ฐ๋ ๊ทธ๋ฆผ ๋
ธ๋ |
|
|
56
57
|
| `selectionMode` | `"single"` ยท `"multiple"` | ํ์ | ๋จ์ผ์ radio, ๋ค์ค์ checkbox๋ก ์ฝํ๋ค |
|
|
57
58
|
| `value`ยท`onValueChange` | ๋จ์ผ `string \| null` / ๋ค์ค `string[]` | ํ์ | ์ ์ดํ. ๋ค์ค์ ๋๋ฅธ ์์๋๋ก ์ถ๊ฐยท์ ๊ฑฐ |
|
|
@@ -62,7 +63,7 @@ import { ImageChoiceGroup } from "@hjmds/react-native/image-choice";
|
|
|
62
63
|
| ํญ๋ชฉ | ๊ฐ | ๊ทผ๊ฑฐ |
|
|
63
64
|
| --- | --- | --- |
|
|
64
65
|
| ํฌ๊ธฐ | ๊ทธ๋ฆผ 72 ์ ์ฌ๊ฐ, ๋ฐ๊นฅ ๊ณ ๋ฆฌ 2 + ๊ฐ๊ฒฉ 2(ํฐ์น ์์ญ 80), radius `radius.md` 12, ์ฒดํฌ ์ 20 | `imageChoiceRecipe` |
|
|
65
|
-
| ๊ฐ๊ฒฉ | ํ์ผ ์ฌ์ด `spacing.xs` 8, ๋ผ๋ฒจโํ์ผ 8 | `imageChoiceRecipe.gap` |
|
|
66
|
+
| ๊ฐ๊ฒฉ | ํ์ผ ์ฌ์ด `spacing.xs` 8, ๋ผ๋ฒจโํ์ผ 8, ๊ทธ๋ฆผโํญ๋ชฉ ๋ผ๋ฒจ `spacing.xxs` 4 | `imageChoiceRecipe.gap`, `itemLabelGap` |
|
|
66
67
|
| ์์ยท์ ๋ ฌ | `items` ์์๋๋ก ์์ ์ชฝ๋ถํฐ ์ค๋ฐ๊ฟ ๋ฐฐ์น, ์ฒดํฌ๋ ํ์ผ ๋ ์ชฝ ์ ๋ชจ์๋ฆฌ | ๋ ๋ ๋๋ฌ |
|
|
67
68
|
| ๊ณ ์ ยท์คํฌ๋กค | ์์ฒด ์คํฌ๋กค ์์. ๋ฐ๊นฅ ๋ชฉ๋กยทํ๋ฉด์ด ์คํฌ๋กค | ๋ ๋ ๋๋ฌ |
|
|
68
69
|
| ์ข์ ํญยทํฐ ๊ธ์ | ๋จ์ ํญ์์ ์ค๋ฐ๊ฟ, ํ์ผ ํฌ๊ธฐ๋ ์ ์ง | flex wrap |
|
|
@@ -78,4 +79,5 @@ import { ImageChoiceGroup } from "@hjmds/react-native/image-choice";
|
|
|
78
79
|
|
|
79
80
|
## ๊ทผ๊ฑฐ
|
|
80
81
|
|
|
82
|
+
- `showItemLabels`: 2026-10-18 ๋ฒ๋ ๊ณต์ ๋ฐ๋ ์ฌ๋ ์ ํ์ด ์ด๋ฆ์ ๋ณด์ด๋ ค๊ณ ๋งํฌ ๋ฒํผ์ ์๋ฐํยท์ฒดํฌยท์ด๋ฆ์ ์ง์ ์กฐ๋ฆฝํ๋ค.
|
|
81
83
|
- ๋์
๊ณ๊ธฐ: 2026-10-16 ๊ตฌ์ฑ ์์ ์ ๊ฒ โ ์ ํ๋ค์ด `selected`๋ฅผ ์ค ghost ๋ฒํผ์ผ๋ก ๊ทธ๋ฆผ ์ ํ์ ํ๋ด ๋ด ์ ํ ์ํ๊ฐ ์ผ๊ด๋์ง ์์๋ค.
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
| ์ด๋ฆ | ์ญํ | Web | Native |
|
|
31
31
|
| --- | --- | --- | --- |
|
|
32
32
|
| `MediaSelectionScreen` | ๋ฏธ๋์ด ์ ํ ๊ฒํ ํ๋ฉด | `/screen-flows` | `/screen-flows` |
|
|
33
|
+
| `MediaSelectionCell` | ํ๋ฉด ๊ฒฉ์๊ฐ ๊ทธ๋ฆฌ๋ ํญ๋ชฉ ์นธ(๋ฏธ๋ฆฌ๋ณด๊ธฐ โ UploadItem โ ์๋กยท์๋๋กยทํ์ ยท์ญ์ ). ๊ฐ์ํ ๋ชฉ๋ก์์ ๊ฐ์ ์นธ์ ๊ทธ๋ฆด ๋ ์ด๋ค. ๋ฏธ๊ฒ์ | `/screen-flows` | `/screen-flows` |
|
|
33
34
|
|
|
34
35
|
granular subpath๋ก๋ง import ๋๋ค(๋ฃจํธ entry์ ์์). ์ถ๊ฐ peer๋ ์๋ค.
|
|
35
36
|
|
|
@@ -96,6 +97,7 @@ Web์ `<img alt="">`๋ ์ฅ์์ฉ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ค. ์ฌ์ง ์ด๋ฆ์ `removeLabe
|
|
|
96
97
|
| `removeLabel` ยท `moveUpLabel` ยท `moveDownLabel` | `(item: MediaSelectionItem) => string` | ํ์ | ์ฌ์ง ์ด๋ฆ์ ํฌํจํ ์ ๊ทผ์ฑ ์ด๋ฆ |
|
|
97
98
|
| `onMove` | `(id: string, direction: -1 \| 1) => void` | ํ์ | ์ฒซ ํญ๋ชฉ์ ์๋กยท๋ง์ง๋ง ํญ๋ชฉ์ ์๋๋ก๋ ๋นํ์ฑ |
|
|
98
99
|
| `onRemove` ยท `onRetry` ยท `onCancel` | `(id: string) => void` | ํ์ | ์ญ์ , UploadItem ์ฌ์๋ยท์ทจ์ |
|
|
100
|
+
| `rotate` | `{ label, itemLabel(item), onRotate(id) }` | ์์ | ๋ฏธ๊ฒ์. ์ฃผ๋ฉด ์๋๋ก์ ์ญ์ ์ฌ์ด์ ํ์ ๋ฒํผ(`label` ํ์ ๋ฌธ๊ตฌ, `itemLabel` ์ฌ์ง ์ด๋ฆ์ ํฌํจํ ์ ๊ทผ์ฑ ์ด๋ฆ). ํ์ ๊ฐ ์ ์ฅ๊ณผ `preview`ยท๊ฒฐ๊ณผ๋ฌผ ๋ฐ์์ ์ ํ์ด ํ๋ค |
|
|
99
101
|
| ๋๋จธ์ง | `ScreenLayout`๊ณผ ๊ฐ์(`children`ยท`footer` ์ ์ธ) | โ | `actions`๋ฅผ ์ฃผ๋ฉด `add` ์์ ๊ฐ์ ํ์ผ๋ก ๋์ธ๋ค |
|
|
100
102
|
|
|
101
103
|
## ๋ฐฐ์น
|
|
@@ -108,6 +110,21 @@ Web์ `<img alt="">`๋ ์ฅ์์ฉ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ค. ์ฌ์ง ์ด๋ฆ์ `removeLabe
|
|
|
108
110
|
| ๊ณ ์ ยท์คํฌ๋กค | ํค๋ยทfooter ๊ณ ์ , ๊ฒฉ์๋ ๋ณธ๋ฌธ ์คํฌ๋กค(`scroll` ๊ธฐ๋ณธ `"screen"`) | `ScreenLayout` |
|
|
109
111
|
| ์ข์ ํญ | ์ข์ ํญ์์๋ 2์ด ์ ์ง; ํญ๋ชฉ ๋ฒํผ ์ค์ ์ค๋ฐ๊ฟ(`flexWrap: "wrap"`)ํด ๋ฒํผ์ ์๋ฅด์ง ์๋๋ค | `screen-flows.tsx` |
|
|
110
112
|
|
|
113
|
+
## ๊ฐ์ํ ๋ชฉ๋ก์ ํญ๋ชฉ ์นธ
|
|
114
|
+
|
|
115
|
+
์ฌ์ง์ด ๋ง์ ์ ํ์ด `VirtualList`ยท`FlatList`๋ก ๋ชฉ๋ก์ ์ง์ ๊ทธ๋ฆด ๋๋ ๊ฐ์ ์นธ `MediaSelectionCell`์ `renderItem`์์ ๊ทธ๋ฆฐ๋ค. ๋ฏธ๋ฆฌ๋ณด๊ธฐ์ ํ๋ ์ค์ ์ ํ์ด ๋ค์ ์กฐ๋ฆฝํ์ง ์๋๋ค(2026-10-18 Utilverse ์ฌ์งโPDFยท์ฌ์ง ๋ ๋๊ตฌ๊ฐ ์ธ๋ค์ผยท์์ยทํ์ ยท์ญ์ ์นธ์ ์ง์ ๋ง๋ค์๋ค).
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
// Native
|
|
119
|
+
import { MediaSelectionCell } from "@hjmds/react-native/screen-flows";
|
|
120
|
+
|
|
121
|
+
<FlatList data={photos} keyExtractor={photo => photo.id} renderItem={({ item, index }) =>
|
|
122
|
+
<MediaSelectionCell item={toMediaItem(item)} index={index} count={photos.length} {...itemActions}
|
|
123
|
+
rotate={{ label: t("photo.rotate"), itemLabel: entry => t("photo.rotateNamed", { name: entry.descriptor.name }), onRotate: rotatePhoto }} />} />
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`index`ยท`count`๋ ์ ์ฒด ๋ชฉ๋ก ๊ธฐ์ค์ด๋ค(์ฒซ ํญ๋ชฉ ์๋กยท๋ง์ง๋ง ํญ๋ชฉ ์๋๋ก ๋นํ์ฑ). `itemActions`๋ ํ๋ฉด๊ณผ ๊ฐ์ `labels`ยท`actionLabels`ยท`removeLabel`ยท`moveUpLabel`ยท`moveDownLabel`ยท`onRemove`ยท`onMove`ยท`onRetry`ยท`onCancel`์ด๋ค. ์นธ ์ฌ์ด ๊ฐ๊ฒฉยท์ด ์๋ host๊ฐ ์ค๋ค. ์ญ์ ํ Web ํค๋ณด๋ ์ด์ ๋ณต๊ตฌ๋ ํ๋ฉด ๊ฒฉ์์ ๋์์ด๋ฏ๋ก ๊ฐ์ํ host๋ ์๊ธฐ ์ด์ ์ ์์ ํ๋ค.
|
|
127
|
+
|
|
111
128
|
## ๊ผญ ์งํฌ ๊ฒ
|
|
112
129
|
|
|
113
130
|
- `actionLabels`๋ ์งง์ ํ์ ๋ฌธ๊ตฌ, `removeLabel`ยท`moveUpLabel`ยท`moveDownLabel`์ ์ฌ์ง ์ด๋ฆ์ ํฌํจํ ์ ๊ทผ์ฑ ์ด๋ฆ์ด๋ค.
|
|
@@ -76,7 +76,7 @@ import { Image } from "react-native";
|
|
|
76
76
|
| `value` | `string` | ํ์ | ์์ ์ ์ดํ. HJM์ ์ด์์ ์ง์ฐ์ง ์๋๋ค |
|
|
77
77
|
| `label` | `string` | ํ์ | ์ ๊ทผ์ฑ ์ด๋ฆ; placeholder ์๋ต ์ ๊ฐ์ ๋ฌธ๊ตฌ |
|
|
78
78
|
| `placeholder` | `string` | `label` | ์งง์ ์๊ฐ ์๋ด์ ์ ์ฒด ์ ๊ทผ์ฑ ์ด๋ฆ์ ๋ถ๋ฆฌ |
|
|
79
|
-
| `description` ยท `error` ยท `invalid` | `string` ยท `string` ยท `boolean` | ์์ | TextArea์ ์๋ดยท์ค๋ฅ ์ฐ๊ฒฐ. Web describedby/invalid, Native hint/์ค๋ฅ
|
|
79
|
+
| `description` ยท `error` ยท `invalid` | `string` ยท `string` ยท `boolean` | ์์ | TextArea์ ์๋ดยท์ค๋ฅ ์ฐ๊ฒฐ. Web describedby/invalid, Native hint/์ค๋ฅ ํ๋ฉด. `error`๋ ๋ํ๋๊ฑฐ๋ ๋ฐ๋ ๋ ์ฆ์(assertive) ์๋ฆฐ๋ค(1.30.0 ์ดํ ์์ ). ์์ฑ์ฐฝ ์ ์ฉ ์๋ฆผ์ ์๊ณ TextArea์ ๊ณต์ฉ Field ์ค๋ฅ ๊ณ์ฝ([Field](field.md#์ค๋ฅ-์๋ฆผ))์ ๊ทธ๋๋ก ์ด๋ค |
|
|
80
80
|
| `onBlur` | `() => void` | ์์ | host์ ์
๋ ฅ ์ค ์ํ ํด์ |
|
|
81
81
|
| `submitMode` | `"newline"` ยท `"send"` | `"newline"` | Web Enter ์ ์ก์ Shift/IME/229 ๋ณดํธ. Native send๋ submit, newline์ ๊ธฐ์กด ์ค๋ฐ๊ฟ |
|
|
82
82
|
| `sendLabel` | `string` | ํ์ | ์ ์ก ๋ฒํผ ๋ฌธ๊ตฌยท์ ๊ทผ์ฑ ์ด๋ฆ |
|
|
@@ -123,6 +123,7 @@ import { Image } from "react-native";
|
|
|
123
123
|
- ์
๋ ฅ ์ฐ๊ฒฐ(blur/IME/๊ฒ์ฆ)์ด ๋ ํ์ํ๋ฉด ์ ํ์ด ๋ด๋ถ TextArea๋ฅผ ๋ฐ๋ก ์กฐ๋ฆฝํ์ง ์๊ณ ๊ธฐ์กด MessageComposer ์์ง์ ๊ณต๊ฐ ์ถ์ ํ์ฅํ๋ค.
|
|
124
124
|
- ๋ก์ปฌ ์ค๋น ์ฌ์ง์ `attachments`๋ก ํ์ํ๋ฉฐ UploadItem์ ์๋ฒ ์
๋ก๋ ์๋ฃ๋ก ๊ฐ์ฅํ์ง ์๋๋ค. ์ค๋น ์ค ํด๋น ํญ๋ชฉ์ `disabled`์ attachmentAction.disabled๋ฅผ ์ฐ๊ฒฐํด ํธ์ง ๊ฐ๋ฅํ ๋ณธ๋ฌธ๊ณผ ์ฌ์ง ์ ๊ฑฐ๋ฅผ ๋ถ๋ฆฌํ๋ค. ์๋ฒ ์ฑ๊ณต ์ ์ด์์ ์ง์ฐ์ง ์๋๋ค.
|
|
125
125
|
- Web ๋ฐฐ์น๋ `layoutStyle`๋ก ํ๋ค. Native๋ ์คํ์ผ ํต๋ก๊ฐ ์์ด ๊ฐ์ธ๋ ๋ ์ด์์์์ ๋ฐฐ์นํ๋ค.
|
|
126
|
+
- ์ ์ก ์คํจ๋ `error`๋ก ๋๊ธด๋ค. ์๋ฆผ์ ์ํด ๊ฐ์ ๋ฌธ๊ตฌ๋ฅผ ํ๋ฉด `notice`์ Notice๋ `context`์ `role="alert"` ๋ฌธ๊ตฌ๋ก ํ ๋ฒ ๋ ๊ทธ๋ฆฌ์ง ์๋๋ค.
|
|
126
127
|
|
|
127
128
|
## ํ๋ซํผ ์ฐจ์ด
|
|
128
129
|
|
|
@@ -140,6 +141,8 @@ Native ๋ด์ฅ ์ญ์ ยท๋ฉ๋ด ๊ธฐํธ๋ ๊ณ ์ ์์ด์ฝ ํ์ ํฌ๊ธฐ๋ฅผ ์ ์ง
|
|
|
140
141
|
|
|
141
142
|
## ๊ทผ๊ฑฐ
|
|
142
143
|
|
|
144
|
+
- `error` ์ฆ์ ์๋ฆผ: 2026-10-18 ๋ฒ๋ DM. ์์ฑ์ฐฝ `error`๊ฐ ์
๋ ฅ ์๋์ ๋ณด์ด๊ธฐ๋ง ํ๊ณ ๋ญ๋
๋์ง ์์(Web ์๋ฆผ ์์, iOS live region ๋ฏธ์ง์) ์ ์ก ์คํจ๋ฅผ ํ๋ฉด notice์ Notice๋ก ๋ฐ๋ก ์๋ ธ๋ค. ์ฒซ ์์ ์ ์์ฑ์ฐฝ์๋ง ์จ๊ธด alert๋ฅผ ๋ํด Web ํ์ ๋ชจ๋์์ ๊ฐ์ ๋ฌธ๊ตฌ๋ฅผ ๋ ๋ฒ ์ฝ์์ผ๋ฏ๋ก, ๊ฐ์ ๋ ๋ชจ๋ Field ์ปจํธ๋กค์ ๊ณต์ฉ ์ค๋ฅ ๊ณ์ฝ์ผ๋ก ์ฎ๊ฒผ๋ค.
|
|
145
|
+
|
|
143
146
|
- ๋ต์ฅ ์ค ์นด๋ ๋ฐฐ์น: 2026-10-16 ์นด์นด์คํก ์ค์ธก.
|
|
144
147
|
- `submitMode` ๋ฑ ์
๋ ฅ ๊ณต๊ฐ ์ถ: 2026-10-07 ๋ฒ๋ ์ฑํ์์ ๊ธฐ์กด blur/IME/๊ฒ์ฆ ์ฐ๊ฒฐ์ด ๋๋ฝ๋๋ค.
|
|
145
148
|
- Native ๋ด์ฅ ๊ธฐํธ ๊ณ ์ ํฌ๊ธฐ: 2026-10-06 ์ต๊ทผ ๊ฒ์ ์ญ์ ๊ธฐํธ๊ฐ ํฐ ๊ธ์์์ ์๋ฆฌ๋ ๊ฒ์ ์ฌํํ๋ค.
|