@hjmds/design-contracts 1.49.0 → 2.0.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.
Files changed (53) hide show
  1. package/dist/base-recipes.d.ts +0 -7
  2. package/dist/base-recipes.d.ts.map +1 -1
  3. package/dist/base-recipes.js +3 -7
  4. package/dist/base-recipes.js.map +1 -1
  5. package/dist/behaviors.d.ts +7 -7
  6. package/dist/catalog.d.ts +42 -11
  7. package/dist/catalog.d.ts.map +1 -1
  8. package/dist/catalog.js +1 -1
  9. package/dist/catalog.js.map +1 -1
  10. package/dist/component-recipes.d.ts +2 -0
  11. package/dist/component-recipes.d.ts.map +1 -1
  12. package/dist/component-recipes.js +2 -0
  13. package/dist/component-recipes.js.map +1 -1
  14. package/dist/date-range.d.ts +56 -8
  15. package/dist/date-range.d.ts.map +1 -1
  16. package/dist/date-range.js +37 -5
  17. package/dist/date-range.js.map +1 -1
  18. package/dist/icon-button-recipe.d.ts +0 -6
  19. package/dist/icon-button-recipe.d.ts.map +1 -1
  20. package/dist/icon-button-recipe.js +8 -11
  21. package/dist/icon-button-recipe.js.map +1 -1
  22. package/dist/screen-patterns.d.ts +8 -0
  23. package/dist/screen-patterns.d.ts.map +1 -1
  24. package/dist/screen-patterns.js +5 -0
  25. package/dist/screen-patterns.js.map +1 -1
  26. package/dist/version.d.ts +1 -1
  27. package/dist/version.d.ts.map +1 -1
  28. package/dist/version.js +1 -1
  29. package/dist/version.js.map +1 -1
  30. package/docs/drawing-editor.md +2 -3
  31. package/docs/generated/component-maturity.md +5 -5
  32. package/docs/generated/renderer-evidence.json +5 -5
  33. package/docs/generated/renderer-evidence.md +3 -3
  34. package/docs/generated/showcase-manifest.json +4 -4
  35. package/docs/optional-adapters.md +1 -1
  36. package/docs/stable-core.md +12 -0
  37. package/docs/theme-palette.md +3 -1
  38. package/docs/usage/README.md +6 -6
  39. package/docs/usage/components/auth-screen-layout.md +2 -0
  40. package/docs/usage/components/avatar.md +4 -0
  41. package/docs/usage/components/button.md +4 -3
  42. package/docs/usage/components/context-menu.md +2 -2
  43. package/docs/usage/components/date-range-picker.md +9 -3
  44. package/docs/usage/components/drawing-editor.md +6 -4
  45. package/docs/usage/components/icon-button.md +6 -6
  46. package/docs/usage/components/keyboard-dock.md +2 -2
  47. package/docs/usage/components/keyboard-form-scroll-view.md +2 -2
  48. package/docs/usage/components/keyboard-motion-provider.md +2 -2
  49. package/docs/usage/components/profile-summary.md +2 -2
  50. package/docs/usage/components/radio-group.md +8 -0
  51. package/docs/usage/components/share-sheet.md +5 -3
  52. package/docs/usage/compositions/disclosure.md +3 -3
  53. package/package.json +1 -1
@@ -7,7 +7,7 @@
7
7
  [선택 기준](SELECTION.md)으로 그대로 사용·공개 슬롯 확장·공통 API 보완·제품 고유 기능 연결을 고른다. 설치 버전의 지침을 본다: `node_modules/@hjmds/design-contracts/docs/usage/`.
8
8
  이름을 모르면 [쉬운 말로 찾고 비교하기](FINDING.md)에서 일상 표현과 선택 기준·요청 예시로 시작한다.
9
9
  제공 기준은 확인한 기본 API 버전이며 최초 출시 버전이 아니다. 구성·화면은 각 API 지침의 제공 버전을 확인한다.
10
- API 안정성은 1.48.0의 생성 manifest에서 읽은 정규(canonical) API 기준이다. 동반·선택형 확장은 개별 계약을 확인한다.
10
+ API 안정성은 1.50.0의 생성 manifest에서 읽은 정규(canonical) API 기준이다. 동반·선택형 확장은 개별 계약을 확인한다.
11
11
  Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품 반영을 의미하지 않는다.
12
12
  `분류`는 Storybook 제목 `<배포|실험>/<단계>/<분류>/<항목>`의 셋째 마디이고, 토큰·구성·화면은 Storybook 메뉴와 같은 순서다.
13
13
 
@@ -95,7 +95,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
95
95
  | [DesignSystemProvider](components/design-system-provider.md) | 기반 기능 | 앱 루트에 한 번 둔다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
96
96
  | [Dialog](components/dialog.md) | 오버레이 | 화면 흐름을 잠시 멈추고 사용자의 주의가 필요한 **짧은 작업**에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
97
97
  | [Divider](components/divider.md) | 레이아웃 | 서로 다른 내용 묶음 사이에 얇은 구분선이 필요할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
98
- | [DrawingEditor](components/drawing-editor.md) | 입력 | 펜·지우개로 그림을 그리고 이미지로 저장하는 전체 편집 화면에 쓴다. | 미게시(1.48.0 이후) | Web · Native | Web beta / Native beta | 실험 |
98
+ | [DrawingEditor](components/drawing-editor.md) | 입력 | 펜·지우개로 그림을 그리고 이미지로 저장하는 전체 편집 화면에 쓴다. | 1.49.0 | Web · Native | Web stable / Native stable | 배포 |
99
99
  | [EditorScreen](components/editor-screen.md) | 레이아웃 | 글쓰기·프로필 수정처럼 한 화면 전체가 편집 흐름일 때 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
100
100
  | [EffectSurface](components/effect-surface.md) | 시각 효과 | 환영·온보딩·빈 히어로처럼 분위기를 주는 배경이 필요할 때 내용 뒤에 장식 레이어(mesh·glow·grain)를 깐다. | 1.12.1 | Web · Native | 개별 API 계약 참조 | 배포 |
101
101
  | [EmptyState](components/empty-state.md) | 상태와 알림 | 목록이 비었거나 검색 결과가 0건이라 **아직 없음**을 알릴 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
@@ -121,9 +121,9 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
121
121
  | [InlineEdit](components/inline-edit.md) | 구성/입력과 작성 | 목록·상세의 제목처럼 이미 확정된 짧은 값 하나를 그 자리에서 명시적으로 편집·저장·취소할 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
122
122
  | [KeyboardAvoidanceContext](components/keyboard-avoidance-context.md) | — | 제품이 직접 만든 키보드 대응 영역 안에서 HJM `ScreenLayout`·`Sheet`가 여백을 두 번 주지 않게 할 때 쓴다. | 1.27.0 | Native | 개별 API 계약 참조 | 배포 |
