@hjmds/design-contracts 1.46.1 → 1.48.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 +2 -1
- package/dist/behaviors.d.ts.map +1 -1
- package/dist/bottom-navigation.d.ts +4 -0
- package/dist/bottom-navigation.d.ts.map +1 -1
- package/dist/bottom-navigation.js +12 -0
- package/dist/bottom-navigation.js.map +1 -1
- package/dist/card.d.ts +2 -2
- package/dist/card.d.ts.map +1 -1
- package/dist/card.js +3 -2
- package/dist/card.js.map +1 -1
- package/dist/carousel.d.ts +1 -0
- package/dist/carousel.d.ts.map +1 -1
- package/dist/carousel.js +1 -0
- package/dist/carousel.js.map +1 -1
- package/dist/catalog.d.ts +151 -31
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +14 -13
- package/dist/catalog.js.map +1 -1
- package/dist/color-picker.d.ts +51 -1
- package/dist/color-picker.d.ts.map +1 -1
- package/dist/color-picker.js +56 -2
- package/dist/color-picker.js.map +1 -1
- package/dist/component-recipes.d.ts +28 -10
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +28 -20
- package/dist/component-recipes.js.map +1 -1
- package/dist/conversation-row.d.ts +1 -1
- package/dist/conversation-row.d.ts.map +1 -1
- package/dist/conversation-row.js +2 -1
- package/dist/conversation-row.js.map +1 -1
- package/dist/data-table.d.ts +12 -0
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +2 -0
- package/dist/data-table.js.map +1 -1
- package/dist/date-range.d.ts +122 -3
- package/dist/date-range.d.ts.map +1 -1
- package/dist/date-range.js +139 -5
- package/dist/date-range.js.map +1 -1
- package/dist/icon.d.ts +2 -1
- package/dist/icon.d.ts.map +1 -1
- package/dist/icon.js +2 -0
- package/dist/icon.js.map +1 -1
- package/dist/recipes.d.ts +1 -0
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +1 -0
- package/dist/recipes.js.map +1 -1
- package/dist/screen-patterns.d.ts +33 -2
- package/dist/screen-patterns.d.ts.map +1 -1
- package/dist/screen-patterns.js +27 -2
- package/dist/screen-patterns.js.map +1 -1
- package/dist/share-sheet.d.ts +115 -0
- package/dist/share-sheet.d.ts.map +1 -0
- package/dist/share-sheet.js +62 -0
- package/dist/share-sheet.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/bottom-navigation.md +16 -0
- package/docs/color-picker.md +24 -19
- package/docs/dialog.md +2 -3
- package/docs/generated/component-maturity.md +12 -12
- package/docs/generated/renderer-evidence.json +71 -19
- package/docs/generated/renderer-evidence.md +15 -14
- package/docs/generated/showcase-manifest.json +35 -21
- package/docs/optional-adapters.md +21 -5
- package/docs/stable-core.md +37 -0
- package/docs/usage/README.md +14 -10
- package/docs/usage/SELECTION.md +109 -0
- package/docs/usage/components/avatar.md +31 -6
- package/docs/usage/components/badge.md +10 -1
- package/docs/usage/components/bottom-navigation.md +16 -0
- package/docs/usage/components/button.md +11 -0
- package/docs/usage/components/card.md +6 -0
- package/docs/usage/components/chat-message.md +9 -1
- package/docs/usage/components/chat-room-members.md +4 -0
- package/docs/usage/components/chat-screen.md +10 -1
- package/docs/usage/components/color-picker.md +65 -28
- package/docs/usage/components/comment-thread-screen.md +16 -1
- package/docs/usage/components/conversation-list-screen.md +81 -0
- package/docs/usage/components/conversation-row.md +5 -1
- package/docs/usage/components/data-table.md +2 -0
- package/docs/usage/components/date-range-picker.md +15 -5
- package/docs/usage/components/dialog.md +3 -3
- package/docs/usage/components/event-attendance-card.md +4 -0
- package/docs/usage/components/follow-list-screen.md +109 -0
- package/docs/usage/components/follow-profile-card.md +4 -0
- package/docs/usage/components/list-row.md +6 -0
- package/docs/usage/components/menu.md +12 -0
- package/docs/usage/components/notice.md +8 -3
- package/docs/usage/components/notification-inbox-screen.md +10 -0
- package/docs/usage/components/notification-row.md +4 -0
- package/docs/usage/components/page-indicator.md +1 -0
- package/docs/usage/components/profile-summary.md +32 -3
- package/docs/usage/components/section.md +2 -2
- package/docs/usage/components/settings-screen.md +1 -1
- package/docs/usage/components/share-sheet.md +139 -0
- package/docs/usage/components/shared-transition-element.md +1 -1
- package/docs/usage/components/sheet.md +1 -0
- package/docs/usage/components/statistic.md +1 -1
- package/docs/usage/compositions/interaction-adapters.md +2 -2
- package/docs/usage/compositions/optional-adapters.md +2 -2
- package/docs/usage/compositions/web-additions.md +5 -5
- package/docs/usage/screens/common-comments.md +1 -0
- package/docs/usage/screens/common-conversation-list.md +22 -1
- package/docs/usage/screens/common-follow-list.md +87 -0
- package/docs/usage/screens/common-settings.md +23 -0
- package/package.json +7 -1
|
@@ -88,6 +88,10 @@ import { Notice } from "@hjmds/react-native/feedback";
|
|
|
88
88
|
|
|
89
89
|
## 배치
|
|
90
90
|
|
|
91
|
+
2026-10-25 개편: 다에리 오프라인 오류에서 붉은 면·붉은 테두리·붉은 제목이 겹치고, 재시도 버튼이 문구 아래 따로 놓여
|
|
92
|
+
오류 한 줄이 화면에서 가장 시끄러운 블록이 됐다(사용자 지적). 테두리를 없애고 tone은 면 tint·앞쪽 막대·아이콘에만 둔다.
|
|
93
|
+
제목은 본문색으로 읽히고, 행동은 Native도 Web처럼 같은 줄 끝에 둔다.
|
|
94
|
+
|
|
91
95
|
2026-10-20 선택의창 지도 위에서 Native 안내 배경의 alpha가 지도 지명·도로를 그대로 비춰 문구와 겹쳤다.
|
|
92
96
|
`panel`은 불투명한 테마 바탕 위에 tone tint를 합성한다(Web의 기존 color-mix와 같은 방식).
|
|
93
97
|
지도·사진 위에서도 아래 콘텐츠가 비치지 않는다. `status`는 기존처럼 배경 없이 유지한다.
|
|
@@ -95,11 +99,12 @@ import { Notice } from "@hjmds/react-native/feedback";
|
|
|
95
99
|
|
|
96
100
|
| 항목 | 값 | 근거 |
|
|
97
101
|
| --- | --- | --- |
|
|
98
|
-
| 크기 | 본문 폭을 꽉 채운다. 아이콘 20(`glyph.sm`), 테두리
|
|
102
|
+
| 크기 | 본문 폭을 꽉 채운다. 아이콘 20(`glyph.sm`), 테두리 없음, 앞쪽 tone 막대 4(`accentBar`), radius `radius.md` 12 | `noticeRecipe` |
|
|
103
|
+
| 색 | 면은 tone tint(info는 중립 `surface.sunken`), 막대·아이콘은 tone 색, 제목은 `content.primary`, 설명은 `content.body`. 제목을 tone 색으로 칠하지 않는다 | `noticeRecipe.tones`·`title.color` |
|
|
99
104
|
| 간격 | 안쪽 `spacing.md` 16, 아이콘·문구·행동 사이 `spacing.xs` 8, 제목·설명 사이 `spacing.xxs` 4. 이웃 블록과는 `layout.contentGap` 16 | `noticeRecipe`, `.hjm-notice*` |
|
|
100
|
-
| 순서·정렬 | 아이콘 — 제목·설명 — 행동. 행동은 하나, `Button` `size="small"` `tone="secondary"`(또는 `ghost`), `primary` 금지.
|
|
105
|
+
| 순서·정렬 | 아이콘 — 제목·설명 — 행동. 행동은 하나, `Button` `size="small"` `tone="secondary"`(또는 `ghost`), `primary` 금지. 두 플랫폼 모두 행동은 문구와 같은 줄 끝이며, 제목·설명 묶음과 행동의 세로 중심을 행 중앙에 맞춘다. 첫 줄에 버튼 높이 기반 여백을 더하지 않는다. Native 아이콘 칸은 제목 첫 줄 높이(토큰×글자 배율) | `.hjm-notice`·`.hjm-notice__icon`·`.hjm-notice__action`, `react-native/src/feedback.tsx` |
|
|
101
106
|
| 고정·스크롤 | 본문 흐름 안. 먼저 읽어야 하는 경고·입력 오류는 영향을 받는 내용 바로 위(화면 상태는 제목 아래, 폼 검증은 제출 버튼 위). 제출 후 성공·실패 결과는 구성 지침이 지정한 행동 인접 위치에 둔다. 떠 있거나 고정되지 않고 함께 스크롤되며 여러 개 쌓지 않는다 | — |
|
|
102
|
-
| 좁은 폭·큰 글자 |
|
|
107
|
+
| 좁은 폭·큰 글자 | 문구 칸이 60%(`contentMinWidthPercent`) 아래로 줄면 행동이 다음 줄로 감긴다. 제목·설명은 줄바꿈된다 | `.hjm-notice__content`(`flex: 1 1 60%`), Native `flexBasis` |
|
|
103
108
|
|
|
104
109
|
## 꼭 지킬 것
|
|
105
110
|
|
|
@@ -95,3 +95,13 @@ import { NotificationInboxScreen } from "@hjmds/react-native/screens";
|
|
|
95
95
|
| --- | --- | --- |
|
|
96
96
|
| 배치·식별 | `layoutStyle`, `className`, `as` | `layoutStyle`, `testID` |
|
|
97
97
|
| 스크롤 | 없음 | `scrollRef`, `scrollProps`(`refreshControl` 등) |
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
## 행 종류와 여백 — 개발 소스, 미게시 (2026-10-25)
|
|
101
|
+
|
|
102
|
+
`rowPresentation="item"`(기본)은 NotificationItem/ListRow용으로 화면이 여백을 소유한다.
|
|
103
|
+
아바타·썸네일을 가진 NotificationRow에는 `rowPresentation="preview"`를 사용한다.
|
|
104
|
+
이때 공통 화면이 body-edge를 선택하며 제품은 contentInset을 별도로 조정하지 않는다.
|
|
105
|
+
같은 알림함에 서로 다른 행 종류가 있어 기본값을 일괄 변경하지 않는다.
|
|
106
|
+
필터·notice가 모두 없으면 빈 notice 영역을 만들지 않는다.
|
|
107
|
+
번뚝의 화면 여백과 행 여백 중복 및 빈 notice 간격을 공통 합성에서 예방하기 위한 변경이다.
|
|
@@ -91,3 +91,7 @@ HJM이 44px 아바타·썸네일, 20px 유형 배지, 2줄 미리보기와 간
|
|
|
91
91
|
## 관련 지침
|
|
92
92
|
|
|
93
93
|
[알림 화면](../screens/common-inbox.md), [ConversationRow](conversation-row.md), [ListRow](list-row.md).
|
|
94
|
+
|
|
95
|
+
### 회원 프로필 이동 (2026-10-25)
|
|
96
|
+
|
|
97
|
+
`avatarAction={label,onPress}`는 발신자 프로필, `action`은 알림 목적지다. 두 컨트롤은 형제로 렌더링되며 프로필 누르기로 알림 읽음 처리를 실행하지 않는다.
|
|
@@ -67,6 +67,7 @@ import { PageIndicator } from "@hjmds/react-native/carousel";
|
|
|
67
67
|
| `count` | 1 이상 정수 | 필수 | 페이지 수. 밖이면 `RangeError` |
|
|
68
68
|
| `maxVisibleDots` | 1 이상 정수 | 전체 | 현재 페이지를 포함하는 연속 창만 표시. 이름·이동은 전체 페이지 기준이며 창 크기는 페이지 수를 넘지 않는다(checkout 미게시) |
|
|
69
69
|
| `interactive` | `boolean` | `true` | `false`이면 위치만 표시하며 점은 누름 대상/키보드/조절 행동을 만들지 않는다. 별도 이전·다음 컨트롤이 있는 작은 표시용(checkout 미게시) |
|
|
70
|
+
| `density` | `comfortable / compact` | `comfortable` | compact는 점 중심 간격과 가로 누름 영역을 24로 줄이고 세로 44는 유지한다. 좁은 상단 표시용 개발 API |
|
|
70
71
|
| `currentIndex` | 0부터 `count - 1` | 필수 | 페이저가 확정한 현재 페이지. 컴포넌트는 따로 기억하지 않는다 |
|
|
71
72
|
| `onCurrentIndexChange` | `(index: number) => void` | 필수 | 점 누름·Web 화살표 키·Native 조절 동작이 요청한 페이지. 처음·끝에서는 같은 값이 다시 올 수 있다(넘어가지 않고 멈춘다) |
|
|
72
73
|
| `composeAccessibleName` | `({ position, total }) => string` | 필수 | 점 하나의 이름. 숫자 문법은 제품 문구다. 빈 문자열은 `TypeError` |
|
|
@@ -45,12 +45,13 @@ import { ProfileSummary } from '@hjmds/react-native/screen-flows';
|
|
|
45
45
|
| prop | 값 | 기본값 | 설명 |
|
|
46
46
|
| --- | --- | --- | --- |
|
|
47
47
|
| name | string | 필수 | 제목(이름) |
|
|
48
|
-
| avatar | ReactNode | 필수 | 원형
|
|
48
|
+
| avatar | ReactNode | 필수 | 원형 48 프레임 안에 넣는 아바타 |
|
|
49
49
|
| headingLevel | 1~6 | 2 | 이름 제목 수준 |
|
|
50
50
|
| badge | ReactNode | 없음 | 이름 옆 배지 |
|
|
51
51
|
| nameActions | ReactNode | 없음 | 이름 행의 행동 |
|
|
52
|
+
| nameEdit | `{label,onAction,disabled?,pending?}` | 없음 | 이름 옆 편집(연필) 아이콘 버튼. 내 프로필에서 닉네임 바꾸기를 연다(2026-10-25, 아래 절) |
|
|
52
53
|
| stats | `{id,value,label,onPress?,disabled?}[]` | `[]` | 통계 수치. `onPress`가 있으면 버튼 |
|
|
53
|
-
| actionsLayout | `inline` · `stacked` | `inline` |
|
|
54
|
+
| actionsLayout | `inline` · `stacked` · `equal` | `inline` | 가로 보조 행동은 inline 줄바꿈 행, 세로 전체 폭은 stacked, 같은 폭의 가로 행동은 equal(개발 소스) |
|
|
54
55
|
| actions | ReactNode | 없음 | 수치 아래 행동 줄. [ProfileScreen](profile-screen.md) `editPlacement="summary"` 안에서는 그 화면의 `edit`이 이 줄 맨 앞에 `secondary` `small` 버튼(`profileSummaryRecipe.editAction`)으로 먼저 들어간다(1.31.0) |
|
|
55
56
|
| children | ReactNode | 없음 | 하단 footer |
|
|
56
57
|
|
|
@@ -60,7 +61,7 @@ import { ProfileSummary } from '@hjmds/react-native/screen-flows';
|
|
|
60
61
|
|
|
61
62
|
| 항목 | 값 | 근거 |
|
|
62
63
|
| --- | --- | --- |
|
|
63
|
-
| 크기 | 원형 아바타 프레임
|
|
64
|
+
| 크기 | 원형 아바타 프레임 48. ProfileScreen 수정 행동은 `Button` `secondary` `small` | `profileSummaryRecipe.avatarSize`·`editAction` |
|
|
64
65
|
| 간격 | 수치 칸 사이 `spacing.md` 16, 값·라벨 사이 `spacing.xxs` 4, 행동 사이 `spacing.xs` 8. 영역 간격은 [CompositionLayout](composition-layout.md) | 양 renderer |
|
|
65
66
|
| 순서·정렬 | 머리(아바타 → 이름·배지 → nameActions) → 수치 줄 → actions → children, 수치 칸은 균등 폭·가운데 | `CompositionLayout` + `flex: 1` 칸 |
|
|
66
67
|
| 고정·스크롤 | 없음. ProfileScreen 본문 스크롤 안에 놓인다 | — |
|
|
@@ -87,3 +88,31 @@ Web의 텍스트/이모지 artwork는 원형72 프레임 안에서 glyph.xxl48
|
|
|
87
88
|
actions={<><Button fullWidth onClick={follow}>{followLabel}</Button><Button fullWidth tone="secondary" onClick={message}>{messageLabel}</Button></>} />
|
|
88
89
|
// Native는 같은 actionsLayout에 Button.onPress를 연결한다.
|
|
89
90
|
```
|
|
91
|
+
|
|
92
|
+
### 같은 폭의 가로 행동 (2026-10-25)
|
|
93
|
+
|
|
94
|
+
번뚝의 Instagram형 프로필 요청에 따라 `actionsLayout="equal"`은 버튼과 메뉴 trigger를 같은 폭의 한 행으로 배치한다. HJM Grid가 열·간격을 소유하며 fragment는 셀로 세지 않는다. 오류 안내는 actions 밖의 children에 공급한다. 팔로우·해제 메뉴·메시지 경로와 문구는 제품이 연결한다. 기존 inline/stacked 동작은 유지한다.
|
|
95
|
+
|
|
96
|
+
### 내 프로필: 사진은 사진 변경, 이름 옆 연필은 닉네임 변경 (2026-10-25)
|
|
97
|
+
|
|
98
|
+
사용자 지시로 모든 앱·웹의 내 프로필 카드에 같은 규칙을 쓴다.
|
|
99
|
+
|
|
100
|
+
- **사진을 누르면 프로필 사진 변경**이 열린다. `avatar`에 `Avatar action={{label: t("photo.change"), onPress}}`를 넣는다(이미지 고르기·자르기·업로드는 제품 소유).
|
|
101
|
+
- **이름 옆에 `nameEdit`(연필 아이콘 버튼)**를 두고 누르면 닉네임 변경이 열린다. 아이콘·크기(`profileSummaryRecipe.nameEdit`, small ghost 36 + hitSlop 4 = 44)·위치(`nameActions` 앞)는 HJM이 소유한다. 연필 그림은 제품 아이콘 세트의 연필을 `nameEdit.icon`으로 넣어 이름 줄의 다른 아이콘(설정 톱니 등)과 같은 선 모양으로 맞춘다. 넣지 않으면 HJM 기본 연필을 그린다.
|
|
102
|
+
- 같은 카드에 "프로필 편집"·"사진 바꾸기"·"닉네임 바꾸기" 같은 별도 버튼을 중복으로 두지 않는다. `ProfileScreen`의 `edit`은 이제 선택이며 손님의 로그인 같은 다른 행동에만 쓴다.
|
|
103
|
+
- 남의 프로필에는 `nameEdit`를 넣지 않고, 사진은 회원 아바타 기준(확대·신고)을 따른다.
|
|
104
|
+
- 제품이 ProfileSummary 대신 고유 머리를 그리는 경우에도 사진 = 사진 변경, 이름 = 닉네임 변경을 지킨다. 이름이 그림(예: Spint 내 정원의 나무 팻말)이면 연필을 따로 두지 않고 그 이름 그림 자체를 닉네임 변경 버튼으로 한다(2026-10-25 사용자 결정).
|
|
105
|
+
|
|
106
|
+
### 프로필 카드 틀 (2026-10-25)
|
|
107
|
+
|
|
108
|
+
같은 날 사용자 지적: Utilverse 프로필 카드(평평한 `default`, radius `lg`)가 BurnTok 카드(`raised` 그림자, 테두리, radius `xl`)와
|
|
109
|
+
달라 보였다. ProfileSummary를 카드에 담는 화면은 `Surface tone="raised" bordered radius="xl" padding="md"`로 감싼다(Web·Native 같은 값).
|
|
110
|
+
제품 컨셉 때문에 카드 대신 고유 머리를 그리는 화면(Spint 내 정원)은 예외로 그 사유를 코드 주석에 남긴다.
|
|
111
|
+
|
|
112
|
+
### 손님 카드의 로그인 (2026-10-25)
|
|
113
|
+
|
|
114
|
+
로그인하지 않은 사용자의 프로필 카드는 로그인이 카드의 주 행동이다. `actionsLayout="stacked"` + `actions`에 전체 폭 primary
|
|
115
|
+
`Button` 하나를 둔다. 이름 옆 작은 버튼(`nameActions`)이나 ProfileScreen `edit` 자리(작은 secondary)를 빌리지 않는다 —
|
|
116
|
+
Utilverse에서 작은 왼쪽 버튼이 카드 안에서 떠 보였다는 사용자 지적으로 정했다. 손님 카드에는 `nameEdit`와 사진 변경을 두지 않는다.
|
|
117
|
+
|
|
118
|
+
2026-10-25 사용자 요청으로 회원 아바타 프레임은 `avatarRecipe.sizes.large`(48px 원형)이다. 댓글·채팅처럼 작은 목록의 크기는 바꾸지 않는다.
|
|
@@ -78,14 +78,14 @@ import { Section } from "@hjmds/react-native/primitives";
|
|
|
78
78
|
| 항목 | 값 | 근거 |
|
|
79
79
|
| --- | --- | --- |
|
|
80
80
|
| 크기 | 자체 배경·테두리·여백이 없다. 폭은 부모를 따른다 | `.hjm-section` |
|
|
81
|
-
| 글자 | 제목 `
|
|
81
|
+
| 글자 | 제목 `bodyLarge` 16/24 500(반복 구획이 본문을 압도하지 않도록 2026-10-25 번뚝 리뷰 반영, 개발 소스·미게시), 설명 `caption` 11/16(두 플랫폼). 2026-10-06까지 Web은 둘 다 본문 14를 물려받았다(1.13.0 변경) | `sectionRecipe.title`·`description`, `.hjm-section__title`·`__description` |
|
|
82
82
|
| 간격 | 머리와 본문 사이 `spacing.xs` 8, 글자 묶음과 행동 사이 `spacing.xs` 8, 제목과 설명 사이 `spacing.xxs` 4. Section끼리의 간격은 Section이 주지 않는다. 감싸는 [Stack](stack.md)이나 `layoutStyle`로 `layout.sectionGap`(`spacing.xl` 24)을 둔다 | `sectionRecipe.gap`·`headerGap`·`copyGap`, `layout.sectionGap` |
|
|
83
83
|
| 순서·정렬 | 머리 행동은 끝(LTR 오른쪽)에 하나, 글자 묶음과 세로 가운데 정렬. 보통 [Link](link.md) 또는 `tone="link"` [Button](button.md) | `.hjm-section__header`, Native `Section` |
|
|
84
84
|
| 고정·스크롤 | 고정되지 않는다. 화면 본문 스크롤 안에 둔다 | — |
|
|
85
85
|
| 좁은 폭·큰 글자 | 폭과 관계없이 한 행을 유지한다. 글자 크기 160% 이상(`largeTextThreshold`)에서만 머리 행이 세로로 쌓이고 행동이 글자 아래로 간다(두 플랫폼 같은 조건). 2026-10-17까지 Web은 폭 600 미만에서도 쌓여, 같은 휴대폰 폭에서 Native는 한 행인데 Web만 행동이 아래 줄로 늘어났다(화면 리뷰 S5#3) | Web `.hjm-root[data-large-text]`, Native `textScale >= 1.6` |
|
|
86
86
|
|
|
87
87
|
```text
|
|
88
|
-
최근 본 장소 [전체 보기] ← 머리: 제목
|
|
88
|
+
최근 본 장소 [전체 보기] ← 머리: 제목 bodyLarge 16, gap spacing.sm 12
|
|
89
89
|
자주 가는 곳이에요 ← 설명: caption 11, gap spacing.xxs 4
|
|
90
90
|
gap spacing.xs 8
|
|
91
91
|
┌──────────── 본문 ────────────┐
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
Web 기본은 `inline`, Native 기본은 `row`이므로 Web·Native 같은 화면에서는 `presentation`을 명시한다.
|
|
73
73
|
- ListRow의 trailing에 Switch만 둘 때는 `labelVisibility="hidden"`을 쓰고 그 ListRow에는 `onPress`를 두지 않는다.
|
|
74
74
|
- 언어·화면 테마·기본 정렬처럼 한 값을 고르는 설정은 [ChoiceRow](choice-row.md)를 쓴다. 행 `description`에 확인된 값을 보이고 시트의 선택·저장 중·실패를 ChoiceRow가 소유한다. `ListRow`+`Sheet`+`RadioGroup`이나 `Select`로 다시 조립하지 않는다(2026-10-23 [선택 기준](choice-row.md#설정-값-선택의-기준)).
|
|
75
|
-
- 섹션 `id`는 안정적인 키로 둔다(번역 문구를 id로 쓰지 않는다).
|
|
75
|
+
- 섹션 `id`는 안정적인 키로 둔다(번역 문구를 id로 쓰지 않는다). 그룹 id·이름·순서는 [설정 그룹 표준](../screens/common-settings.md#설정-그룹-표준-2026-10-25)을 따른다(2026-10-25).
|
|
76
76
|
- 저장 방식·낙관적 갱신·실패 복구·탈퇴 확인은 제품과 [action-session](../../action-session.md)이 소유한다.
|
|
77
77
|
저장 실패는 `state`가 아니라 `notice`로 알린다.
|
|
78
78
|
- 배치: Web·Native 모두 `layoutStyle`(Web은 `className`도 있다). 섹션 색·여백을 덮지 않는다.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# ShareSheet
|
|
2
|
+
|
|
3
|
+
- 단계: 컴포넌트
|
|
4
|
+
- 상태: 실험
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 미게시(1.47.0 이후)
|
|
7
|
+
- 검토일: 2026-10-26
|
|
8
|
+
- 근거: `src/share-sheet.ts`의 `shareSheetRecipe`·`resolveShareSheetSendOutcome`·`filterShareSheetRecipients`; Web `src/share-sheet.tsx`, Native `src/share-sheet.tsx`
|
|
9
|
+
- 스토리북: `실험/컴포넌트/오버레이/공유 시트`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
링크 하나를 **앱 안의 받는 사람에게 보내거나(선택), 링크를 복사하거나, 기기 공유 시트로 내보내는** 공유 시트에 쓴다. 단톡방에 보내기·팔로잉에게 DM 보내기처럼 앱 안 대상이 있으면 `recipients`를 주고, 외부 공유만 있으면 생략한다. Sheet·ImageChoiceGroup·버튼을 제품에서 다시 조립하지 않는다.
|
|
14
|
+
|
|
15
|
+
## 쓰지 않을 때
|
|
16
|
+
|
|
17
|
+
| 상황 | 대신 쓸 것 |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| 링크 없이 값 하나만 복사 | `ClipboardButton`·`ClipboardIconButton`(Web) |
|
|
20
|
+
| 받는 사람을 고른 뒤 대화방을 새로 만들거나 초대하는 흐름 | `ChatRoomCreate`·`ChatRoomInvite` |
|
|
21
|
+
| 사진·파일 원본을 보내기 | 제품 업로드 흐름 + 기기 공유 |
|
|
22
|
+
| 공유 외 여러 행동이 섞인 메뉴 | `Menu` |
|
|
23
|
+
|
|
24
|
+
## 공개 이름과 import
|
|
25
|
+
|
|
26
|
+
| 이름 | 역할 | Web | Native |
|
|
27
|
+
| --- | --- | --- | --- |
|
|
28
|
+
| `ShareSheet` | 기본. 공유 시트 | `@hjmds/react/share-sheet` | `@hjmds/react-native/share-sheet` |
|
|
29
|
+
| `ShareSheetProps`, `ShareSheetRecipients`, `ShareSheetRecipient`, `ShareSheetRecipientSearch` | 보조. 타입 | `@hjmds/react/share-sheet` | `@hjmds/react-native/share-sheet` |
|
|
30
|
+
| `shareSheetRecipe`, `resolveShareSheetSendOutcome`, `filterShareSheetRecipients`, `resolveShareSheetPickerValue`, `validateShareSheet` | 보조. 배치 값·보내기 결과 규칙 | `@hjmds/design-contracts/share-sheet` | 같음 |
|
|
31
|
+
|
|
32
|
+
## 최소 사용 예
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
// Web
|
|
36
|
+
import { ShareSheet } from "@hjmds/react/share-sheet";
|
|
37
|
+
|
|
38
|
+
<ShareSheet open={open} onOpenChange={setOpen} title={t("share.title")} link={url} shareTitle={room.name}
|
|
39
|
+
labels={{ close: t("common.close"), copyLink: t("share.copyLink"), systemShare: t("share.shareTo"), linkCopied: t("share.linkCopied"), actionFailed: t("share.failed") }}
|
|
40
|
+
onLinkCopied={() => track("share", { method: "copy_link" })}
|
|
41
|
+
recipients={{
|
|
42
|
+
items: chats.map(chat => ({ id: chat.id, label: chat.name, image: <Avatar name={chat.name} decorative /> })),
|
|
43
|
+
labels: recipientLabels, status: chatsQuery.isError ? "error" : chatsQuery.isLoading ? "loading" : "ready", onRetry: () => void chatsQuery.refetch(),
|
|
44
|
+
onSend: async ids => sendToChats(ids, url), // { sentIds, error? }
|
|
45
|
+
}} />
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
// Native
|
|
50
|
+
import * as Clipboard from "expo-clipboard";
|
|
51
|
+
import { ShareSheet } from "@hjmds/react-native/share-sheet";
|
|
52
|
+
|
|
53
|
+
<ShareSheet open={open} onOpenChange={setOpen} title={t("share.title")} link={url} shareTitle={drop.title} labels={labels}
|
|
54
|
+
copyLink={async link => { if (!(await Clipboard.setStringAsync(link))) throw new Error("clipboard"); }}
|
|
55
|
+
icons={{ copyLink: <Link size={24} color={colors.text} />, systemShare: <Share size={24} color={colors.text} /> }}
|
|
56
|
+
recipients={{ items: following.map(user => ({ id: user.id, label: user.nickname, image: <UserAvatar user={user} /> })), labels: recipientLabels,
|
|
57
|
+
onSend: async ids => sendDirectMessages(ids) }} />
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 축과 기본값
|
|
61
|
+
|
|
62
|
+
| prop | 값 | 기본값 | 설명 |
|
|
63
|
+
| --- | --- | --- | --- |
|
|
64
|
+
| `open`·`onOpenChange` | `boolean` · `(open: boolean, detail: { reason }) => void` | 필수 | 제어형. 닫히면 선택·보낸 표시·검색어·안내가 처음으로 돌아간다 |
|
|
65
|
+
| `title` | `string` | 필수 | 시트 제목. Native Android 기기 공유 선택창 제목으로도 쓴다 |
|
|
66
|
+
| `labels` | `{ close, copyLink, systemShare, linkCopied, actionFailed }` | 필수 | 지역화 문구. 빈 문자열이면 `TypeError` |
|
|
67
|
+
| `link` | `string` | 필수 | 복사·공유할 URL. 바뀌면 이전 "복사함" 안내를 지운다 |
|
|
68
|
+
| `shareTitle`·`shareText` | `string` | — | 기기 공유 시트에 링크와 함께 넘기는 제목·문구 |
|
|
69
|
+
| `copyLink` | Web `((link: string) => Promise<void>) \| false` · Native `((link: string) => Promise<void>) \| null` | Web `navigator.clipboard.writeText` · Native 필수 | 링크 복사. Web `false`·Native `null`이면 복사 행동을 숨긴다. 거부되면 실패 안내를 보인다 |
|
|
70
|
+
| `systemShare` | Web `((content) => Promise<void>) \| false` · Native `((content) => Promise<"completed" \| "dismissed">) \| false` | Web `navigator.share`(없는 브라우저는 숨김) · Native `Share.share` | 기기 공유. `false`면 숨긴다. iOS 미리보기용 제품 모듈 등으로 바꿀 수 있다. `content`는 `{ url, title?, text? }` |
|
|
71
|
+
| `onLinkCopied` | `() => void` | — | 복사가 끝난 뒤. 분석 기록용 |
|
|
72
|
+
| `onSystemShare` | `(outcome: "completed" \| "dismissed") => void` | — | 기기 공유 시트가 닫힌 뒤. `completed`도 전달 확인이 아니다 |
|
|
73
|
+
| `icons` | Web `{ copyLink?, systemShare? }` · Native `{ copyLink, systemShare }` | Web HJM `copy`·`share` 아이콘 · Native 필수 | 두 원형 행동의 그림 |
|
|
74
|
+
| `recipients` | 아래 표 | — | 앱 안 받는 사람. 없으면 외부 공유만 |
|
|
75
|
+
| `notice` | `ReactNode` | — | 본문 맨 위 제품 안내(보내기 보상 등) |
|
|
76
|
+
|
|
77
|
+
| `recipients` 필드 | 값 | 기본값 | 설명 |
|
|
78
|
+
| --- | --- | --- | --- |
|
|
79
|
+
| `items` | `{ id, label, image, description?, disabled? }[]` | 필수 | 받는 사람. id 중복·빈 이름이면 `TypeError` |
|
|
80
|
+
| `labels` | `{ group, search, clearSearch, loading, loadFailed, retry, empty, noResults, sent, sendFailed, send(count) }` | 필수 | 지역화 문구. `send`는 개수를 받아 버튼 문구를 만든다 |
|
|
81
|
+
| `status` | `"ready"` · `"loading"` · `"error"` | `"ready"` | 목록 상태. `error`면 `onRetry`가 있을 때 다시 시도 버튼을 붙인다 |
|
|
82
|
+
| `onRetry`·`retrying` | `() => void` · `boolean` | — | 불러오기 다시 시도와 그 진행 표시 |
|
|
83
|
+
| `search` | `{ query?, onQueryChange?, searching? }` | — | 생략하면 이름으로 이 안에서 거른다. `onQueryChange`를 주면 제품이 검색어와 결과 `items`를 맡는다(서버 검색) |
|
|
84
|
+
| `sentIds` | `readonly string[]` | `[]` | 시트를 열기 전에 이미 보낸 받는 사람. 체크된 채 잠긴다 |
|
|
85
|
+
| `onSend` | `(ids: readonly string[]) => Promise<{ sentIds; error? }>` | 필수 | 고른 사람에게 보낸다. `sentIds`는 잠기고 나머지는 선택된 채 남는다. `error`가 없으면 `labels.sendFailed`를 보인다. 요청하지 않은 id를 돌려주면 `TypeError` |
|
|
86
|
+
|
|
87
|
+
## 배치
|
|
88
|
+
|
|
89
|
+
| 항목 | 값 | 근거 |
|
|
90
|
+
| --- | --- | --- |
|
|
91
|
+
| 크기 | 받는 사람이 있으면 Sheet `size="large"`(Native는 본문 스크롤), 없으면 `auto`. 원형 행동 IconButton `large`(지름 52) `secondary`, 보내기 Button `large` `primary` 가득 폭 | `shareSheetRecipe.size`·`actionButton`·`sendButton` |
|
|
92
|
+
| 간격 | 본문 영역 사이 `spacing.md` 16, footer의 보내기–행동 줄 `spacing.xs` 8, 두 원형 행동 사이 `spacing.xxxl` 40, 버튼–글자 `spacing.xs` 8 | `shareSheetRecipe.bodyGap`·`footerGap`·`actionGap`·`actionCaptionGap` |
|
|
93
|
+
| 순서·정렬 | 본문: 제품 `notice` → 보내기 오류 → 복사·공유 안내 → 검색 → 목록(또는 불러오는 중·오류·빈 상태). footer: 보내기 버튼(고른 사람이 있을 때만) → 가운데 정렬 [링크 복사][공유하기], 각 버튼 아래 caption 글자 | 두 렌더러 |
|
|
94
|
+
| 고정·스크롤 | footer는 Sheet가 고정하고 본문만 스크롤한다. 받는 사람은 ImageChoiceGroup 타일(이름 2줄) | Sheet footer, `shareSheetRecipe.picker` |
|
|
95
|
+
| 좁은 폭·큰 글자 | 타일은 폭에 맞춰 줄바꿈하고 크기는 유지한다. 두 원형 행동은 한 줄 가운데에 둔다 | ImageChoiceGroup |
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
┌──────────────────────────────┐
|
|
99
|
+
│ ─── 공유 │ ← Sheet 머리(끌어서 닫기)
|
|
100
|
+
├──────────────────────────────┤ ← 본문(스크롤)
|
|
101
|
+
│ [제품 안내] │
|
|
102
|
+
│ [보내기 오류 / 링크 복사 안내] │
|
|
103
|
+
│ [🔍 이름 검색 ×] │
|
|
104
|
+
│ [얼굴][얼굴][얼굴][얼굴] │
|
|
105
|
+
│ 서연 민준 지우 하린 │
|
|
106
|
+
│ 보냄 │ ← 보낸 사람: 체크·잠금·설명 줄
|
|
107
|
+
├──────────────────────────────┤ ← footer(고정)
|
|
108
|
+
│ [ 2명에게 보내기 ] │ ← 고른 사람이 있을 때만
|
|
109
|
+
│ (🔗) (↗) │
|
|
110
|
+
│ 링크 복사 공유하기 │
|
|
111
|
+
└──────────────────────────────┘ ← 안전 영역
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 꼭 지킬 것
|
|
115
|
+
|
|
116
|
+
- 보이는 문구·접근성 이름은 모두 `labels`로 준다. 컴포넌트가 만드는 문구는 없다.
|
|
117
|
+
- 기기 공유 시트는 열렸다 닫힌 것만 안다. 전달됐다고 안내하거나 "보냄"으로 표시하지 않는다. 앱 안 보내기만 `onSend` 결과로 "보냄"을 표시한다.
|
|
118
|
+
- `onSend`는 실제로 전달한 id만 `sentIds`로 돌려준다. 실패한 사람은 선택된 채 남아 다시 누르면 그 사람만 보낸다(이미 보낸 사람에게 두 번 보내지 않는다).
|
|
119
|
+
- 보내는 동안 시트는 사용자가 닫을 수 없고(`busy`) 목록은 잠긴다. 연속 탭은 요청 하나로 묶인다. 로그인 만료처럼 시트를 닫아야 하는 실패는 제품이 `onOpenChange`의 제어 상태를 `false`로 바꾼다.
|
|
120
|
+
- 검색칸에 자동 포커스를 주지 않는다. 하단 시트는 열릴 때 입력에 포커스하지 않으므로 키보드가 목록과 공유 버튼을 가리지 않는다.
|
|
121
|
+
- Native 복사는 제품이 `copyLink`로 준다(`expo-clipboard` 등). HJM Native 핵심은 클립보드 모듈을 설치하지 않는다.
|
|
122
|
+
|
|
123
|
+
## 플랫폼 차이
|
|
124
|
+
|
|
125
|
+
| 항목 | Web | Native |
|
|
126
|
+
| --- | --- | --- |
|
|
127
|
+
| 복사 기본값 | `navigator.clipboard.writeText` | 없음. `copyLink` 필수(`null`이면 숨김) |
|
|
128
|
+
| 기기 공유 기본값 | `navigator.share({ url, title?, text? })`. 없는 브라우저와 서버 렌더에서는 숨김 | `Share.share`. iOS는 링크를 `url`로, Android는 `url`을 버리므로 제목·문구 다음 줄에 링크를 붙인 `message`로 보낸다 |
|
|
129
|
+
| 공유 취소 | `AbortError` 거부 → `onSystemShare("dismissed")`, 실패 안내 없음 | iOS `dismissedAction` → `dismissed`. Android는 항상 `completed` |
|
|
130
|
+
| 교체 함수의 결과 | `Promise<void>`, 취소는 `AbortError`로 거부 | `Promise<"completed" \| "dismissed">` |
|
|
131
|
+
| 아이콘 | 생략하면 HJM `copy`·`share` | `icons` 필수(핵심이 그림을 그리지 않는다) |
|
|
132
|
+
| 안내 알림 | 복사 성공 `status`, 실패 `alert` | 복사 성공 polite, 실패 assertive 알림 |
|
|
133
|
+
|
|
134
|
+
## 근거
|
|
135
|
+
|
|
136
|
+
- 도입: 2026-10-26 사용자 지시 "공유에 대해서 디자인 시스템에 넣고 꺼내서 쓰자 번뚝 공유 그대로 공통화 해". 번뚝 Web·Native 공유 시트(받는 사람 검색·다중 선택·보낸 사람 잠금·부분 실패 재시도·두 원형 외부 행동)를 제품 중립 API로 옮겼다. 오늘갈게 단톡방 보내기처럼 다른 제품도 같은 시트를 쓴다.
|
|
137
|
+
- 기기 공유는 전달을 주장하지 않음: 번뚝 공유가 공유 시트를 연 것만 관측할 수 있어 시도만 기록했다. `onSystemShare`의 `completed`도 같은 뜻이다.
|
|
138
|
+
- 실패한 사람만 선택 유지: 번뚝 부분 실패 재시도에서 이미 보낸 DM이 중복되지 않게 했다. 요청하지 않은 id 보고는 보내지 않은 사람을 "보냄"으로 표시하므로 오류로 둔다.
|
|
139
|
+
- 별도 보조 기능(supplemental): Sheet에 새 상태 축을 더하지 않고 Sheet·ImageChoiceGroup·IconButton·SearchField를 조합한 공유 전용 구성이라 catalog canonical 행이나 Sheet companion으로 두지 않고 자기 지침과 스토리를 가진다(`ChoiceRow`와 같은 방식, `scripts/sync-public-component-map.mjs`).
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
- 지원: Native
|
|
6
6
|
- 적용: 1.12.1
|
|
7
7
|
- 검토일: 2026-10-06
|
|
8
|
-
- 근거: [Optional interaction adapters · Shared screen transition](../../../../../docs/interaction-adapters.md#shared-screen-transition)(Native 전용,
|
|
8
|
+
- 근거: [Optional interaction adapters · Shared screen transition](../../../../../docs/interaction-adapters.md#shared-screen-transition)(Native 전용, 문서화한 host에서 stable, supplemental, 카탈로그 계약 없음), `packages/react-native/src/screen-transition.tsx`, 시연은 구성 스토리 `배포/구성/직접 조작과 모션/끌기·밀기·화면 전환` › 카드 확대와 화면 전환. 예제 스토리는 2026-10-06 사용자 승인으로 스토리북 배포([승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정)). 스토리북 배포는 API 게시가 아니다(`적용` 참고)
|
|
9
9
|
- 스토리북: `배포/구성/직접 조작과 모션/끌기·밀기·화면 전환`
|
|
10
10
|
|
|
11
11
|
## 언제 쓰나
|
|
@@ -245,6 +245,7 @@ import { SheetPanel } from "@hjmds/react-native/sheet-panel";
|
|
|
245
245
|
|
|
246
246
|
- 배치는 손잡이(최소 44) → 본문이며 위 모서리만 `sheetRecipe.content.radius`로 둥글게 한다. 하단은 화면에 붙어 지도색이 새지 않게 각진다. 좌우 바깥 여백은 부모의 대칭 `layoutStyle`로 연결한다.
|
|
247
247
|
- `maximumHeight`는 부모의 지도/탭 영역을 제외한 가용 높이, `height`는 선택 상태 간 유지할 높이다. 본문만 스크롤하며 고정 높이에서 남은 영역을 채운다. 부모가 이미 safe area를 제외한 지도 높이를 주므로 safe area를 다시 더하지 않는다.
|
|
248
|
+
- `safeAreaBottom`(기본 0)은 시트 아래 가장자리를 덮는 다른 요소의 높이다(예: 위로 솟은 하단 탭 버튼). 표면은 그대로 아래 끝까지 닿아 탭바와 한 면으로 이어지고, 본문 아래 여백과 접힌 상태의 보이는 높이에 이 값을 더해 내용과 손잡이가 가려지지 않는다. 음수·무한대는 `RangeError`다. 2026-10-25 다에리 지도 시트가 솟은 메아리 탭과 겹치지 않으려고 탭바 위로 떠서 지도 띠가 보였던 문제에서 추가했다.
|
|
248
249
|
- 접으면 손잡이만 남는다. 손잡이 끌기와 본문 맨 위의 아래 방향 끌기가 같은 snap 정책을 쓴다. 취소되면 시작 위치로 돌아오며 높이가 바뀌어도 접힘 상태를 유지한다. 내용 안에서 시작한 스크롤은 중간에 맨 위에 도달해도 시트를 끌지 않는다.
|
|
249
250
|
- 손잡이를 눌러도 접기/펼치기가 가능하며 `accessibilityState.expanded`가 바뀐다. 접힌 본문은 보조기술에서 숨기고 뒤 지도는 숨기지 않는다. 포커스 격리·backdrop·Android back 가로채기는 없다.
|
|
250
251
|
- 제품의 가로 넘김은 `contentGesture`로 제공한다. 수직 동작/멀티터치/짧은 탭을 가로 넘김이 빼앗지 않도록 제품 gesture의 방향 기준을 지정한다. `handleActions`/`onHandleAction`으로 이전/다음 같은 도메인 접근성 행동을 추가할 수 있다. `overlay`는 제품 그림·소인 슬롯이다.
|
|
@@ -116,7 +116,7 @@ Native 프레임의 모서리는 `statisticRecipe.presentations`의 역할을 Pr
|
|
|
116
116
|
| 항목 | 값 | 근거 |
|
|
117
117
|
| --- | --- | --- |
|
|
118
118
|
| 크기 | `presentation="surface"`의 안쪽 여백: `comfortable` `spacing.md` 16 · `compact` `spacing.sm` 12(두 플랫폼). 테두리 `stroke.default` 1, radius `md` 12 | `statisticRecipe.density`·`presentations`, `styles.css` `.hjm-statistic[data-presentation="surface"]` |
|
|
119
|
-
| 글자 | 라벨 `label` 12 medium(`compact` `caption` 11), 값 `
|
|
119
|
+
| 글자 | 라벨 `label` 12 medium(`compact` `caption` 11), 값 `title` 18 semibold(2026-10-25 번뚝 리뷰: 콘텐츠보다 큰 요약 숫자를 줄임, 개발 소스·미게시)(`fontWeight.semibold`, `compact` `bodyLarge` 16. 2026-10-21 다에리 오늘의 기록 숫자가 크다는 사용자 피드백으로 compact 값을 한 단계 낮춤. 2026-10-17 큰 글자 단계 폐지로 `heading.level2` 굵기 참조를 끊음), prefix·suffix `body` 14 medium 본문색, hint `caption`, 추세 `caption` medium(두 플랫폼). 2026-10-06까지 Web은 라벨 본문 14 medium, 값은 밀도와 관계없이 18, prefix·suffix는 흐린색이었다(1.13.0 변경) | `statisticRecipe.density`·`label`·`value`·`affix`·`hint`·`trend`, `.hjm-statistic__*` |
|
|
120
120
|
| 간격 | 그룹 간격 `statisticRecipe.group.gap` = `spacing.xs` 8. 지표 안 줄 간격(라벨·값·추세) `comfortable` `spacing.xs` 8 · `compact` `spacing.xxs` 4(두 플랫폼) | `styles.css` `.hjm-statistic`·`.hjm-statistic-group`, `statisticRecipe` |
|
|
121
121
|
| 순서·정렬 | 화면 위쪽 요약 영역이나 카드 안에 둔다. 지표 여러 개는 [Grid](grid.md)를 직접 짜지 말고 `StatisticGroup`으로 묶는다 | — |
|
|
122
122
|
| 고정·스크롤 | — | — |
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
순서 바꾸기·행 작업·내용 전환·카드 넘기기·달성 축하·카드 확대 화면 전환 같은 선택형 상호작용 어댑터를 한 화면에서 함께 쓸 때, 각 어댑터를 어디에 놓고 무엇으로 감싸야 하는지 확인하는 구성이다.
|
|
16
16
|
|
|
17
17
|
스토리는 2026-10-06 사용자 승인으로 스토리북 배포됐다. 어댑터 API 자체의 성숙도는 따로다: `sortable`·`swipe-actions`·`content-transition`·`carousel-motion`·`celebration`은
|
|
18
|
-
Web·Native stable, Native `screen-transition
|
|
18
|
+
Web·Native stable, Native `screen-transition`도 명시한 React Navigation host 범위에서 stable이다. 각 어댑터는 root에서 재노출되지 않고 subpath로만 import하며, 제품에 해당 peer를 설치해야 한다.
|
|
19
19
|
|
|
20
20
|
## 구성 요소
|
|
21
21
|
|
|
@@ -182,7 +182,7 @@ const gutter = resolveWindowClass(useWindowDimensions().width) === "compact" ? "
|
|
|
182
182
|
| 행 작업 | 버튼 상시 노출 | 스와이프로 드러냄 + 상시 "작업 보기", `rowId`·`openRowId`·`actionsLabel` 필수 |
|
|
183
183
|
| 카드 크기 | 부모 폭 | 측정한 `width`·`height` 필수 |
|
|
184
184
|
| 축하 위치 | 자체 canvas | 스크롤 밖 화면 오버레이, Skia 필요 |
|
|
185
|
-
| 화면 전환 | 없음(제품 라우터) | `screen-transition`(
|
|
185
|
+
| 화면 전환 | 없음(제품 라우터) | `screen-transition`(stable, React Navigation 7.4.1 + 문서화한 patch) |
|
|
186
186
|
| 제스처 host | — | 화면 루트 `GestureHandlerRootView` |
|
|
187
187
|
|
|
188
188
|
## 함정
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
Native 앱 한 화면에서 이미지 확대 보기, 끌어서 높이를 바꾸는 시트, OS 길게 누르기 메뉴, 키보드를 따라 올라가는 하단 행동을 함께 쓸 때 provider 중첩 순서와 각 요소의 자리를 확인하는 구성이다.
|
|
16
16
|
|
|
17
|
-
네 어댑터(`image-viewer`·`sheet-gesture`·`context-menu-native`·`keyboard-controller`)는
|
|
17
|
+
네 어댑터(`image-viewer`·`sheet-gesture`·`context-menu-native`·`keyboard-controller`)는 문서화한 peer/host 범위에서 stable이고 Native 전용이다. 링크된 네이티브 모듈이 필요하므로
|
|
18
18
|
Expo Go가 아니라 호환 개발 클라이언트에서만 확인된다. 이 기능이 필요 없으면 기본 [Sheet](../components/sheet.md)·[ContextMenu](../components/context-menu.md)·
|
|
19
19
|
[KeyboardAvoiding](../components/keyboard-avoiding.md)을 쓴다.
|
|
20
20
|
|
|
@@ -158,7 +158,7 @@ const gutter = resolveWindowClass(useWindowDimensions().width) === "compact" ? "
|
|
|
158
158
|
|
|
159
159
|
| 항목 | Web | Native |
|
|
160
160
|
| --- | --- | --- |
|
|
161
|
-
| 지원 | 없음(기본 Sheet·ContextMenu 사용) | 네 어댑터
|
|
161
|
+
| 지원 | 없음(기본 Sheet·ContextMenu 사용) | 네 어댑터 stable(명시한 peer/host 범위) |
|
|
162
162
|
| 실행 환경 | — | 호환 개발 클라이언트(Expo Go 불가) |
|
|
163
163
|
|
|
164
164
|
## 함정
|
|
@@ -27,7 +27,7 @@ Native 화면에서 같은 일이 필요하면 이 구성을 옮기지 않는다
|
|
|
27
27
|
|
|
28
28
|
| 컴포넌트 | 역할 | 지침 |
|
|
29
29
|
| --- | --- | --- |
|
|
30
|
-
| `ColorPicker` |
|
|
30
|
+
| `ColorPicker` | 채도·밝기 영역 + 색조 막대 + 불투명도(`alpha`) + HEX 입력 + 프리셋 견본. controlled | [ColorPicker](../components/color-picker.md) |
|
|
31
31
|
| 선택값 문구 `Text` `role="status"` | 바뀐 HEX를 알림. 값은 `<bdi dir="ltr">`로 감싼다 | [Text](../components/text.md) |
|
|
32
32
|
| `Watermark` | 본문 위 장식 SVG 타일. 포인터·선택을 가로채지 않는다 | [Watermark](../components/watermark.md) |
|
|
33
33
|
| `Affix` | 가장 가까운 스크롤 조상 상단에 `position: sticky`로 붙는다 | [Affix](../components/affix.md) |
|
|
@@ -60,14 +60,14 @@ Native 화면에서 같은 일이 필요하면 이 구성을 옮기지 않는다
|
|
|
60
60
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
61
61
|
| --- | --- | --- | --- |
|
|
62
62
|
| 바깥 틀 | HJM ScreenLayout의 본문 | 세 스토리 모두 바깥 폭·여백을 정하지 않는다. 페이지 본문은 [Container](../components/container.md)가 정한다. Affix만 가장 가까운 스크롤 조상(문서 또는 제품의 `overflow: auto` 영역)에 붙는다. Web 전용이라 안전 영역·화면 키보드 처리는 없다 | Container gutter 폭 600 미만 `compact` 16 · 이상 `regular` 20 |
|
|
63
|
-
| 색
|
|
64
|
-
|
|
|
63
|
+
| 색 편집 | `ColorPicker` 영역·막대 | fieldset 첫 부분, 폭을 채운다 | 영역 4:3, 막대 높이 24, 부품 사이 `spacing.md` |
|
|
64
|
+
| 미리보기·HEX·프리셋 | `ColorPicker` 내부 | 막대 아래 순서대로 | 미리보기 원 44, 견본 누름 영역 44(원 32) |
|
|
65
65
|
| 선택값 | live 문구 | ColorPicker 바로 아래, 같은 Stack | `spacing.md` 16. 스토리는 `Container size="reading"`(최대 720) |
|
|
66
66
|
| 워터마크 | `Watermark` | 감싼 본문 전체를 덮는 오버레이(`inset: 0`) | `tileWidth` 240 · `tileHeight` 160 · `rotate` -22 · `opacity` 0.12(기본값), 색 `--hjm-color-text-sub` |
|
|
67
67
|
| 고정 행동 | `Affix` | 스크롤 조상 상단 + `offset` | `offset` 기본 0, 띄울 때는 spacing 토큰 값(스토리 `spacing.xs` 8). 스크롤 조상보다 높으면 고정하지 않고 흐름에 남는다 |
|
|
68
68
|
| 저장 실패 | `Notice` danger | 저장 버튼 위(Watermark 본문 안, Affix 아래 본문 첫머리) | 버튼과 `spacing.md` 16 |
|
|
69
69
|
|
|
70
|
-
- ColorPicker 내부 간격은
|
|
70
|
+
- ColorPicker 내부 간격은 HJM이 둔다(`padding: 1rem`, 부품 사이 `spacing.md`). 제품에서 덮지 않는다.
|
|
71
71
|
- Affix는 스크롤 조상 안에서만 붙는다. 그 조상의 높이·overflow는 HJM 화면 골격이 정한다(스토리의 280px 박스는 데모 값).
|
|
72
72
|
|
|
73
73
|
## 흐름과 상태
|
|
@@ -104,7 +104,7 @@ import { Stack, Text } from "@hjmds/react/layout";
|
|
|
104
104
|
<Stack gap="md">
|
|
105
105
|
<ColorPicker label={t("theme.accent")} value={color} onValueChange={setColor} alpha
|
|
106
106
|
presets={productPresets}
|
|
107
|
-
labels={{
|
|
107
|
+
labels={{ hex: t("theme.hex"), hue: t("theme.hue"), saturation: t("theme.saturation"), brightness: t("theme.brightness"), opacity: t("theme.opacity"), invalid: t("theme.invalidHex"), presets: t("theme.presets") }} />
|
|
108
108
|
<Text role="status">{t("theme.selected")} <bdi dir="ltr">{color}</bdi></Text>
|
|
109
109
|
</Stack>;
|
|
110
110
|
|
|
@@ -57,6 +57,7 @@ host: 남은 높이·safe area·키보드
|
|
|
57
57
|
| --- | --- | --- |
|
|
58
58
|
| 답글 접기·펼치기 | expandedIds와 원문 데이터 분리 | 서버 순서를 보존하고 접어도 답글 초안을 지우지 않는다 |
|
|
59
59
|
| 목록 추가 조회 실패 | 기존 items + threadFooter 재시도 | 이미 읽던 댓글과 스크롤을 유지한다 |
|
|
60
|
+
| 답글 더 보기 | `moreRepliesLabel` + (서버 페이지면) `repliesPage` | 답글은 5개씩 열고 받은 답글을 먼저 보인 뒤 다음 페이지를 요청한다([답글 5개씩](../components/comment-thread-screen.md#답글-5개씩-2026-10-25-정책-미게시)) |
|
|
60
61
|
| 첫 댓글 | empty + composer | 최초 로딩 실패와 실제 0건을 구분한다 |
|
|
61
62
|
| 전송 실패 | 기존 ready 또는 empty + notice | 초안·답글 대상을 유지한다. 항목별 좋아요 실패는 해당 항목만 복구한다 |
|
|
62
63
|
|
|
@@ -23,7 +23,7 @@ host: 남은 높이·safe area
|
|
|
23
23
|
|
|
24
24
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
25
25
|
| --- | --- | --- | --- |
|
|
26
|
-
| 바깥 틀 |
|
|
26
|
+
| 바깥 틀 | ConversationListScreen(가상화 host는 `scroll="content"`) | route 본문 | 헤더·notice는 화면 recipe padding 16, 본문 좌우는 0(행이 여백 소유) |
|
|
27
27
|
| 검색 | SearchField(라벨 "대화 검색", 검색 아이콘, placeholder "이름이나 메시지 검색") | `notice` | 화면 recipe itemGap 12. 결과가 없으면 `state.empty` |
|
|
28
28
|
| 행 | 라우팅 host(Pressable·링크) + ConversationRow | 본문 목록 | 아바타 56, 행 padding 가로 16(`conversationRowRecipe.paddingInline`)·세로 10, 화면 끝에서 아바타까지 16 |
|
|
29
29
|
| 행 메뉴 | ContextMenu | 행 host를 감싼다 | Native 카드 폭 최대 280, 행 48, 아이콘 24 |
|
|
@@ -107,3 +107,24 @@ import { ContextMenu } from "@hjmds/react/context-menu";
|
|
|
107
107
|
- 헤더 새 대화 IconButton·`notice` 검색·Web 메뉴 아이콘: 2026-10-17 화면 검토(S5)에서 예제가 새 대화 행동 없이, `notice`에 데모 안내 문구를 두고, Web과 Native 문구·메뉴 모양이 갈라진 것을 확인했다. 예제 데이터·문구는 `showcase/shared/conversation-list.ts` 하나를 두 플랫폼이 읽는다.
|
|
108
108
|
- 검색 아이콘·placeholder, 행동 결과 줄의 위치: 같은 날 재검증에서 notice로 옮긴 SearchField가 아이콘도 placeholder도 없는 빈 상자였고, 데모 행동 결과("미리보기 도구")가 기본 스토리 화면 위에 여백 없이 붙어 있었다. 아이콘은 Web `renderSearchIcon`·Native `renderLeading`(lucide `Search`)이고, 결과 줄은 다른 화면처럼 도구 스토리("행동 결과 확인")에서만 보인다.
|
|
109
109
|
- 본문 `contentInset="body-edge"`: 2026-10-17 사용자 결정. 화면 검토 S5#9에서 본문 padding 16과 행 padding 16이 겹쳐 아바타가 약 32pt 안쪽에서 시작했다(인스타그램 DM 약 16). 기존 `none`은 앱 사용처가 "host가 모든 여백 소유"로 써서 의미를 바꿀 수 없었다.
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## 공통 화면 API — 개발 소스, 미게시 (2026-10-25)
|
|
113
|
+
|
|
114
|
+
Web은 `@hjmds/react/screens`, Native는 `@hjmds/react-native/screens`의
|
|
115
|
+
`ConversationListScreen`을 사용한다. 기존 ScreenLayout 상태·헤더·footer·스크롤 계약을
|
|
116
|
+
재사용하며 `contentInset`은 노출하지 않는다. `search`는 검색 입력, `notice`는 조회 안내다.
|
|
117
|
+
두 슬롯은 공통 notice 영역에서 배치되고, 본문 여백은 행이 한 번만 소유한다.
|
|
118
|
+
제품은 ConversationRow에 데이터·라우팅·메뉴를 연결한다. 목록 바깥 Card·추가 좌우 padding·
|
|
119
|
+
List 구분선을 넣지 않는다. Native 가상화 host만 `scroll="content"`, 일반 자식은 기본 스크롤을 쓴다.
|
|
120
|
+
|
|
121
|
+
번뚝에서 같은 행을 쓰고도 앱 wrapper로 배치가 달라진 것이 도입 이유다.
|
|
122
|
+
이전에는 이 화면 조합이 Showcase에만 있었으므로 별도 공통 화면을 공개했고, 예제와 제품이
|
|
123
|
+
같은 구현을 소비한다. 기존 ScreenLayout·ListDetailScreen은 일반 화면/상세 전환용으로 유지한다.
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
import { ConversationListScreen } from "@hjmds/react/screens";
|
|
127
|
+
<ConversationListScreen title={title} search={searchField} actions={composeAction} state={state}>
|
|
128
|
+
{conversationLinks}
|
|
129
|
+
</ConversationListScreen>
|
|
130
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# 팔로워와 팔로잉
|
|
2
|
+
|
|
3
|
+
- 단계: 화면
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.47.0
|
|
7
|
+
- 검토일: 2026-10-25
|
|
8
|
+
- 근거: 사용자 Instagram 팔로워 화면 요청
|
|
9
|
+
- 스토리북: `배포/화면/소통/팔로워와 팔로잉`
|
|
10
|
+
|
|
11
|
+
## 목적
|
|
12
|
+
|
|
13
|
+
두 관계 목록을 검색하고 프로필로 이동하며 팔로우한다. [FollowListScreen](../components/follow-list-screen.md)이 전체 화면을 소유한다.
|
|
14
|
+
|
|
15
|
+
## 영역 구조
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
헤더 → 관계 탭 → 정렬·검색 도구 → 검색·안내 → 인물 목록
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
22
|
+
| --- | --- | --- | --- |
|
|
23
|
+
| 바깥 틀 | FollowListScreen | route 본문 | ScreenLayout 기본 16 여백 |
|
|
24
|
+
| 탐색 | Tabs·SearchField | 헤더 아래 | spacing.sm 12 |
|
|
25
|
+
| 인물 | List·ListRow | 스크롤 본문 | 48px 원형 아바타, comfortable, separator none |
|
|
26
|
+
|
|
27
|
+
뒤로가기·대상 이름 → 팔로워/팔로잉 수치 탭 → 검색·비차단 안내 → 평평한 인물 목록.
|
|
28
|
+
48px 원형 아바타·사용자 이름·선택적인 표시 이름은 왼쪽, 팔로우하기·맞팔로우하기 또는 메시지 보내기는 오른쪽이다. 내 팔로워의 삭제는 X 아이콘, 팔로잉의 취소는 더보기 메뉴에 둔다.
|
|
29
|
+
|
|
30
|
+
2026-10-25 사용자 참고 화면과 후속 지시를 반영했다. 일반 회원 아바타는 48px 원형으로 통일하며 댓글·채팅의 작은 크기는 유지한다. 이미 팔로우한 회원의 버튼에 탭과 같은 “팔로잉”을 반복하지 않는다.
|
|
31
|
+
|
|
32
|
+
## 버튼과 행동 위치
|
|
33
|
+
|
|
34
|
+
| 행동 | 컴포넌트·tone | 위치 | 개수·순서 |
|
|
35
|
+
| --- | --- | --- | --- |
|
|
36
|
+
| 뒤로가기 | ScreenLayout.exit | 헤더 왼쪽 | 하나 |
|
|
37
|
+
| 팔로우 | Button primary/secondary | 행 우측 | 제품 자격에 따라 0~1 |
|
|
38
|
+
| 추가 행동 | 제품 action | 팔로우 다음 | 선택적 |
|
|
39
|
+
|
|
40
|
+
뒤로가기는 헤더 왼쪽, 프로필 이동은 행, 팔로우와 삭제 확인은 독립된 우측 행동이다.
|
|
41
|
+
|
|
42
|
+
## 상태
|
|
43
|
+
|
|
44
|
+
| 상태 | 화면 모습 | 행동 |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| 기본 | 인물 목록 | 탐색·팔로우 |
|
|
47
|
+
| 로딩 | 중앙 로딩 | 조회 대기 |
|
|
48
|
+
| 빈 | 중앙 empty 문구 | 검색 변경 |
|
|
49
|
+
| 오류 | 중앙 조회 오류 또는 행 요청 오류 | 재시도 |
|
|
50
|
+
|
|
51
|
+
초기 로딩·오류·빈 목록은 공통 본문 상태다. 갱신 실패는 notice와 캐시 목록을 함께 유지한다.
|
|
52
|
+
팔로우 진행·실패는 해당 행에서만 표시한다.
|
|
53
|
+
|
|
54
|
+
## 사용하는 지침
|
|
55
|
+
|
|
56
|
+
| 지침 | 쓰는 곳 |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| [FollowListScreen](../components/follow-list-screen.md) | 전체 화면과 제품 슬롯 |
|
|
59
|
+
|
|
60
|
+
[FollowListScreen](../components/follow-list-screen.md)의 슬롯·배치·소유권을 따른다.
|
|
61
|
+
|
|
62
|
+
## 코드 골격
|
|
63
|
+
|
|
64
|
+
Web/Native `src/components/FollowListScreen.stories.tsx`의 실제 공개 API 예제를 사용한다.
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
// Web
|
|
68
|
+
import { FollowListScreen } from "@hjmds/react/follow-list";
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
// Native
|
|
73
|
+
import { FollowListScreen as NativeFollowListScreen } from "@hjmds/react-native/follow-list";
|
|
74
|
+
// 양 renderer의 실행 props는 위 Storybook 예제에서 제공한다.
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 큰 글자·다크·좁은 폭
|
|
78
|
+
|
|
79
|
+
| 조건 | 바뀌는 것 |
|
|
80
|
+
| --- | --- |
|
|
81
|
+
| 다크 | 제품 Provider의 토큰 색상 적용 |
|
|
82
|
+
| 좁은 폭 | 공통 행의 텍스트 축소·줄바꿈 |
|
|
83
|
+
| 최대 글자 | 루트 지침에 따라 범위 제외 |
|
|
84
|
+
|
|
85
|
+
제품 Provider 테마와 공통 행 동작을 따른다. 최대 글자 조건은 루트 지침대로 범위 제외다.
|
|
86
|
+
|
|
87
|
+
2026-10-25 후속 조정: 더보기는 세로 점 세 개(`moreVertical`)다. 정렬 기준은 우측에 놓고 검색창은 바로 아래 항상 보인다. 정렬·검색 간격은 spacing.xs(8)다. 번뚝의 최신순은 팔로우 관계 생성 시각 내림차순이며 이름순도 선택할 수 있다.
|
|
@@ -27,6 +27,29 @@ host: 남은 높이·safe area·키보드
|
|
|
27
27
|
| 내용 | 공개 슬롯 | 제목 → 프로필 → 설정 섹션과 행 | 화면 recipe의 sectionGap·itemGap; 슬롯 안은 각 지침 토큰 |
|
|
28
28
|
| 상태 | state 또는 해당 API 상태 | 본문 자리·비차단 notice | 입력 중 실패는 본문 높이와 초안을 유지 |
|
|
29
29
|
|
|
30
|
+
## 설정 그룹 표준 (2026-10-25)
|
|
31
|
+
|
|
32
|
+
2026-10-25 사용자 지시로 정했다. 같은 날 Spint·BurnTok·Utilverse 설정을 나란히 보니 그룹 이름과 순서가 앱마다 달랐다
|
|
33
|
+
("앱 설정" 한 묶음, "언어" 묶음 안의 "언어" 행 하나, 계정이 맨 아래 등). 행 아이콘 유무도 서로 달랐다.
|
|
34
|
+
모든 앱·웹의 설정 화면은 아래 그룹 id·이름·순서를 쓴다.
|
|
35
|
+
해당 항목이 없는 그룹은 생략한다. 순서를 바꾸거나 이름을 제품마다 바꾸지 않는다.
|
|
36
|
+
|
|
37
|
+
| 순서 | 섹션 `id` | 이름(ko / en) | 넣는 것 |
|
|
38
|
+
| --- | --- | --- | --- |
|
|
39
|
+
| 1 | `account` | 계정 / Account | 내 프로필 입구·닉네임·프로필 사진·연결한 계정. 손님이면 로그인 행 하나 |
|
|
40
|
+
| 2 | `display` | 화면 / Display | 화면 모드 → 언어 → 그 밖의 표시 설정(홈 화면 테마, 글자 방향 등). 언어도 여기 둔다 |
|
|
41
|
+
| 3 | `sound` | 소리와 진동 / Sound & haptics | 효과음·진동. 없으면 생략 |
|
|
42
|
+
| 4 | `notifications` | 알림 / Notifications | 푸시 알림, 알림 종류 설정, 알림함 입구 |
|
|
43
|
+
| 5 | `privacy` | 개인정보 / Privacy | 사용 통계 보내기, 광고 개인정보 선택, 차단한 사용자 |
|
|
44
|
+
| 6 | `support` | 도움과 약관 / Help & legal | 문의·문제 알리기, 개인정보처리방침, 이용약관, 오픈소스 라이선스, 버전 |
|
|
45
|
+
| 7 | `account-actions` | 계정 관리 / Account actions | 로그아웃, 계정 삭제. 항상 맨 끝의 별도 묶음이다(파괴 행동 규칙) |
|
|
46
|
+
|
|
47
|
+
- **그룹 이름과 행 이름을 겹치지 않는다.** "언어" 그룹 안의 "언어" 행처럼 한 행만 담는 같은 이름 그룹을 만들지 않는다. 그래서 언어와 화면 모드는 `display` 하나에 둔다.
|
|
48
|
+
- **"앱 설정"처럼 내용을 말하지 않는 묶음 이름을 쓰지 않는다.** 위 표의 그룹으로 나눈다.
|
|
49
|
+
- **행 시작 아이콘:** 선택 행(ChoiceRow)과 이동 행(ListRow)은 같은 그룹 안에서 모두 시작 아이콘을 둔다. 아이콘 크기·색은 `listRowRecipe.trailing.glyph`와 `iconColor`를 쓴다. 켜고 끄는 행(Switch row)은 아이콘 없이 label·description만 둔다. 한 그룹 안에서 아이콘 있는 선택 행과 없는 선택 행을 섞지 않는다.
|
|
50
|
+
- **프로필 탭이 있는 앱:** 내 프로필 탭에서 설정을 여는 앱(Spint·BurnTok)도 같은 순서를 쓴다. `account` 그룹에는 프로필 화면에서 할 수 없는 항목(닉네임·연결한 계정·사진 지우기)만 남긴다. 사진 변경 자체는 내 사진을 누르는 것으로 한다([Avatar 지침](../components/avatar.md)).
|
|
51
|
+
- **문구는 제품이 소유한다.** 그룹 이름은 위 의미를 지키는 제품 i18n 문구이고, 영어 외 언어는 같은 뜻으로 번역한다. `id`는 위 영문 id를 그대로 쓴다.
|
|
52
|
+
|
|
30
53
|
## 버튼과 행동 위치
|
|
31
54
|
|
|
32
55
|
| 행동 | 컴포넌트·tone | 위치 | 개수·순서 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hjmds/design-contracts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.48.0",
|
|
4
4
|
"description": "Renderer-neutral design contracts, tokens, recipes, and behaviors shared by HJM products.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -823,6 +823,12 @@
|
|
|
823
823
|
"import": "./dist/shortcut-rail.js",
|
|
824
824
|
"default": "./dist/shortcut-rail.js"
|
|
825
825
|
},
|
|
826
|
+
"./share-sheet": {
|
|
827
|
+
"types": "./dist/share-sheet.d.ts",
|
|
828
|
+
"react-native": "./dist/share-sheet.js",
|
|
829
|
+
"import": "./dist/share-sheet.js",
|
|
830
|
+
"default": "./dist/share-sheet.js"
|
|
831
|
+
},
|
|
826
832
|
"./components/time-picker": {
|
|
827
833
|
"types": "./dist/time-picker.d.ts",
|
|
828
834
|
"react-native": "./dist/time-picker.js",
|