123
123
  | [KeyboardAvoiding](components/keyboard-avoiding.md) | — | 추가 native peer 없이 하단 행동(BottomCTA, 채팅 입력창)이 소프트웨어 키보드에 가려지지 않게 할 때 쓴다. | 1.12.1 | Native | 개별 API 계약 참조 | 배포 |
124
- | [KeyboardDock](components/keyboard-dock.md) | 구성/직접 조작과 모션 | `react-native-keyboard-controller`를 설치한 앱에서 화면 하단에 고정된 행동(BottomCTA, 채팅 입력창)이 키보드와 함께 위아래로 움직이게 할 때 쓴다(Native 전용, 별도 보조 기능. API 성숙도는 실험적 어댑터). 내부는 `KeyboardStickyView`이며, 여백을 바꾸는 대신 키보드 움직임을 따라 translate 한다. | 1.12.1 | Native | 개별 API 계약 참조 | 배포 |
125
- | [KeyboardFormScrollView](components/keyboard-form-scroll-view.md) | 구성/직접 조작과 모션 | 입력 필드가 여러 개인 세로 스크롤 폼(가입, 프로필 수정, 주소 입력)에서 포커스된 필드가 키보드에 가려지지 않게 스크롤해 줄 때 쓴다(Native 전용, 별도 보조 기능. API 성숙도는 실험적 어댑터). 내부는 `react-native-keyboard-controller`의 `KeyboardAwareScrollView`이고, `keyboardShouldPersistTaps="handled"`로 고정돼 키보드가 열린 채 버튼을 눌러도 탭이 전달된다. | 1.12.1 | Native | 개별 API 계약 참조 | 배포 |
126
- | [KeyboardMotionProvider](components/keyboard-motion-provider.md) | 구성/직접 조작과 모션 | `@hjmds/react-native/keyboard-controller` 어댑터(KeyboardDock, KeyboardFormScrollView)를 쓰는 앱의 루트에 **한 번** 설치한다(Native 전용, 별도 보조 기능. API 성숙도는 실험적 어댑터). 내부는 `react-native-keyboard-controller`의 `KeyboardProvider`이며, 어댑터를 준비하려고 OS 키보드를 미리 띄우지 않도록 `preload={false}`로 고정돼 있다. | 1.12.1 | Native | 개별 API 계약 참조 | 배포 |
124
+ | [KeyboardDock](components/keyboard-dock.md) | 구성/직접 조작과 모션 | `react-native-keyboard-controller`를 설치한 앱에서 화면 하단에 고정된 행동(BottomCTA, 채팅 입력창)이 키보드와 함께 위아래로 움직이게 할 때 쓴다(Native 전용, 별도 보조 기능. 문서화한 선택 peer 계약은 stable). 내부는 `KeyboardStickyView`이며, 여백을 바꾸는 대신 키보드 움직임을 따라 translate 한다. | 1.12.1 | Native | 개별 API 계약 참조 | 배포 |
125
+ | [KeyboardFormScrollView](components/keyboard-form-scroll-view.md) | 구성/직접 조작과 모션 | 입력 필드가 여러 개인 세로 스크롤 폼(가입, 프로필 수정, 주소 입력)에서 포커스된 필드가 키보드에 가려지지 않게 스크롤해 줄 때 쓴다(Native 전용, 별도 보조 기능. 문서화한 선택 peer 계약은 stable). 내부는 `react-native-keyboard-controller`의 `KeyboardAwareScrollView`이고, `keyboardShouldPersistTaps="handled"`로 고정돼 키보드가 열린 채 버튼을 눌러도 탭이 전달된다. | 1.12.1 | Native | 개별 API 계약 참조 | 배포 |
126
+ | [KeyboardMotionProvider](components/keyboard-motion-provider.md) | 구성/직접 조작과 모션 | `@hjmds/react-native/keyboard-controller` 어댑터(KeyboardDock, KeyboardFormScrollView)를 쓰는 앱의 루트에 **한 번** 설치한다(Native 전용, 별도 보조 기능. 문서화한 선택 peer 계약은 stable). 내부는 `react-native-keyboard-controller`의 `KeyboardProvider`이며, 어댑터를 준비하려고 OS 키보드를 미리 띄우지 않도록 `preload={false}`로 고정돼 있다. | 1.12.1 | Native | 개별 API 계약 참조 | 배포 |
127
127
  | [Layout](components/layout.md) | 레이아웃 | 앱의 상시 골격(header · sidebar · main · footer)을 한 번 세울 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
128
128
  | [Link](components/link.md) | 동작 | 사용자가 복사하거나 새 탭으로 열 수 있는 **목적지**로 이동할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
129
129
  | [List](components/list.md) | 데이터 표시 | 이미 다 불러온, 개수가 많지 않은 행들을 이름 있는 목록 하나로 묶을 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
@@ -177,7 +177,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
177
177
  | [SettingsScreen](components/settings-screen.md) | 레이아웃 | 앱의 설정 화면 전체에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
178
178
  | [SharedTransitionElement](components/shared-transition-element.md) | 구성/직접 조작과 모션 | 목록의 카드(사진·썸네일)를 눌러 상세 화면으로 갈 때, 같은 요소가 두 화면 사이에서 확대·축소되어 이어지는 공유 요소 전환에 쓴다. | 1.12.1 | Native | 개별 API 계약 참조 | 배포 |
179
179
  | [SharedTransitionScreen](components/shared-transition-screen.md) | 구성/직접 조작과 모션 | `createHjmTransitionStack()`으로 만든 stack에서 공유 요소 전환을 쓸 때, **각 라우트 본문**을 감싼다. | 1.12.1 | Native | 개별 API 계약 참조 | 배포 |
180
- | [ShareSheet](components/share-sheet.md) | 오버레이 | 링크 하나를 **앱 안의 받는 사람에게 보내거나(선택), 링크를 복사하거나, 기기 공유 시트로 내보내는** 공유 시트에 쓴다. | 미게시(1.47.0 이후) | Web · Native | 개별 API 계약 참조 | 실험 |
180
+ | [ShareSheet](components/share-sheet.md) | 오버레이 | 링크 하나를 **앱 안의 받는 사람에게 보내거나(선택), 링크를 복사하거나, 기기 공유 시트로 내보내는** 공유 시트에 쓴다. | 1.48.0 | Web · Native | 개별 API 계약 참조 | 배포 |
181
181
  | [Sheet](components/sheet.md) | 오버레이 | 현재 화면 위에 모달로 띄우는 보조 작업 패널에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
182
182
  | [ShortcutRecorder](components/shortcut-recorder.md) | 구성/직접 조작과 모션 | Web 앱에서 사용자가 수정키+키 조합을 눌러 기록하고, 확인 뒤 실제 단축키로 등록하게 할 때 쓴다. | 1.21.0 | Web | 개별 API 계약 참조 | 배포 |
183
183
  | [Sidebar](components/sidebar.md) | 탐색 | 데스크톱 Web의 세로 내비게이션에 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
@@ -88,6 +88,7 @@ import { AuthScreenFooter, AuthScreenHero, AuthScreenLayout } from "@hjmds/react
88
88
  | `layoutStyle` | margin·width·flex·`alignSelf` | — | 바깥 배치 전용(Web·Native 모두) |
89
89
  | `initialFocus` | `first-input` · `none` | `first-input` | 페이지 진입 시 첫 유효 입력 또는 `initialFocusTarget` 칸에 한 번 포커스한다. 기본값·진입 한정 정책 변경은 1.33.0부터 제공. 진입 종료 뒤 단계 변경·입력 표시로 자동 포커스를 다시 시작하지 않으며 하단 시트 안에서는 자동 포커스하지 않는다. [진입 포커스](screen-layout.md#진입-포커스) |
90
90
  | Native `testID` | `string` | — | 본문 `ScrollView` 식별(머리 띠가 있어도 같은 `ScrollView`) |
91
+ | Native `backdrop` | `ReactNode` | — | 미게시(checkout 개발). 스크롤 내용 맨 위 뒤에 깔리는 장식 장면(제품 하늘 띠 등). hero·main·footer와 **함께 스크롤**하고 터치·접근성에서 빠진다. 제품 노드는 내용 위 끝 기준 절대 위치로 놓으며 좌우 여백 밖까지 닿는다. 화면 뒤에 고정한 그림 위로 글자가 지나가지 않게 한다 |
91
92
  | Native `safeAreaBottom` | 0 이상의 유한수 | `0` | 화면이 닿는 창 아래 가장자리의 safe area(`useSafeAreaInsets().bottom`, 이미 아래를 들인 host 안이면 0). 아래 여백이 `max(paddingBlock, inset)` — `regular` max(40, inset), `compact` max(20, inset) — 으로 한 번만 들어간다(ScreenLayout과 같은 규칙, 합산 없음). 키보드가 열리면 paddingBlock만. 음수·무한대는 `RangeError` |
92
93
  | `AuthScreenHero` `title` · `eyebrow` · `description` · `mark` · `headingLevel` | 문자열 · `ReactNode` · `1`~`3` | `headingLevel` 1 | 제목은 필수이고 빈 문자열이면 `TypeError`. 밀도는 바깥 `AuthScreenLayout`을 따른다 |
93
94
  | `AuthScreenFooter` `links` · `notice` · `children` | 링크 배열(1개 이상) · `ReactNode` | — | 빈 링크 목록·빈 라벨은 `TypeError`(LS-04: 약관 링크는 로그인 전 같은 화면에 있어야 한다). `children`은 링크 아래 제품 내용 |
@@ -156,6 +157,7 @@ import { AuthScreenFooter, AuthScreenHero, AuthScreenLayout } from "@hjmds/react
156
157
 
157
158
  - `title`·`exit`를 쓰면 바깥에 얹던 TopBar·제품 헤더를 지운다. 둘 다 두면 머리가 두 줄이 된다. Native에서 host TopBar가 맡던 위 safe area(`safeAreaTop`)는 host의 SafeAreaView top으로 옮긴다.
158
159
 
160
+ - Native에서 하늘 띠 같은 장식을 레이아웃 바깥(화면 뒤)에 고정하지 않는다. 내용이 넘쳐 스크롤되면 글자가 그림 위로 지나가 읽히지 않는다(2026-10-28 다에리 가입 단계). `backdrop`으로 넘긴다.
159
161
  - Native에서 아래 홈 인디케이터를 `SafeAreaView edges={['bottom']}`·`paddingBottom: insets.bottom` 래퍼로 들이지 말고 `safeAreaBottom`으로 넘긴다. 둘을 같이 주면 inset + paddingBlock이 다시 더해진다.
160
162
  - 1.28.0까지 Native는 iOS `contentInsetAdjustmentBehavior="automatic"`으로 위·아래 safe area를 iOS에서만 더했다. 1.28.1부터 위는 host, 아래는 `safeAreaBottom`이다. 위 safe area를 아무도 들이지 않던 화면은 host에서 TopBar `safeAreaTop`이나 SafeAreaView top으로 들인다.
161
163
  - Native는 자체로 키보드 스크롤 여백을 준다. 부모가 다시 keyboard avoidance를 하면 여백이 중복될 수 있으니
@@ -192,3 +192,7 @@ Avatar를 쓰지 못한 이유(이름 필수, Web 이미지 호스트 없음, 4
192
192
  사용자 요청으로 Web·Native 기본값은 `size="large"`(48), `shape="circle"`이다. 프로필과 일반 회원 목록에도 48을 쓰고 댓글·채팅 등 촘촘한 목록의 명시적인 작은 단계는 유지한다. 제품별 사진·이니셜·이모지·동물 그림도 같은 회원 프레임을 따른다. 콘텐츠 썸네일·앱 아이콘의 모양이나 크기에는 적용하지 않는다.
193
193
 
194
194
  기본 `frame="outlined"`는 `border.control` 50% alpha, 1px의 원형 윤곽이다. 2026-10-25 번뚝 이모지 아바타에서 기존 선은 흐리고 2px 불투명 선은 진하다는 피드백을 거쳐 1px·50%로 조정했으며, 사용자가 이 값을 디자인 시스템 기본값으로 지정했다. 지름은 테두리 포함이고 Web·Native에 동일하다. 미게시 개발 중 추가했던 `strong` 옵션은 기본값과 합쳐 제거했다. 제품에서 별도 frame을 지정할 필요가 없다.
195
+
196
+ ### 그림 전체 표시 (2026-10-28)
197
+
198
+ 스핀트에서 직접 그린 정사각형 그림의 모서리가 원형 프로필에서 잘린 사례로 `imageFit="contain"`을 추가했다. 기본 `cover`는 기존 사진 채움을 유지한다. `contain`은 사진·그림 전체를 프레임 안에 넣고 여백을 남긴다. 공통 recipe의 `1 / √2` 비율로 정사각형 대각선까지 원 안에 들어오며 이미지의 모서리를 다시 둥글게 자르지 않는다. Web·Native와 기존 저장 이미지에 동일하게 적용한다. Native `renderImage`의 `size`에는 실제 이미지 영역 크기가 전달된다.
@@ -131,7 +131,7 @@ Web·Native와 라이트·다크 테마에 동일하게 적용한다.
131
131
  Native에서 `style`·`labelStyle`을 넘기면 실행 중 `TypeError`가 난다.
132
132
  - 색은 tone과 제품 테마 토큰으로 바꾼다. 버튼마다 브랜드 색을 하드코딩하지 않는다.
133
133
  - 진행 중 상태는 `loading`으로 표시하고 같은 자리에 별도 Spinner를 겹치지 않는다. children을 빈 문자열이나 다른 길이의 진행 문구로 바꾸지 않는다. 렌더러가 문구·아이콘을 숨긴 자리에 중앙 스피너만 표시하고 기존 크기·접근성 이름을 유지한다.
134
- - "한 화면 `primary` 하나"의 예외는 다음과 같다. `selected`를 준 버튼은 tone과 관계없이 `buttonRecipe.states.selected`(배경 `bg`, 글자 `contentBrand`, 선택 테두리 없음)로 칠해져 primary 채움이 아니므로 세지 않고(Web `.hjm-button[data-selected="true"]`, Native `internal/recipe-button.tsx`, 작품 카드의 저장 토글), Sheet·Dialog 안의 행동은 그 표면 안에서 primary 하나를 센다([Sheet](sheet.md) `footer`, 작품 탐색·랜딩 스토리의 시트).
134
+ - `selected`도 원래 tone의 배경을 유지하므로 `primary` 버튼이면 주 행동 개수에 포함한다. Sheet·Dialog 안의 행동은 그 표면 안에서 primary 하나를 센다([Sheet](sheet.md) `footer`).
135
135
 
136
136
  - `selected` 버튼 여러 개를 나란히 두어 하나 고르기·여러 개 고르기·켜고 끄기를 흉내 내지 않는다. 그 의도는 [선택 조립 규칙](../SELECTION.md#조립-규칙필수)의 SegmentedControl·RadioGroup·Chip·Switch를 쓴다.
137
137
 
@@ -150,8 +150,7 @@ Web·Native와 라이트·다크 테마에 동일하게 적용한다.
150
150
 
151
151
  - footer 순서를 모든 본문 행동에 강제하지 않는다. 시간 선택처럼 주 행동 다음에 초기화가 오는 구성과 AlertDialog의 좁은 폭 순서는 각각 명시된 구성 계약을 따른다.
152
152
 
153
- `selected`에는 선택 테두리를 추가하지 않는다. `selected` 접근성 상태와
154
- contentBrand 글자·아이콘 색은 유지하며 키보드 focus outline은 선택과 별도로 유지한다.
153
+ 2026-10-28 사용자 지시: `selected`로 배경색을 바꾸지 않는다. 원래 tone의 배경·테두리를 유지하고 선택 테두리를 추가하지 않는다. ghost는 투명하게 남으며 primary·danger의 채움과 역상 글자색도 유지한다. 선택은 전경색·채운 아이콘 또는 상태 라벨로 표현하며 `aria-pressed`/Native 접근성 상태와 키보드 focus outline은 유지한다. 꽃 반응에 선택 면이 중복으로 생긴 사례 때문에 기존 선택 배경 지침을 대체했다. IconButton도 같은 규칙을 쓴다.
155
154
 
156
155
  ## 저장 확정 라벨과 프레임 예약 (npm 1.21.0 · 2026-10-15)
157
156
 
@@ -237,3 +236,5 @@ import { ButtonLink } from "@hjmds/react/actions";
237
236
  2026-10-20 다에리의 지도/장소 이동에서 초안 보호·오프라인 차단을 유지하기 위해 추가했다.
238
237
  전달받은 anchor props와 `ref`를 실제 링크에 그대로 연결하며, `href`가 없으면 내부 라우팅도 하지 않는다.
239
238
  버튼의 색/크기/초점/비활성·수정키/새 탭 의미는 HJM이 소유하고 제품은 목적지·이동 처리를 연결한다.
239
+
240
+ 선택 recipe 직접 소비의 변경: `states.selected`는 전경만 소유하며 배경·테두리는 `tones[tone]`에서 읽는다. 필드 제거는 다음 contracts major 게시 대상으로 기록하며 컴포넌트의 `selected` 사용법은 동일하다.
@@ -12,7 +12,7 @@
12
12
 
13
13
  Web에서 제품이 소유한 영역(카드·목록 행·캔버스)의 우클릭·길게 누르기·Shift+F10에 명령 목록을
14
14
  띄울 때 쓴다. 같은 명령은 화면의 다른 경로(버튼·Menu)에도 있어야 한다. Native는 추가 의존성 없는
15
- canonical `ContextMenu`(`@hjmds/react-native/context-menu`, 미게시, 2026-10-16)가 채팅 길게 누르기 메뉴와 같은
15
+ canonical `ContextMenu`(`@hjmds/react-native/context-menu`, 2026-10-16 도입)가 채팅 길게 누르기 메뉴와 같은
16
16
  모양(배경 어둡게 + 누른 위치 근처 카드, 행 높이 48, 아이콘 24 칸, 위험 항목 빨간 글자)으로 연다. 대화 목록·댓글처럼
17
17
  행을 길게 눌러 여는 동작 목록은 이것을 쓴다. OS 고유 메뉴가 꼭 필요할 때만 선택 어댑터 `NativeContextMenu`를 쓴다.
18
18
 
@@ -148,7 +148,7 @@ import { Pressable } from "react-native";
148
148
  | 메뉴 접근성 이름 | `accessibilityLabel`(필수) | 없음(자식 요소가 이름을 가짐) |
149
149
  | 항목 `textValue`·`shortcut` | 있음 | 없음 |
150
150
  | 열림 알림 | 없음 | `onOpenChange(open)` |
151
- | 성숙도 | stable | 실험적 어댑터(기기 증거 전까지 canonical unsupported) |
151
+ | 성숙도 | stable | 선택형 어댑터 stable (canonical ContextMenu와 별도) |
152
152
 
153
153
  Web 활성 항목은 실제 마우스 이동으로만 바꾸며(1.15.0 변경), 늦은 경계 이벤트만으로 키보드 선택을 덮지 않는다. 클릭·키보드 실행·포커스 복귀와
154
154
  Native OS 어댑터는 기존 계약을 유지한다. 고정 대기 시간이 아니라 입력 의도로 구분한다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-25
7
+ - 검토일: 2026-10-28
8
8
  - 근거: [DateRange](../../date-range.md), 격자는 [Calendar](../../calendar.md), 규칙 함수 `src/date-range.ts`
9
9
  - 스토리북: `배포/컴포넌트/입력/기간 선택`
10
10
 
@@ -20,7 +20,7 @@
20
20
  | --- | --- |
21
21
  | 날짜 하나 | [DatePicker](date-picker.md) |
22
22
  | 달력을 보여 주기만 하거나 날짜 하나를 인라인으로 고름 | [Calendar](calendar.md) |
23
- | 접힌 필드에서 눌러 여는 구간 선택 | 없음. 필요하면 [Sheet](sheet.md)·[Popover](popover.md) 안에 제품이 합성한다 |
23
+ | 오버레이에서 고르고 적용 버튼으로 확정 | [Sheet](sheet.md) 안에 합성하고 `footer`에 `BottomCTA`를 둔다. 본문 안에서 접어 두기만 하면 `collapsible`을 쓴다 |
24
24
 
25
25
  ## 공개 이름과 import
26
26
 
@@ -80,7 +80,8 @@ import { DateRangePicker } from "@hjmds/react-native/date-range";
80
80
  | `previousMonth` · `nextMonth` | `{ month: string, label: string }` | — | 없으면 이동 버튼이 없다 |
81
81
  | `renderCellContent` | `(date: string) => ReactNode` | — | 날짜 아래 보조 표시. 넘길 때만 행에 보조 줄이 생긴다 |
82
82
  | `summary` | `{ startLabel, endLabel, placeholder, formatDate(date) }` | — | 격자 위 시작일·종료일 칸. 지금 고르는 쪽(`resolveDateRangeActiveEdge`: 비었으면 시작, 고르는 중이면 끝, 완성되면 없음)을 테두리로 표시한다. 문구는 i18n, 날짜 형식은 제품 소유 |
83
- | `presets` | `{ id, label, length: { days } \| { months } }[]` | — | 요약 아래 단일 선택 Chip 줄(최근 7일·1개월 등). 모두 `grid.todayDate`로 끝나고(`resolveDateRangePreset`), 값이 그 구간과 같을 때만 선택으로 보인다. 누르면 끝 달로 `onFocusedMonthChange(month, "jump")`를 요청한다 |
83
+ | `presets` | `{ id, label, length: { days } \| { months } \| { all: true } }[]` | — | 요약 아래 단일 선택 Chip 줄. `days`·`months`는 `grid.todayDate`로 끝나고 `all`은 빈 구간(날짜 제한 없음)이다(`resolveDateRangePreset`). 값이 그 구간과 같을 때만 선택으로 보인다. 누르면 끝 달로 `onFocusedMonthChange(month, "jump")`를 요청한다(`all` 제외). 기간 필터의 표준 칩은 `dateRangeRecipe.presets.standard`(1일·1주일·1개월·3개월·전체)이고 문구는 제품 i18n이다 |
84
+ | `collapsible` | `{ expanded?, defaultExpanded?, onExpandedChange? }` | 없음(늘 펼침) | 주면 격자만 `summary` 뒤로 접고 `presets`는 계속 보인다. `summary`가 필수이고(없으면 `TypeError`) 그 줄이 펼침 버튼(`aria-expanded`·Native `expanded` 상태, 끝에 아래/위 화살표)이 된다. 접힌 채 시작하고(`defaultExpanded` 기본 `false`), 프리셋을 누르거나 날짜 탭으로 구간이 완성되면 다시 접는다(`resolveDateRangeExpandedAfterSelection`) |
84
85
  | Web `layoutStyle` | 배치 전용 style | — | 루트 배치 |
85
86
 
86
87
  ## 배치
@@ -98,6 +99,7 @@ import { DateRangePicker } from "@hjmds/react-native/date-range";
98
99
  | 색 | 띠 `primary` 15% tint(두 끝과 같은 브랜드색. 처음 쓴 `surfaceSelected`는 기본 팔레트에서 #f0f1f3 회색이라 구간이 거의 보이지 않았다), 두 끝 Calendar 선택 원(`action.brand`). 칸 바탕 `surface.sunken`, 다음에 고를 칸 테두리 2 `action.brand`, 빈 값 `content.tertiary` | `dateRangeRecipe` |
99
100
  | 순서·정렬 | 오버레이 없이 본문(폼·필터 영역·[Section](section.md)) 안에 격자를 펼쳐 둔다. 위에서 `summary`(시작일 · 종료일) → `presets` 칩 줄 → 월 머리(이전 · 월 이름 · 다음) → 요일 → 날짜 격자. 제출 행동은 격자 **아래**에 둔다. 시트에서는 `footer`에 `BottomCTA` [보조(전체 날짜 등)][주(적용)] 한 줄로 두고 시트 높이는 `auto`로 맞춘다. `summary`를 쓰면 별도 안내·결과 문장을 두지 않는다. 제출 버튼은 `isCompleteDateRange(value)`가 참일 때만 활성이다 | 같은 파일 |
100
101
  | 고정·스크롤 | 본문과 함께 스크롤한다(오버레이 없음) | — |
102
+ | 접기 | `collapsible`이면 요약 두 칸 뒤에 화살표 열(`dateRangeRecipe.toggle`: 아이콘 `glyph.sm` 20, 칸과 간격 `spacing.xs` 8)이 붙고 요약 줄 전체가 버튼이다. 접히면 요약과 프리셋 칩 줄만 남고 격자는 그리지 않는다. 검색 필드가 있으면 그 아래에 요약 → 칩 → (펼쳤을 때) 격자 순서로 둔다 | `dateRangeRecipe.toggle`, `.hjm-date-range__summary[data-toggle]` |
101
103
  | 좁은 폭·큰 글자 | Web 격자 최소 폭 308보다 좁은 컨테이너에 두지 않는다 | `packages/react/src/styles.css` `.hjm-calendar__grid` |
102
104
 
103
105
  ## 꼭 지킬 것
@@ -107,6 +109,10 @@ import { DateRangePicker } from "@hjmds/react-native/date-range";
107
109
  - `rangeLabels`와 셀 이름(`composeAccessibleName`)은 i18n 키로 만든다. 구간 위치 접미사는 컴포넌트가
108
110
  셀 이름 뒤에 `, `로 붙인다.
109
111
  - 달을 넘기면 제품이 새 달의 `cells`와 `monthLabel`을 다시 만들어 넘긴다.
112
+ - 필터처럼 고른 기간을 바로 반영하는 자리에서는 `collapsible`과 표준 프리셋(1일·1주일·1개월·3개월·전체)을 쓰고,
113
+ 빈 값(전체)을 기본으로 둔다. [전체·오늘·기간 지정] 같은 별도 탭을 위에 겹쳐 두지 않는다. 프리셋과 같은 선택을 두 번 한다.
114
+ 2026-10-28 사용자 요청: 다에리 만난 동물 필터에서 기간을 고른 뒤에도 달력이 늘 펼쳐져 목록을 밀어냈고, 탭과 프리셋이 겹쳤다.
115
+ - 적용 버튼이 있는 시트에서는 `all` 칩 대신 `BottomCTA` 보조 행동(전체 날짜)을 둔다. 적용 버튼은 완성된 구간에서만 켜진다.
110
116
 
111
117
  ## 플랫폼 차이
112
118
 
@@ -1,12 +1,14 @@
1
1
  # DrawingEditor
2
2
 
3
3
  - 단계: 컴포넌트
4
- - 상태: 실험
4
+ - 상태: 배포
5
5
  - 지원: Web · Native
6
- - 적용: 미게시(1.48.0 이후)
6
+ - 적용: 1.49.0
7
7
  - 검토일: 2026-10-27
8
8
  - 근거: 다에리와 스핀트의 프로필 손그림 편집기가 복제돼 도구·이력·레이아웃이 갈라지는 것을 막으라는 사용자 요청
9
- - 스토리북: `실험/컴포넌트/입력/손그림 편집기`
9
+ - 스토리북: `배포/컴포넌트/입력/손그림 편집기`
10
+
11
+ 1.49.0에는 beta로 처음 게시됐으며 stable 판정은 1.50.0부터 적용된다.
10
12
 
11
13
  ## 언제 쓰나
12
14
 
@@ -117,7 +119,7 @@ Web는 pointer capture와 `touch-action: none`을 사용한다. 첫 터치/포
117
119
 
118
120
  다에리 Native·Web, 스핀트 Native가 이 컴포넌트를 소비한다. 제품의 중복 캔버스·도구·이력 구현과 그 전용 계약 테스트는 제거하고
119
121
  공통 계약/renderer 테스트로 옮긴다. 제품 테스트는 계정·업로드 실패·탐색 연결을 계속 확인한다.
120
- 소스 연결 개발에서만 제공되는 후보이며 npm 게시·스토어 반영·실기기 검증과 구분한다.
122
+ 공개 패키지 설치, 제품 채택과 실기기 검증은 각각 확인한다. 캔버스 획 입력은 포인터·터치 전용이며 키보드로 그리는 기능은 제공하지 않는다.
121
123
 
122
124
  ## 플랫폼 차이
123
125
 
@@ -80,14 +80,13 @@ import { NotificationBell } from "@hjmds/react-native/notification-bell";
80
80
  ### 아이콘 링크 — `IconButtonLink` (2026-10-23, 개발 소스·미게시)
81
81
 
82
82
  Button에 대한 ButtonLink처럼 IconButton 틀(`tone`·`size`·`shape`·`edge`·`hitSlop`·`layoutStyle`)을 이동 링크에 쓴다.
83
- 토글·로딩·변경 행동은 IconButton에 남는다. `current`는 지금 보고 있는 화면의 진입점이며 선택된 IconButton과 같은 면
84
- (`iconButtonRecipe.states.selected`)으로 칠하고 Web `aria-current="page"`, Native `accessibilityState.selected`로 알린다.
83
+ 토글·로딩·변경 행동은 IconButton에 남는다. `current`는 지금 보고 있는 화면의 진입점이며 선택된 IconButton과 같이 기존 tone의 배경을 유지하고 Web `aria-current="page"`, Native `accessibilityState.selected`로 알린다.
85
84
 
86
85
  | prop | Web | Native | 기본값 |
87
86
  | --- | --- | --- | --- |
88
87
  | 목적지 | `href: string` | `destination: LinkDestination` | 필수 |
89
88
  | 이름 | `label: string`(접근성 이름, 빈 값이면 `TypeError`) | 같음 | 필수 |
90
- | `current` | `boolean` → `aria-current="page"` + 선택 면 | `boolean` → `selected` + 선택 면 | `false` |
89
+ | `current` | `boolean` → `aria-current="page"` + 기존 배경 유지 | `boolean` → `selected` + 기존 배경 유지 | `false` |
91
90
  | `disabled` | `href`를 지우고 클릭·가운데 클릭을 막는다(ButtonLink와 같다) | 누름을 막는다 | `false` |
92
91
  | router 연결 | `renderAnchor(props)` — `ref`·`href`·이름·`aria-current`·차단 핸들러가 담긴 props를 자기 링크에 펼친다 | `onNavigate(destination)` 또는 `renderLink(props)`(TopBarAction `intent="link"`와 같다). 둘 다 없으면 `TypeError` | — |
93
92
 
@@ -124,7 +123,7 @@ Client Component에서 연결한다.
124
123
  | `size` | `small` 36 · `medium` 44 · `large` 52 | `medium` | `small`은 터치 여백 4 추가. 아이콘 크기는 각각 `sm`·`md`·`lg` |
125
124
  | `shape` | `rounded` · `circle` | `rounded` | — |
126
125
  | `elevation` | `none` · `raised` · `floating` · `overlay` | `none` | 떠 있는 컨트롤(번뚝 Dock 가운데 버튼)의 그림자 토큰 단계(`iconButtonRecipe.elevation`). `primary`·`secondary`·`danger`만 받고 `ghost`에 주면 `TypeError`. Web은 `--hjm-shadow-<단계>`, Native는 iOS 그림자 + Android elevation. `IconButtonLink`에는 없다(미게시, 2026-10-23) |
127
- | `selected` | `true` · `false` | — | 주면 토글이 된다(Web `aria-pressed`, Native 접근성 state) |
126
+ | `selected` | `true` · `false` | — | 주면 토글이 된다(Web `aria-pressed`, Native 접근성 state). 선택 전후 tone 배경은 유지한다 |
128
127
  | `edge` | `start` · `end` · `both` | — | 버튼이 놓인 컨테이너 가장자리(논리 방향, RTL에서 뒤집힘). `ghost`면 터치 영역은 그대로 두고 그 쪽으로 (보이는 지름 − 아이콘 칸) / 2만큼 내민다(`medium` 10, `small` 8, `large` 12, `resolveIconButtonEdgeOverhang`). 테두리·배경이 있는 tone은 틀이 정렬 기준이라 움직이지 않는다 |
129
128
  | `loading` | `true` · `false` | — | 누름을 막고 스피너로 바꾼다. Native는 기본으로 포커스를 유지한다(`disableWhileLoading`으로만 옛 동작) |
130
129
  | `loading` 중 `renderLoadingIndicator`(Native) | `(props: { color: string; size: "small" }) => ReactNode` | 기본 스피너 | 스피너를 바꿀 때만 |
@@ -187,8 +186,7 @@ TopBar
187
186
  `tone="inverse"`를 준다. 다른 아이콘 라이브러리의 컴포넌트를 직접 넣으면(Native) 색을 제품이 넘겨야 한다.
188
187
  - Native `IconButton`의 `style`은 제거되었다. 배치는 `layoutStyle`, 외형은 `tone`·`size`·`shape`로 지정한다.
189
188
 
190
- 2026-10-10 사용자 요청: Button과 같은 선택 표현을 쓴다. 선택은 bg 바탕·contentBrand 아이콘으로
191
- 표시하고 추가 선택 테두리는 없다. aria-pressed/Native selected와 키보드 focus outline은 유지한다.
189
+ 2026-10-28 사용자 지시: **선택해도 배경색은 바꾸지 않는다.** Button·IconButton·현재 경로인 IconButtonLink는 원래 tone의 배경과 테두리를 유지한다. ghost는 선택 후에도 투명하며 primary·danger는 원래 채움과 대비되는 글자색을 유지한다. 선택은 글자·아이콘의 전경 표현 또는 채운 glyph로 표시한다. Web `aria-pressed`/`aria-current`, Native `accessibilityState.selected`와 키보드 focus outline을 유지한다. 스핀트 꽃 반응 주변에 선택 면이 중복으로 생긴 사례가 계기이며 2026-10-10의 선택 배경 지침을 대체한다. 제품별 접근성 상태 우회나 배경 덮어쓰기를 추가하지 않고 `selected`를 그대로 쓴다.
192
190
 
193
191
  ## 근거
194
192
 
@@ -199,3 +197,5 @@ TopBar
199
197
  컴포넌트마다 오프셋을 따로 두지 않고 이 한 규칙과 계약 값으로 맞췄다. Toast 닫기(같은 날 `toastRecipe.close.inlineOverhang`)와 같은 식이다.
200
198
  - 도구성 행동은 IconButton: 2026-10-17 사용자 지시 — '나의 기록' 헤더의 새로고침·새 기록 글자 버튼.
201
199
  - 선택 표현: 2026-10-10 사용자 요청 — Button과 같은 선택 표현을 쓴다.
200
+
201
+ 공개 recipe 직접 소비 시 `states.selected.background`·`border` 대신 `tones[tone].background`·`border`를 읽는다. 선택 recipe는 전경만 소유한다. 이 recipe 필드 제거는 다음 게시 시 contracts의 major 변경으로 기록하며 컴포넌트의 `selected`·`current` 사용법은 바꾸지 않는다.
@@ -11,7 +11,7 @@
11
11
  ## 언제 쓰나
12
12
 
13
13
  `react-native-keyboard-controller`를 설치한 앱에서 화면 하단에 고정된 행동(BottomCTA, 채팅 입력창)이
14
- 키보드와 함께 위아래로 움직이게 할 때 쓴다(Native 전용, 별도 보조 기능. API 성숙도는 실험적 어댑터). 내부는 `KeyboardStickyView`이며,
14
+ 키보드와 함께 위아래로 움직이게 할 때 쓴다(Native 전용, 별도 보조 기능. 문서화한 선택 peer 계약은 stable). 내부는 `KeyboardStickyView`이며,
15
15
  여백을 바꾸는 대신 키보드 움직임을 따라 translate 한다.
16
16
 
17
17
  ## 쓰지 않을 때
@@ -107,4 +107,4 @@ const keyboardOpen = useKeyboardState((state) => state.isVisible);
107
107
 
108
108
  - sticky 좌표는 창 기준이다. Storybook 캔버스처럼 아래에 다른 막대가 있는 host에서는 위치가 어긋나므로
109
109
  쇼케이스도 앱 크기 host에서 확인한다.
110
- - API 성숙도가 실험적 어댑터다(2026-10-06 Storybook 배포와 별개, [근거](../../optional-adapters.md#evidence-and-promotion)). 실제 키보드 애니메이션은 mock 테스트로 검증되지 않는다. 개발 클라이언트로 확인한다.
110
+ - 문서화한 선택 peer 계약은 stable이다([근거](../../optional-adapters.md#current-support--2026-10-26)). 실제 키보드 애니메이션은 mock 테스트로 검증되지 않는다. 개발 클라이언트로 확인한다.
@@ -11,7 +11,7 @@
11
11
  ## 언제 쓰나
12
12
 
13
13
  입력 필드가 여러 개인 세로 스크롤 폼(가입, 프로필 수정, 주소 입력)에서 포커스된 필드가 키보드에
14
- 가려지지 않게 스크롤해 줄 때 쓴다(Native 전용, 별도 보조 기능. API 성숙도는 실험적 어댑터). 내부는 `react-native-keyboard-controller`의
14
+ 가려지지 않게 스크롤해 줄 때 쓴다(Native 전용, 별도 보조 기능. 문서화한 선택 peer 계약은 stable). 내부는 `react-native-keyboard-controller`의
15
15
  `KeyboardAwareScrollView`이고, `keyboardShouldPersistTaps="handled"`로 고정돼 키보드가 열린 채 버튼을 눌러도 탭이 전달된다.
16
16
 
17
17
  ## 쓰지 않을 때
@@ -92,4 +92,4 @@ import { spacing } from "@hjmds/design-contracts/foundations";
92
92
  좌우 여백까지 스크롤 컨테이너에 직접 준다. 규칙은 세로 여백만 스크롤에, 좌우는 Container `gutter`다.
93
93
 
94
94
  - `refreshControl`, `onScroll` 같은 다른 ScrollView prop은 타입에서 빠져 있어 넘길 수 없다.
95
- - API 성숙도가 실험적 어댑터다(2026-10-06 Storybook 배포와 별개, [근거](../../optional-adapters.md#evidence-and-promotion)). 포커스 이동·키보드 애니메이션은 개발 클라이언트로 기기에서 확인한다.
95
+ - 문서화한 선택 peer 계약은 stable이다([근거](../../optional-adapters.md#current-support--2026-10-26)). 포커스 이동·키보드 애니메이션은 개발 클라이언트로 기기에서 확인한다.
@@ -11,7 +11,7 @@
11
11
  ## 언제 쓰나
12
12
 
13
13
  `@hjmds/react-native/keyboard-controller` 어댑터(KeyboardDock, KeyboardFormScrollView)를 쓰는 앱의
14
- 루트에 **한 번** 설치한다(Native 전용, 별도 보조 기능. API 성숙도는 실험적 어댑터). 내부는 `react-native-keyboard-controller`의
14
+ 루트에 **한 번** 설치한다(Native 전용, 별도 보조 기능. 문서화한 선택 peer 계약은 stable). 내부는 `react-native-keyboard-controller`의
15
15
  `KeyboardProvider`이며, 어댑터를 준비하려고 OS 키보드를 미리 띄우지 않도록 `preload={false}`로 고정돼 있다.
16
16
 
17
17
  ## 쓰지 않을 때
@@ -83,4 +83,4 @@ import { KeyboardMotionProvider } from "@hjmds/react-native/keyboard-controller"
83
83
  - tsc·mock 테스트 통과는 peer 설치와 native 연결의 근거가 아니다. 다른 optional subpath
84
84
  (celebration·effect-surface·qr-code·thinking-orb·toast-liquid)에서 앱에 없는 peer 때문에
85
85
  기기 Metro에서만 크래시가 난 사례가 있다(2026-10). 개발 클라이언트로 실제 화면을 띄워 확인한다.
86
- - 이 어댑터의 API 성숙도는 실험이다(2026-10-06 Storybook 배포와 별개). 기기·보조기술 검증은 아직 없다([근거](../../optional-adapters.md#evidence-and-promotion)).
86
+ - 문서화한 선택 peer 계약은 stable이다. 소비 기기의 키보드·보조기술 검증은 별도로 확인한다([근거](../../optional-adapters.md#current-support--2026-10-26)).
@@ -50,7 +50,7 @@ import { ProfileSummary } from '@hjmds/react-native/screen-flows';
50
50
  | badge | ReactNode | 없음 | 이름 옆 배지 |
51
51
  | nameActions | ReactNode | 없음 | 이름 행의 행동 |
52
52
  | nameEdit | `{label,onAction,disabled?,pending?}` | 없음 | 이름 옆 편집(연필) 아이콘 버튼. 내 프로필에서 닉네임 바꾸기를 연다(2026-10-25, 아래 절) |
53
- | stats | `{id,value,label,onPress?,disabled?}[]` | `[]` | 통계 수치. `onPress`가 있으면 버튼 |
53
+ | stats | `{id,value,label,onPress?,disabled?}[]` | `[]` | 통계 수치. `onPress`가 있으면 버튼. 한 줄 최대 4칸(`profileSummaryRecipe.stats.maxPerRow`), 넘치면 다음 줄로 가고 마지막 줄도 같은 칸 폭이다 |
54
54
  | actionsLayout | `inline` · `stacked` · `equal` | `inline` | 가로 보조 행동은 inline 줄바꿈 행, 세로 전체 폭은 stacked, 같은 폭의 가로 행동은 equal(개발 소스) |
55
55
  | actions | ReactNode | 없음 | 수치 아래 행동 줄. [ProfileScreen](profile-screen.md) `editPlacement="summary"` 안에서는 그 화면의 `edit`이 이 줄 맨 앞에 `secondary` `small` 버튼(`profileSummaryRecipe.editAction`)으로 먼저 들어간다(1.31.0) |
56
56
  | children | ReactNode | 없음 | 하단 footer |
@@ -62,7 +62,7 @@ import { ProfileSummary } from '@hjmds/react-native/screen-flows';
62
62
  | 항목 | 값 | 근거 |
63
63
  | --- | --- | --- |
64
64
  | 크기 | 원형 아바타 프레임 48. ProfileScreen 수정 행동은 `Button` `secondary` `small` | `profileSummaryRecipe.avatarSize`·`editAction` |
65
- | 간격 | 수치 칸 사이 `spacing.md` 16, 값·라벨 사이 `spacing.xxs` 4, 행동 사이 `spacing.xs` 8. 영역 간격은 [CompositionLayout](composition-layout.md) | 양 renderer |
65
+ | 간격 | 수치 칸 사이·수치 줄 사이 `spacing.md` 16, 값·라벨 사이 `spacing.xxs` 4, 행동 사이 `spacing.xs` 8. 영역 간격은 [CompositionLayout](composition-layout.md) | 양 renderer |
66
66
  | 순서·정렬 | 머리(아바타 → 이름·배지 → nameActions) → 수치 줄 → actions → children, 수치 칸은 균등 폭·가운데 | `CompositionLayout` + `flex: 1` 칸 |
67
67
  | 고정·스크롤 | 없음. ProfileScreen 본문 스크롤 안에 놓인다 | — |
68
68
  | 좁은 폭·큰 글자 | 수치 칸은 `minWidth: 0`으로 나눠 갖고, 행동 줄은 `wrap` | 양 renderer |
@@ -163,3 +163,11 @@ Chip/직접 wrap으로 조립하고 있었다. `presentation="chips"`는 기존
163
163
  ### 그림 미리보기와 기본 선택 표시
164
164
 
165
165
  2026-10-20 다에리 반응 카드 이관: Web도 Native와 같이 `indicator="none"`으로 기본 원형 표시를 숨길 수 있다(로컬·미게시). `renderLabel`의 비상호작용 그림/문구와 공통 카드의 선택 색으로 상태를 나타낼 때 사용한다. 실제 radio·그룹 이름·checked·키보드 선택·비활성 계약은 유지되며, 기본값 `default`는 기존 원형 표시를 유지한다. 제품에서 카드 테두리/눌림/선택 틀을 다시 만들지 않는다.
166
+
167
+ ### 가로 스크롤 카드 (2026-10-28 개발 소스)
168
+
169
+ 오늘야구의 일자 카드 요청으로 `overflow="scroll"`을 Web/Native RadioGroup에 추가했다.
170
+ `presentation="card"` 또는 `"chips"`에서 `grid` 없이 사용하며 기본 `wrap` 동작은 유지한다.
171
+ 공통 레일이 가로 스크롤과 선택 항목 노출을 소유한다. 제품은 `renderLabel`로 요일·일자 등
172
+ 콘텐츠만 공급한다. 비활성인 선택 날짜도 표시할 수 있다. 조회 실패를 비활성 날짜로 바꾸지 않는다.
173
+ 이 API는 아직 게시하지 않은 개발 소스 변경이다.
@@ -1,12 +1,14 @@
1
1
  # ShareSheet
2
2
 
3
3
  - 단계: 컴포넌트
4
- - 상태: 실험
4
+ - 상태: 배포
5
5
  - 지원: Web · Native
6
- - 적용: 미게시(1.47.0 이후)
6
+ - 적용: 1.48.0
7
7
  - 검토일: 2026-10-26
8
8
  - 근거: `src/share-sheet.ts`의 `shareSheetRecipe`·`resolveShareSheetSendOutcome`·`filterShareSheetRecipients`; Web `src/share-sheet.tsx`, Native `src/share-sheet.tsx`
9
- - 스토리북: `실험/컴포넌트/오버레이/공유 시트`
9
+ - 스토리북: `배포/컴포넌트/오버레이/공유 시트`
10
+
11
+ 1.48.0부터 제공하며 stable 지원은 1.50.0부터 적용된다.
10
12
 
11
13
  ## 언제 쓰나
12
14
 
@@ -123,8 +123,8 @@ import { Container, Stack, Text } from "@hjmds/react/layout";
123
123
  ```
124
124
 
125
125
  ```tsx
126
- // Native: 단일 펼침만. ContextMenu·Menubar는 Web 전용이고, 길게 누르기 메뉴는 실험적
127
- // `NativeContextMenu`(`/context-menu-native`)가 있다.
126
+ // Native: 단일 펼침만. ContextMenu는 `/context-menu`에 있고 Menubar는 Web 전용이다.
127
+ // OS 메뉴가 필요하면 선택형 `NativeContextMenu`(`/context-menu-native`)를 쓴다.
128
128
  import { Collapsible } from "@hjmds/react-native/collapsible";
129
129
  import { Text } from "@hjmds/react-native/primitives";
130
130
 
@@ -140,7 +140,7 @@ import { Text } from "@hjmds/react-native/primitives";
140
140
  | 항목 | Web | Native |
141
141
  | --- | --- | --- |
142
142
  | `Collapsible` | `/collapsible`, 배치는 `layoutStyle`(또는 `className`) | `/collapsible`, 배치는 `layoutStyle`(`style`은 제거된 속성) |
143
- | 대상 메뉴 | `ContextMenu`(stable) | `NativeContextMenu`(실험적 어댑터) |
143
+ | 대상 메뉴 | `ContextMenu`(stable) | `NativeContextMenu`(선택형 stable 어댑터) |
144
144
  | `Menubar` | 있음 | 없음 |
145
145
 
146
146
  ## 함정
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hjmds/design-contracts",
3
- "version": "1.49.0",
3
+ "version": "2.0.0",
4
4
  "description": "Renderer-neutral design contracts, tokens, recipes, and behaviors shared by HJM products.",
5
5
  "license": "MIT",
6
6
  "repository": {