@hjmds/design-contracts 1.34.0 → 1.35.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/anchor.d.ts +4 -1
- package/dist/anchor.d.ts.map +1 -1
- package/dist/anchor.js +3 -3
- package/dist/anchor.js.map +1 -1
- package/dist/base-recipes.d.ts +15 -1
- package/dist/base-recipes.d.ts.map +1 -1
- package/dist/base-recipes.js +14 -3
- package/dist/base-recipes.js.map +1 -1
- package/dist/behaviors.d.ts +22 -0
- package/dist/behaviors.d.ts.map +1 -1
- package/dist/behaviors.js +2 -0
- package/dist/behaviors.js.map +1 -1
- package/dist/calendar.d.ts +9 -7
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js +31 -2
- package/dist/calendar.js.map +1 -1
- package/dist/card.d.ts +15 -1
- package/dist/card.d.ts.map +1 -1
- package/dist/card.js +14 -1
- package/dist/card.js.map +1 -1
- package/dist/carousel.d.ts +3 -0
- package/dist/carousel.d.ts.map +1 -1
- package/dist/carousel.js +10 -1
- package/dist/carousel.js.map +1 -1
- package/dist/catalog.d.ts +147 -17
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +9 -6
- package/dist/catalog.js.map +1 -1
- package/dist/colors.d.ts +1 -0
- package/dist/colors.d.ts.map +1 -1
- package/dist/colors.js +2 -0
- package/dist/colors.js.map +1 -1
- package/dist/component-recipes.d.ts +45 -11
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +35 -15
- package/dist/component-recipes.js.map +1 -1
- package/dist/component-references.d.ts +2 -2
- package/dist/component-references.js +1 -1
- package/dist/component-references.js.map +1 -1
- package/dist/design-profile.d.ts +14 -0
- package/dist/design-profile.d.ts.map +1 -1
- package/dist/design-profile.js +15 -0
- package/dist/design-profile.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/progress-recipe.d.ts +6 -0
- package/dist/progress-recipe.d.ts.map +1 -1
- package/dist/progress-recipe.js +15 -2
- package/dist/progress-recipe.js.map +1 -1
- package/dist/recipes.d.ts +2 -2
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +2 -2
- package/dist/recipes.js.map +1 -1
- package/dist/screen-patterns.d.ts +32 -2
- package/dist/screen-patterns.d.ts.map +1 -1
- package/dist/screen-patterns.js +26 -2
- package/dist/screen-patterns.js.map +1 -1
- package/dist/semantic-colors.d.ts +5 -0
- package/dist/semantic-colors.d.ts.map +1 -1
- package/dist/semantic-colors.js +2 -0
- package/dist/semantic-colors.js.map +1 -1
- package/dist/sheet.d.ts +15 -0
- package/dist/sheet.d.ts.map +1 -1
- package/dist/sheet.js +11 -0
- package/dist/sheet.js.map +1 -1
- package/dist/shortcut-rail.d.ts +20 -0
- package/dist/shortcut-rail.d.ts.map +1 -0
- package/dist/shortcut-rail.js +28 -0
- package/dist/shortcut-rail.js.map +1 -0
- package/dist/showcase.js +2 -2
- package/dist/showcase.js.map +1 -1
- package/dist/steps.d.ts +6 -4
- package/dist/steps.d.ts.map +1 -1
- package/dist/steps.js +3 -0
- package/dist/steps.js.map +1 -1
- package/dist/time-picker.d.ts +70 -0
- package/dist/time-picker.d.ts.map +1 -0
- package/dist/time-picker.js +51 -0
- package/dist/time-picker.js.map +1 -0
- package/dist/timeline.d.ts +13 -1
- package/dist/timeline.d.ts.map +1 -1
- package/dist/timeline.js +11 -1
- package/dist/timeline.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/calendar.md +19 -1
- package/docs/carousel.md +10 -0
- package/docs/collapsible.md +9 -0
- package/docs/consumer-policy.md +8 -12
- package/docs/design-profile.md +9 -0
- package/docs/expansion-roadmap.md +1 -1
- package/docs/generated/component-maturity.md +5 -4
- package/docs/generated/renderer-evidence.json +141 -16
- package/docs/generated/renderer-evidence.md +5 -3
- package/docs/generated/showcase-manifest.json +69 -7
- package/docs/optional-adapters.md +11 -0
- package/docs/stable-core.md +25 -0
- package/docs/steps.md +4 -0
- package/docs/theme-palette.md +2 -0
- package/docs/time-picker.md +22 -114
- package/docs/usage/README.md +9 -7
- package/docs/usage/SELECTION.md +44 -2
- package/docs/usage/components/accordion.md +4 -2
- package/docs/usage/components/alert-dialog.md +5 -0
- package/docs/usage/components/anchor.md +4 -1
- package/docs/usage/components/badge.md +7 -4
- package/docs/usage/components/bottom-cta.md +29 -4
- package/docs/usage/components/bottom-navigation.md +8 -0
- package/docs/usage/components/button.md +42 -2
- package/docs/usage/components/calendar.md +8 -1
- package/docs/usage/components/card.md +111 -7
- package/docs/usage/components/carousel.md +4 -1
- package/docs/usage/components/chat-screen.md +1 -1
- package/docs/usage/components/checkbox-group.md +33 -3
- package/docs/usage/components/checkbox.md +8 -2
- package/docs/usage/components/chip.md +5 -0
- package/docs/usage/components/collapsible.md +16 -1
- package/docs/usage/components/comment-thread-screen.md +12 -3
- package/docs/usage/components/composition-layout.md +6 -1
- package/docs/usage/components/conversation-row.md +2 -1
- package/docs/usage/components/data-table.md +10 -0
- package/docs/usage/components/description-list.md +1 -0
- package/docs/usage/components/field.md +8 -0
- package/docs/usage/components/icon-tile.md +29 -10
- package/docs/usage/components/image-choice-group.md +6 -0
- package/docs/usage/components/image.md +8 -1
- package/docs/usage/components/list-detail-screen.md +10 -4
- package/docs/usage/components/list-row.md +3 -0
- package/docs/usage/components/list.md +33 -1
- package/docs/usage/components/message-composer.md +2 -0
- package/docs/usage/components/notice.md +5 -1
- package/docs/usage/components/notification-row.md +19 -9
- package/docs/usage/components/onboarding-screen.md +3 -0
- package/docs/usage/components/page-indicator.md +12 -0
- package/docs/usage/components/profile-summary.md +16 -3
- package/docs/usage/components/progress.md +24 -0
- package/docs/usage/components/radio-group.md +45 -3
- package/docs/usage/components/radio.md +8 -2
- package/docs/usage/components/repeatable-field-group.md +1 -0
- package/docs/usage/components/screen-layout.md +46 -3
- package/docs/usage/components/search-screen.md +29 -1
- package/docs/usage/components/segmented-control.md +19 -3
- package/docs/usage/components/select.md +2 -0
- package/docs/usage/components/sheet.md +56 -3
- package/docs/usage/components/spinner.md +10 -0
- package/docs/usage/components/statistic.md +23 -0
- package/docs/usage/components/steps.md +11 -4
- package/docs/usage/components/switch.md +5 -0
- package/docs/usage/components/tabs.md +4 -0
- package/docs/usage/components/time-picker.md +88 -0
- package/docs/usage/components/timeline.md +19 -6
- package/docs/usage/components/top-bar.md +7 -0
- package/docs/usage/compositions/shortcut-rail.md +99 -0
- package/docs/usage/compositions/time-selection.md +5 -0
- package/package.json +17 -4
package/docs/time-picker.md
CHANGED
|
@@ -1,114 +1,22 @@
|
|
|
1
|
-
# TimePicker
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
`
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Collection
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
- 선택적 `disabled`(예: 영업시간 밖 시각을 회색 처리)
|
|
24
|
-
- `none|single` 선택 모드, 정적 목록(비동기 상태 불필요)
|
|
25
|
-
|
|
26
|
-
인 **정확히 `CollectionItemDescriptor` 하나**다. 그리고 "트리거 + 적응형 오버레이(Web
|
|
27
|
-
popover / Native Sheet) + 단일 committed key + disabled 항목을 건너뛰는 예측 가능한
|
|
28
|
-
방향키 이동"은 이미 `Select`가 `beta`로 검증한 계약 그대로다. 즉:
|
|
29
|
-
|
|
30
|
-
**시 Select 하나 + 분 Select 하나 = TimePicker.** 제품이 두 값을 `"HH:mm"` 문자열로
|
|
31
|
-
합치기만 하면 antd `TimePicker`가 푸는 사용자 문제(하루 중 시각 하나를 고른다)는 이미
|
|
32
|
-
완결된다. 새 recipe도, 새 behavior도, 새 상태 축도 필요하지 않다 — Dropdown이
|
|
33
|
-
`Menu`로 완전히 흡수된 것과 같은 자리다: 격자처럼 여기서만 필요한 새 조각
|
|
34
|
-
(Calendar의 `row`/`column`/`overflow` 같은)이 하나도 없다.
|
|
35
|
-
|
|
36
|
-
## 실사용처 확인
|
|
37
|
-
|
|
38
|
-
Yajalal 전체(`날짜/시간 선택`, `TimePicker`, 알림 설정 화면 `NotificationSettingsScreen.tsx`
|
|
39
|
-
등)를 검색했지만 하루 중 시각 하나를 고르는 UI 자체가 어디에도 없다. 측정된 요구가
|
|
40
|
-
없다는 점에서도 지금 새 계약을 여는 것을 정당화할 근거가 없다.
|
|
41
|
-
|
|
42
|
-
## 왜 압축된 단일 트리거(진짜 antd 형태)를 만들지 않았는가
|
|
43
|
-
|
|
44
|
-
두 개의 Select를 나란히 두는 것과 antd처럼 **트리거 하나 + 팝업 하나 안에 두 열이
|
|
45
|
-
동기화된 형태**는 시각적으로 다르다. 후자를 만들려면:
|
|
46
|
-
|
|
47
|
-
- 하나의 열림 상태가 두 열을 동시에 제어해야 하고,
|
|
48
|
-
- 트리거의 표시 문자열이 두 committed key를 조합해야 하며(Statistic/Calendar와 같은
|
|
49
|
-
"제품이 포맷한 문자열을 받는다" 원칙),
|
|
50
|
-
- **"selection"이 더 이상 자동 닫힘 사유가 아니어야 한다** — 시만 고르고 분을 아직
|
|
51
|
-
안 골랐는데 닫히면 안 되기 때문이다(DatePicker/Select는 값 하나를 고르면 바로
|
|
52
|
-
닫히지만, 여기서는 두 값이 다 맞아떨어져야 "완료"다).
|
|
53
|
-
|
|
54
|
-
이 세 가지는 실제로 작겠지만 **새로운 축**이다 — Select의 트리거/오버레이/커밋 계약을
|
|
55
|
-
그대로 복제하면서 "selection은 안 닫는다"는 예외 하나만 얹는 얇은 wrapper가 된다.
|
|
56
|
-
지금은 이 압축 형태를 요구하는 화면이 없으므로, 순전히 미관상의 이유로 그 wrapper를
|
|
57
|
-
먼저 만들지 않는다 — 로드맵의 "측정된 요구가 나타나면 그때 기본을 재검토한다" 원칙과
|
|
58
|
-
`docs/notification.md`/`docs/dropdown.md`가 이미 세운 관례를 그대로 따른다.
|
|
59
|
-
|
|
60
|
-
## 제품이 지금 composing할 때 지킬 것 (판정이 이대로 유지되는 동안)
|
|
61
|
-
|
|
62
|
-
- 값은 각 Select의 committed key를 그대로 쓰되, 최종 시각은 **문자열** `"HH:mm"`
|
|
63
|
-
(24시간제, 앞자리 0)로 합성한다. `Date` 객체를 어디에도 들이지 않는다 — DatePicker와
|
|
64
|
-
같은 이유(시간대·서머타임을 이 패키지가 가지면 안 된다).
|
|
65
|
-
- **초 단위는 넣지 않는다.** 측정된 요구가 없고, 두 Select를 세 Select로 늘리는 것도
|
|
66
|
-
같은 근거로 보류한다.
|
|
67
|
-
- **12시간제(오전/오후)는 넣지 않는다.** 24시간제 값 위에 표시만 로케일별로 다르게
|
|
68
|
-
하고 싶다면 그건 제품이 표시 문자열을 포맷하는 몫이다(Statistic 원칙) — 값 자체에
|
|
69
|
-
`period` 축을 추가하지 않는다.
|
|
70
|
-
|
|
71
|
-
## 판정이 뒤집힐 조건
|
|
72
|
-
|
|
73
|
-
실제 화면이 **트리거 하나 + 팝업 하나**의 압축된 형태를 구체적으로 요구하면(예: 경기
|
|
74
|
-
알림 시각 설정처럼 공간이 좁은 자리), 그때 얇은 `time-picker.ts`를 연다. 그 경우 권장
|
|
75
|
-
설계:
|
|
76
|
-
|
|
77
|
-
- `hours`/`minutes` 각 열은 `collection.ts`의 `CollectionItemDescriptor`/
|
|
78
|
-
`validateCollection`/`getCollectionNavigationTarget`/`getCollectionTypeaheadMatch`를
|
|
79
|
-
**그대로 재사용**한다 — 여기서 재정의할 것이 없다.
|
|
80
|
-
- 트리거/오버레이 축(`open`/`defaultOpen`/`onOpenChange`, 라벨)은 `DatePickerOpenState`/
|
|
81
|
-
`DatePickerLabel`과 같은 모양으로 새 파일에 다시 선언한다(자급자족 원칙 — DatePicker가
|
|
82
|
-
`collection.ts`의 Select 타입을 그대로 가져오지 않고 같은 모양을 다시 선언한 것과
|
|
83
|
-
같은 이유).
|
|
84
|
-
- `dismiss`에서 `"selection"`을 뺀다 — 여기서만 다른 예외이므로 반드시 문서에
|
|
85
|
-
남긴다. 대신 명시적 `"confirm"` 사유를 추가한다.
|
|
86
|
-
- 초·12시간제 축은 이 조건이 왔다고 해서 자동으로 열리지 않는다 — 그 축은 별도로
|
|
87
|
-
다시 측정한다.
|
|
88
|
-
|
|
89
|
-
## 배선 명세 (리드 참고)
|
|
90
|
-
|
|
91
|
-
catalog의 `{ name: "TimePicker", category: "input", platform: "adaptive", status:
|
|
92
|
-
"planned" }`(`src/catalog.ts:69`) 행은 바꿀 것이 없다 — recipe/behavior가 원래 없었고,
|
|
93
|
-
지금도 없다. `src/time-picker.ts`, `test/time-picker.test.ts`는 만들지 않았다.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
## 2026-09-16: React/RN 작동 조합
|
|
97
|
-
|
|
98
|
-
명시 요청에 따라 위 합성 경로를 양쪽 Showcase의 `패턴/시간 선택`에 구현했다.
|
|
99
|
-
시 00–23와 분 00–59의 독립 Select, 두 값이 있어야 가능한 확정, 초기화, 변경 시 확정 결과 해제를
|
|
100
|
-
포함한다. 새 TimePicker renderer를 선언하지 않고 기존 공개 `Select`를 그대로 조합한다.
|
|
101
|
-
|
|
102
|
-
- Web API: `@hjmds/react/forms`의 `Select`, `@hjmds/react/actions`의 `Button`.
|
|
103
|
-
- Native API: `@hjmds/react-native/forms`의 `Select`, `@hjmds/react-native/actions`의 `Button`.
|
|
104
|
-
- [Web 예제](../../../showcase/web/src/patterns/TimeSelection.stories.tsx)와
|
|
105
|
-
[Native 예제](../../../showcase/native/src/TimeSelection.stories.tsx)는 제품이 가져갈 상태 연결을 보여준다.
|
|
106
|
-
- 두 값은 `selectedKey` / `onSelectionChange`로 통제하고 둘 다 non-null일 때 `HH:mm`으로 조합한다.
|
|
107
|
-
`00`도 유효한 선택이다. 시각을 실제 알림·서버 시각으로 바꾸는 작업은 제품 소유다.
|
|
108
|
-
- [Ant TimePicker](https://ant.design/components/time-picker/)의 명시 확정 흐름을 비교했다.
|
|
109
|
-
HJM 예제에서는 각 Select의 선택과 최종 시각 확정을 분리한다. 두 popup을 하나로 합치지 않는다.
|
|
110
|
-
- 320px 브라우저에서 시만 선택했을 때 확정 비활성, 23:59 확정, 초기화 후 비활성을 확인했다.
|
|
111
|
-
Native는 타입·Showcase 검사 범위이며 실제 기기 조작 확인은 별도다.
|
|
112
|
-
|
|
113
|
-
Catalog의 `planned + roadmap.composed`는 독립 renderer가 없다는 의미로 유지한다.
|
|
114
|
-
합성 예제 완료를 새 renderer 수나 stable 승격으로 더하지 않는다.
|
|
1
|
+
# TimePicker
|
|
2
|
+
|
|
3
|
+
2026-10-19: 사용자가 승인한 통합 열 선택 디자인으로 독립 컴포넌트를 제공한다.
|
|
4
|
+
이 결정은 과거의 ‘Select 두 개로 충분하므로 새 renderer를 만들지 않는다’ 판단을 대체한다.
|
|
5
|
+
선택의창에서 날짜 선택 시트 안에 다시 시·분 Sheet가 열리는 실제 요구가 생겼고,
|
|
6
|
+
두 열의 초안·확정·취소와 접근성 이동을 제품별로 조립하지 않도록 HJM으로 옮겼다.
|
|
7
|
+
|
|
8
|
+
- 계약과 recipe: `src/time-picker.ts`. Web/RN: `@hjmds/react/time-picker`, `@hjmds/react-native/time-picker`.
|
|
9
|
+
- `presentation="field"`(기본): 필드 한 개 → Web Popover / Native Sheet 한 개. 두 열 선택은 초안이며 확인 버튼으로만 `onValueChange`한다. 닫기·Escape·바깥 누름·시트 dismiss는 확정하지 않는다.
|
|
10
|
+
- `presentation="inline"`: 같은 두 열을 기존 폼·시트 안에 표시한다. 최종 확인은 바깥 폼이 소유한다. 각 선택에서 완전하고 사용 가능한 값은 `HH:mm`, 미완성·선택 불가 초안은 `null`로 전달한다. 부모가 null을 반영해도 사용 중인 부분 선택은 유지한다.
|
|
11
|
+
- 값은 `HH:mm | null`, 날짜·시간대·서머타임·현재 시각 계산은 제품 소유. 자정 `00:00`도 유효하다.
|
|
12
|
+
- `minuteStep` 기본 1. 60의 양의 정수 약수만 허용한다. 기존 값이 간격과 맞지 않아도 자동 반올림하지 않는다.
|
|
13
|
+
- `isTimeUnavailable(time)`은 제품이 제공한다. 시는 사용 가능한 분이 하나라도 있으면 선택할 수 있다. 선택한 분을 몰래 바꾸지 않으며, 그 조합이 불가하면 확인할 수 없다.
|
|
14
|
+
- 외부 `value` 변경은 초안에 반영한다. 비활성·읽기 전용으로 전환하면 열린 선택기를 닫고 확정하지 않는다.
|
|
15
|
+
- Web: 열마다 roving focus, 방향키·Home·End가 비활성 항목을 건너뛰며 선택한다. Enter/Space는 버튼 기본 동작. Popover가 초점·Escape·바깥/blur dismiss·복귀를 소유한다.
|
|
16
|
+
- Native: 각 값은 이름·checked/disabled 상태를 가진 radio이며 host 활성화를 사용한다. Sheet가 back·dismiss·safe area·복귀를 소유한다. 실제 VoiceOver/TalkBack 기기 검증과 host action 검사는 구분한다.
|
|
17
|
+
|
|
18
|
+
기존 Collection/Select는 단일 선택으로 닫히는 계약이다. TimePicker는 그 wrapper가 아니라 두 부분을 한 번에 확정하는 세션이다. 목록 값과 공통 Popover/Sheet/Button을 재사용하며, 날짜 엔진이나 새 native peer를 추가하지 않는다.
|
|
19
|
+
|
|
20
|
+
디자인 모티브: [Ant Design](https://ant.design/components/time-picker/)의 통합 열·확정,
|
|
21
|
+
[Material 3](https://m3.material.io/components/time-pickers/)의 큰 시각 표시.
|
|
22
|
+
색·모서리·간격은 HJM recipe와 소비 제품의 semantic palette를 따른다.
|
package/docs/usage/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
버튼 위치에서 시작해 구성·컴포넌트 지침으로 내려가고, 값은 토큰 지침에서 고른다.
|
|
7
7
|
[선택 기준](SELECTION.md)으로 그대로 사용·공개 슬롯 확장·공통 API 보완·제품 고유 기능 연결을 고른다. 설치 버전의 지침을 본다: `node_modules/@hjmds/design-contracts/docs/usage/`.
|
|
8
8
|
제공 기준은 확인한 기본 API 버전이며 최초 출시 버전이 아니다. 구성·화면은 각 API 지침의 제공 버전을 확인한다.
|
|
9
|
-
API 안정성은 1.
|
|
9
|
+
API 안정성은 1.34.0의 생성 manifest에서 읽은 정규(canonical) API 기준이다. 동반·선택형 확장은 개별 계약을 확인한다.
|
|
10
10
|
Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품 반영을 의미하지 않는다.
|
|
11
11
|
`분류`는 Storybook 제목 `<배포|실험>/<단계>/<분류>/<항목>`의 셋째 마디이고, 토큰·구성·화면은 Storybook 메뉴와 같은 순서다.
|
|
12
12
|
|
|
@@ -53,7 +53,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
53
53
|
| [Breadcrumb](components/breadcrumb.md) | 탐색 | Web의 깊은 계층 화면에서 현재 위치까지의 경로를 보여 주고 상위 계층으로 바로 돌아가게 할 때 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
|
|
54
54
|
| [Button](components/button.md) | 동작 | 사용자가 누르면 무언가가 일어나는 텍스트 행동에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
55
55
|
| [Calendar](components/calendar.md) | 데이터 표시 | 화면에 항상 펼쳐진 한 달 격자에서 날짜 하나를 고를 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
56
|
-
| [Card](components/card.md) | 데이터 표시 |
|
|
56
|
+
| [Card](components/card.md) | 데이터 표시 | 인용문과 출처를 묶으면 미게시 동반 API `QuoteCard`를 사용한다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
57
57
|
| [Carousel](components/carousel.md) | 데이터 표시 | 한 번에 카드 하나만 보이고 사용자가 순서대로 넘겨 보는 유한한 묶음에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
58
58
|
| [Celebration](components/celebration.md) | 구성/직접 조작과 모션 | 목표 달성, 첫 완료처럼 드물게 일어나는 성공 순간에 한 번 터지는 색종이 효과에 쓴다. | 1.12.1 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
59
59
|
| [Chart](components/chart.md) | 데이터 표시 | 수치의 흐름·비교·몫·모양·관계를 그림으로 읽게 할 때 쓴다. | 1.21.0 | Web · Native | Web beta / Native beta | 배포 |
|
|
@@ -102,7 +102,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
102
102
|
| [HistogramExplorer](components/histogram-explorer.md) | 구성/정보 표시 | 실제 관측값 배열의 분포를 동폭 구간 막대로 보여 주고, 사용자가 구간 수를 바꿔 볼 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
103
103
|
| [Icon](components/icon.md) | 글자와 아이콘 | HJM semantic 이름(`search`, `back`, `chevronEnd`, `notifications` 등 47개)으로 고르는 그림 기호에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
104
104
|
| [IconButton](components/icon-button.md) | 동작 | 보이는 글자 없이 아이콘만으로 표시하는 행동에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
105
|
-
| [IconTile](components/icon-tile.md) |
|
|
105
|
+
| [IconTile](components/icon-tile.md) | 데이터 표시 | 프로필·런처 격자의 아이콘 위/제목 아래 항목에 쓴다. | 1.26.0 | Web · Native | Web stable / Native stable | 배포 |
|
|
106
106
|
| [Image](components/image.md) | 데이터 표시 | 원본 크기를 아는 사진·차트 이미지를 로드 전에 자리를 잡아 두고, 실패해도 의미를 잃지 않게 보여 줄 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
107
107
|
| [ImageChoiceGroup](components/image-choice-group.md) | 화면/계정 | 아바타 얼굴, 프로필 사진, 모음에 넣을 사진처럼 **그림을 보고 고르는** 단일·다중 선택에 쓴다. | 미게시(1.26.1 이후) | Web · Native | Web beta / Native beta | 배포 |
|
|
108
108
|
| [ImageComparison](components/image-comparison.md) | 데이터 표시 | 같은 좌표와 비율의 두 이미지를 겹쳐 변화량을 비교할 때 쓴다. | 1.14.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
@@ -130,7 +130,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
130
130
|
| [Notice](components/notice.md) | 상태와 알림 | 화면 흐름 안 **제자리에 남아 있는** 상태 알림에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
131
131
|
| [NotificationInboxScreen](components/notification-inbox-screen.md) | 화면/소통 | 알림함 화면 전체 틀에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
132
132
|
| [NotificationItem](components/notification-item.md) | 구성/정보 표시 | 알림함의 알림 한 행에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
133
|
-
| [NotificationRow](components/notification-row.md) |
|
|
133
|
+
| [NotificationRow](components/notification-row.md) | 데이터 표시 | 알림 목록에서 아바타, 유형 배지, 미리보기, 시간, 읽지 않음 표시, 선택적 썸네일을 나란히 배치한다. | 1.26.0 | Web · Native | Web stable / Native stable | 배포 |
|
|
134
134
|
| [NumberField](components/number-field.md) | 입력 | 범위가 정해진 **정확한 수 하나**를 입력받을 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
135
135
|
| [OnboardingScreen](components/onboarding-screen.md) | 화면/소개 | 첫 실행 소개·초기 설정처럼 **몇 단계를 차례로 넘기는 화면**에 쓴다. | 1.14.0 | Web · Native | Web stable / Native stable | 배포 |
|
|
136
136
|
| [OtpField](components/otp-field.md) | 입력 | 문자·메일로 받은 **숫자 인증번호**를 칸 모양으로 입력받을 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
@@ -144,7 +144,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
144
144
|
| [Popover](components/popover.md) | 오버레이 | 트리거에 붙어 뜨는 비모달 표면 안에 **포커스를 받는 임의 콘텐츠**를 둘 때 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
|
|
145
145
|
| [PricingChoice](components/pricing-choice.md) | 구성/선택과 필터 | 요금제·결제 주기 중 하나를 고르고, 선택한 실제 가격과 혜택을 확인한 뒤 [계속]으로 제품 결제 흐름을 시작할 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
146
146
|
| [ProfileScreen](components/profile-screen.md) | 화면/계정 | 내 프로필(또는 계정) 화면 틀에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
147
|
-
| [ProfileSummary](components/profile-summary.md) |
|
|
147
|
+
| [ProfileSummary](components/profile-summary.md) | 데이터 표시 | 프로필의 아바타·이름·배지·통계 수치·행동을 구성한다. | 1.26.0 | Web · Native | Web stable / Native stable | 배포 |
|
|
148
148
|
| [Progress](components/progress.md) | 상태와 알림 | 작업이 얼마나 진행됐는지 보여 줄 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
149
149
|
| [ProgressiveBlur](components/progressive-blur.md) | 시각 효과 | 스크롤 영역의 바깥에 더 내용이 있음을 알리거나 장식 이미지 가장자리를 흐릴 때 쓴다. | 1.14.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
150
150
|
| [QRCode](components/qr-code.md) | 데이터 표시 | 문자열(초대 링크, 연결 코드, 결제·체크인 URL)을 다른 기기의 카메라로 스캔하게 할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
@@ -158,7 +158,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
158
158
|
| [SavedItemsScreen](components/saved-items-screen.md) | 화면/콘텐츠 | 저장한 이미지·게시물을 컬렉션 표지 → 사진 격자 → 상세 순서로 탐색할 때 쓴다. | 1.17.1 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
159
159
|
| [ScreenLayout](components/screen-layout.md) | 화면/화면 틀과 도구 | 한 라우트 화면의 뼈대가 필요할 때 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
160
160
|
| [SearchField](components/search-field.md) | 입력 | 목록·화면 안에서 검색어를 입력받을 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
161
|
-
| [SearchScreen](components/search-screen.md) |
|
|
161
|
+
| [SearchScreen](components/search-screen.md) | 레이아웃 | 검색어 입력, 필터, 최근 검색, 결과 목록을 갖춘 검색 화면 전체에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
162
162
|
| [Section](components/section.md) | 레이아웃 | 화면 안의 내용 묶음에 제목·설명·머리 행동(“모두 보기”, “편집”)을 붙일 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
163
163
|
| [SegmentedControl](components/segmented-control.md) | 입력 | 2~4개의 짧은 보기 중 **항상 하나가 선택된** 전환에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
164
164
|
| [Select](components/select.md) | 입력 | 폼 한 칸에서 여러 선택지 중 하나를 고르게 할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
@@ -195,12 +195,13 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
195
195
|
| [TextTransition](components/text-transition.md) | 시각 효과 | 같은 자리의 짧은 문자열이 바뀔 때(상태 문구, 버튼 옆 안내, 단계 이름) 바뀐 순간을 짧은 등장 모션으로 알린다. | 1.12.1 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
196
196
|
| [ThinkingOrb](components/thinking-orb.md) | 상태와 알림 | AI 에이전트가 실제로 검색·생성·듣기 같은 작업을 하는 동안 그 단계를 보여 줄 때만 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
197
197
|
| [Timeline](components/timeline.md) | 데이터 표시 | 이미 일어난 일을 시간 순서대로 보여 줄 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
198
|
+
| [TimePicker](components/time-picker.md) | 입력 | 하루 중 시각 하나를 시·분 두 열에서 고를 때 쓴다. | 미게시(1.34.0 이후) | Web · Native | Web stable / Native stable | 배포 |
|
|
198
199
|
| [Toast](components/toast.md) | 상태와 알림 | 방금 한 행동의 결과처럼 **무시해도 안전한 짧은 알림**에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
199
200
|
| [ToggleFeedbackButton](components/toggle-feedback-button.md) | 구성/직접 조작과 모션 | 좋아요·저장·알림 받기처럼 켜고 끄는 선택에 반응 모션과 개수를 붙인다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
200
201
|
| [ToggleGroup](components/toggle-group.md) | 입력 | 여러 개를 동시에 켜고 끄는 짧은 버튼 묶음에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
201
202
|
| [Tooltip](components/tooltip.md) | 오버레이 | Web에서 이미 이름과 focus를 가진 컨트롤(대개 IconButton)에 **짧은 보충 설명 한 문장**을 붙일 때 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
|
|
202
203
|
| [Top](components/top.md) | 레이아웃 | 화면 **본문의 첫 블록**에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
203
|
-
| [TopBar](components/top-bar.md) | 탐색 |
|
|
204
|
+
| [TopBar](components/top-bar.md) | 탐색 | 2026-10-18 전사 기준: 하단 탭·상단 탐색과 중복되는 최상위 화면명은 생략한다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
204
205
|
| [Tour](components/tour.md) | 오버레이 | 새 화면·새 기능을 처음 만난 사용자에게 화면의 여러 요소를 순서대로 짚어 설명할 때 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
|
|
205
206
|
| [TransferList](components/transfer-list.md) | 입력 | 한 항목 집합을 두 목록으로 나누고 사용자가 항목을 오가게 할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
206
207
|
| [TransferTask](components/transfer-task.md) | 구성/피드백과 복구 | 업로드·다운로드·동기화처럼 실제 전송 계층이 진행량을 알려 주는 작업에 시작·취소·진행률·결과를 한 묶음으로 붙인다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
@@ -285,6 +286,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
285
286
|
| [테마 조합](compositions/design-profile-comparison.md) | 비교와 검증 | 같은 기능에 10가지 표현을 적용하고, 앱 소유 테마 설정을 넣었을 때 네 단계의 전파와 상태 유지를 검토할 때 쓴다. | 1.16.0 | Web · Native | 구성 API별 | 배포 |
|
|
286
287
|
| [토스트 배치 비교](compositions/toast-layout.md) | 비교와 검증 | Toast 카드 한 장의 내부 배치(톤 배지·제목·설명·닫기·실행 버튼)와 화면 위 위치를 좁은 폭·큰 글자·긴 문구·톤별로 확인하는 비교 스토리다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
|
|
287
288
|
| [환경 조합 검증](compositions/environment-matrix.md) | 비교와 검증 | 제품 화면이 테마·쓰기 방향·글자 크기·모션 설정이 달라져도 같은 의미를 유지하는지 확인할 때, 어떤 환경 조합과 검증 항목을 골라 볼지 정하는 기준표로 쓴다. | 1.12.1 | Web | 구성 API별 | 배포 |
|
|
289
|
+
| [ShortcutRail](compositions/shortcut-rail.md) | 콘텐츠 | 최근 장소·최근 도구처럼 누르면 바로 열리는 유한 목록이다. | 미게시(1.34.0 이후) | Web · Native | 구성 API별 | 실험 |
|
|
288
290
|
|
|
289
291
|
## 화면
|
|
290
292
|
|
package/docs/usage/SELECTION.md
CHANGED
|
@@ -5,6 +5,29 @@
|
|
|
5
5
|
이 선택 기준은 모든 화면·구성에 적용한다. **공통 구조는 HJM이 완성해서 제공하고, 앱은
|
|
6
6
|
데이터·문구·브랜드·권한·추가 기능을 연결한다.** 도입 계기는 [근거](#근거)에 있다.
|
|
7
7
|
|
|
8
|
+
## 작업 시작: 수정 위치 → 공개 API → 확인 범위
|
|
9
|
+
|
|
10
|
+
2026-10-18 사용자 요청: 에이전트가 여러 문서의 중복 규칙을 다시 해석하지 않고 필요한 구현으로
|
|
11
|
+
도달하도록 작업 진입점을 모았다. 아래는 기존 정본을 찾는 경로이며 새 승인·검사 절차가 아니다.
|
|
12
|
+
|
|
13
|
+
1. **수정 위치를 고른다.** 공통 배치·상태·상호작용 문제는 HJM, 데이터·문구·권한·서버 확정 문제는 제품이다.
|
|
14
|
+
[소유권](#소유권)과 아래 [합성 경계 사례](#합성-경계-사례)로 구분한다. 원인이 불명확하면 해당 입력과 renderer를 추적한다.
|
|
15
|
+
2. **필요한 API만 찾는다.** [색인](README.md)에서 화면 → 구성 → 컴포넌트 → 토큰 순으로 찾고,
|
|
16
|
+
선택한 API의 사용 지침과 실제 공개 타입·export를 읽는다. 모든 단계의 지침을 전부 읽는 순서가 아니다.
|
|
17
|
+
기존 API가 부족할 때만 아래 선택 표로 확장 경로를 결정한다.
|
|
18
|
+
3. **실행할 소스를 확인한다.** 설치 패키지를 쓰는 경로는 그 버전의 지침·타입·export가 기준이다.
|
|
19
|
+
로컬 소스 연결로 개발하는 경로는 실제 연결된 checkout을 기준으로 확인하고 설치 버전과 차이를 구분한다.
|
|
20
|
+
로컬에서 보이는 미게시 prop을 production에서도 제공한다고 판단하지 않는다. 버전 상승·게시·제품 채택을 자동으로 붙이지 않는다.
|
|
21
|
+
4. **변경에 필요한 확인만 고른다.** HJM 수정은 checkout의 `CONTRIBUTING.md` → 「변경 유형별 작업 범위」,
|
|
22
|
+
제품 수정은 제품 지침과 [소비 정책 §5](../consumer-policy.md#5-필수-검증과-evidence)를 따른다.
|
|
23
|
+
문서만 바꾸면 설명·참조 연결, 동작을 바꾸면 관련 타입·동작, export·peer를 바꾸면 해당 의존 경계를 확인한다.
|
|
24
|
+
전체 감사·빌드·게시를 일상 작업에 자동으로 추가하지 않는다.
|
|
25
|
+
|
|
26
|
+
브랜드 변경일 때만 [브랜드 경계](../brand-boundary.md), 새 공개 API 등록·승격일 때만
|
|
27
|
+
[등록·승격 기준](../stable-promotion.md)을 추가로 읽는다. HJM 게시 요청은 checkout의
|
|
28
|
+
`docs/RELEASE_GOVERNANCE.md`로 이어진다. npm에 없는 checkout 문서를 설치 패키지의 상대 링크로 찾지 않는다.
|
|
29
|
+
읽는 문서를 줄이는 것이 이미 적용되는 공통 규칙을 면제하지는 않는다.
|
|
30
|
+
|
|
8
31
|
## 선택 기준
|
|
9
32
|
|
|
10
33
|
| 선택 | 조건 | 구현 경로 |
|
|
@@ -28,6 +51,20 @@
|
|
|
28
51
|
제품에서 동일한 기본 구조를 다시 만드는 허용 경로로 해석하지 않는다. 배지는 의미·값을 앱이
|
|
29
52
|
결정하고 위치·간격·넘침 표현은 해당 HJM 구성과 Badge 계약을 따른다.
|
|
30
53
|
|
|
54
|
+
### 합성 경계 사례
|
|
55
|
+
|
|
56
|
+
| 필요한 변경 | 구현 위치와 연결 | 경계를 넘는 구현 |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
| 검색 결과에 제품의 지도 추가 | 제품 지도 기능을 선택한 화면 API의 콘텐츠 슬롯에 연결. 검색 입력·필터·상태 배치는 HJM 유지 | 지도 화면이라는 이유로 검색창·도구·빈 상태 틀까지 제품에서 재구현 |
|
|
59
|
+
| 탐색과 도구를 헤더 한 행에 배치 | `ScreenLayout.header`에 HJM `TopBar`를 연결하고 공개 `leading`·`actions` 사용 | 앱의 `View`/`div`로 헤더 정렬·높이·행동 위치를 다시 정의 |
|
|
60
|
+
| 댓글 목록을 가상화 | 제품은 가상화 host·데이터를 연결하고 행은 `CommentThreadRow` 등 공개 공통 표시 사용 | 가상화를 이유로 아바타·배지·메뉴·답장 배치를 제품 행에 복제 |
|
|
61
|
+
| 검색 빈 상태가 위쪽에 붙음 | 제품이 상태와 host 높이를 올바르게 전달하는지 확인. 입력이 맞으면 HJM의 해당 상태 영역 배치를 수정 | 제품별 spacer·margin으로 중앙처럼 보이게 보정 |
|
|
62
|
+
| 저장 실패 때 입력이 사라짐 | 제품의 draft·요청 확정 처리와 HJM 콜백을 추적해 값을 지우는 소유 지점을 수정 | UI에 이전 값을 따로 복제해 실제 상태 오류를 감춤 |
|
|
63
|
+
|
|
64
|
+
기존 공통 API의 합성은 허용한다. 합성할 때 공통 영역의 위치·상태 엔진까지 새로 정의해야 한다면
|
|
65
|
+
HJM의 기존 API를 확장하거나 하위 구성을 공개한다. 도메인 로직은 그 확장에 옮기지 않는다.
|
|
66
|
+
표의 API도 실행할 버전의 공개 타입·export에서 지원을 확인한다.
|
|
67
|
+
|
|
31
68
|
## 조립 규칙(필수)
|
|
32
69
|
|
|
33
70
|
예제·제품 화면 모두 아래 표의 컴포넌트를 쓴다. 버튼·글자로 대체 조립하지 않는다.
|
|
@@ -47,11 +84,16 @@
|
|
|
47
84
|
| 도구성 행동(새로고침·추가·편집·삭제·공유·검색·필터·더보기) | `IconButton` + 아이콘, 글자는 `label`(보조기술 이름). HJM 화면은 아이콘 슬롯(`ScreenFlowAction.icon` 등) | 헤더·행·도구 막대의 글자 버튼 |
|
|
48
85
|
| 화면 나가기(뒤로·닫기·설정 나가기) | `ScreenLayout`(과 이를 쓰는 화면)·`AuthScreenLayout`의 `exit` — 헤더 시작 쪽 아이콘 버튼, 글자 없음. 단계형 작성의 앞 단계는 `EditorScreen` `stepBack`(1.31.0) | 본문·하단의 "나가기" 글자 버튼, 화면마다 다른 화살표, 로그인·작성 화면 위에 따로 얹은 `TopBar` |
|
|
49
86
|
| 버튼 묶음 간격 | 세로로 쌓으면 `gap="xs"`(8), 가로로 나란하면 `gap="sm"`(12) — `actionGroupRecipe` | 세로 버튼 사이 12 이상 |
|
|
50
|
-
|
|
|
51
|
-
|
|
|
87
|
+
| 나란한 컴포넌트 높이 | 항상 같은 높이로 위·아래 경계를 맞춘다. 버튼·필터·탭·선택 카드·입력/아이콘 도구 모두 포함. 여러 줄이면 같은 행의 가장 높은 콘텐츠에 맞추며 줄바꿈 그리드는 행별 적용. 세로의 동일 행동 묶음도 같은 크기 규격. 공통 renderer/그리드가 소유(2026-10-20 다에리 선택 카드 높이 불일치 사용자 지적) | 문구·아이콘·선택/강조 상태마다 다른 높이, 내용 자르기·글자 축소·넘치는 고정 높이로 맞추기 |
|
|
88
|
+
| 상단 영역과 본문 연결 | 상태바·노치/Dynamic Island safe area → 헤더 → 본문 첫 배경을 연속된 바탕으로 연결한다. 안내·캐릭터/말풍선·브랜드 영역의 배경/표면 전환·좌우 경계·여백도 연결한다. 고정/접힘 상단은 실제 높이를 공통 화면/스크롤 host에 반영해 본문을 가리지 않는다(2026-10-20 다에리 사용자 지적) | 하늘색 상태바/장면 사이의 짙은 헤더 띠, 상태바만 별도 덧칠, 불필요한 빈 띠·굵은 선·중복 테두리/그림자, 상단 아래 본문 가림, 제품별 좌표/보정 여백 덧대기 |
|
|
89
|
+
| 레이아웃 바깥 여백·본문 폭 | `ScreenLayout`·부모 콘텐츠 영역의 좌우 바깥 여백은 항상 같은 값, 상하 바깥 여백도 같은 기준. 같은 영역의 카드·입력·목록·버튼 묶음은 동일 폭·경계. 카드 내부 내용·padding은 해당 recipe 유지(2026-10-19 사용자 명확화) | 서로 다른 본문 폭, 한쪽 margin·고정 폭 왼쪽 몰림으로 생긴 바깥 여백 불일치 |
|
|
90
|
+
| 앱·모바일 웹뷰 글자 | 본문·라벨을 기본으로, 제목은 필요한 최소 단계. 굵기·간격으로 위계를 만들고 기존 24 상한을 기본값으로 사용하지 않는다(2026-10-19 사용자 지시) | 큰 구획 제목·히어로 문구·과도하게 큰 숫자 |
|
|
91
|
+
| 페이지 진입 시 입력 포커스 | `ScreenLayout`·`AuthScreenLayout`의 기본 `first-input`, 주 입력 지정은 `initialFocusTarget`. 상단·본문 입력만 대상이고 `ScreenLayout.footer`의 하단 작성창은 제외한다(checkout 변경·미게시). 하단 시트는 자동 입력 포커스 없음([진입 포커스](components/screen-layout.md#진입-포커스), 1.33.0 정책 변경) | 앱별 `autoFocus`·지연 `ref.focus()`·단계 전환·시트 열기 뒤 자동 포커스 |
|
|
52
92
|
|
|
53
93
|
리뷰 기준: 위 "쓰지 않을 것" 패턴이 보이면 예제 조립 문제인지 컴포넌트 recipe 문제인지 먼저 가리고, 원인 쪽(예제 또는 HJM 컴포넌트)을 고친다.
|
|
54
94
|
|
|
95
|
+
2026-10-20 스크롤 상단 연결: 콘텐츠 위의 투명한 상단 바를 우선하고 장면은 그 뒤로 이어진다. 필요한 컨트롤 면은 유지하며, 가독성이 필요할 때만 반투명/부드러운 scroll-edge 효과를 사용한다. 고정 색 띠로 덮지 않는다. 상태바·safe area도 스크롤 배경 전환을 함께 따르고, 공통 host가 헤더의 실제 높이를 측정해 첫 콘텐츠를 보호한다. 맨 위/중간/본문/복귀 상태를 확인한다. 다에리 스크롤 단절 지적 및 [Apple](https://developer.apple.com/videos/play/wwdc2025/356/)·[React Navigation](https://reactnavigation.org/docs/elements/) 공식 사례에 근거한다. Native에서는 로컬 `ScreenLayout.headerPosition="overlay"`와 `TopBar.surface="transparent"`로 연결한다(미게시).
|
|
96
|
+
|
|
55
97
|
## 구현과 확인
|
|
56
98
|
|
|
57
99
|
- 화면 → 구성 → 컴포넌트 → 토큰 순으로 찾고 기존 공개 API와 양 renderer를 먼저 확인한다.
|
|
@@ -63,14 +63,14 @@ import { Text } from "@hjmds/react-native/primitives";
|
|
|
63
63
|
|
|
64
64
|
| prop | 값 | 기본값 | 설명 |
|
|
65
65
|
| --- | --- | --- | --- |
|
|
66
|
-
| `items` | Web `{ id, title: ReactNode, panel: ReactNode, disabled? }[]` · Native `{ value, title: string, description?, content, disabled?, accessibilityLabel?, accessibilityHint?, contentAccessibilityLabel? }[]` | 필수 | 한 개 이상, 식별자 고유 |
|
|
66
|
+
| `items` | Web `{ id, title: ReactNode, panel: ReactNode, disabled?, accessibilityLabel? }[]` · Native `{ value, title: string, description?, content, disabled?, accessibilityLabel?, accessibilityHint?, contentAccessibilityLabel? }[]` | 필수 | 한 개 이상, 식별자 고유 |
|
|
67
67
|
| `density` | `compact` · `comfortable` | `comfortable` | compact는 trigger 최소 높이가 최소 터치 영역이다 |
|
|
68
68
|
| Web `allowsMultipleExpanded` · Native `multiple` | `boolean` | `false` | 여러 항목 동시 펼침 |
|
|
69
69
|
| Web `value`·`defaultValue` · Native `expandedValues`·`defaultExpandedValues` | `readonly string[]`(펼친 식별자) | `defaultValue`·`defaultExpandedValues` `[]` | 제어·비제어 둘 다 된다 |
|
|
70
70
|
| Web `onValueChange` · Native `onExpandedValuesChange` | `(value: readonly string[]) => void` | — | 펼친 식별자 전체 목록을 받는다(단일 모드는 0~1개) |
|
|
71
71
|
| Web `headingLevel` | `2`~`6` | `3` | 화면의 heading 위계에 맞춰 정한다 |
|
|
72
72
|
| Native `label` | `string` | 필수 | 그룹 접근성 이름 |
|
|
73
|
-
|
|
|
73
|
+
| `renderIndicator` | Web `(props: { value, expanded, disabled }) => ReactNode` · Native는 `color, size`도 제공 | `+`/`−` | Web 슬롯·항목 접근성 이름은 미게시 로컬 소스다. 장식 펼침 표시만 교체한다. 점수 등 도메인 정보를 넣으면 item의 `accessibilityLabel`에 값도 포함한다. 버튼·링크를 넣지 않는다 |
|
|
74
74
|
| `layoutStyle` | margin·width·flex·`alignSelf` | — | 바깥 배치 전용 |
|
|
75
75
|
| Native `style`·`itemStyle`·`triggerStyle`·`titleStyle`·`indicatorStyle`·`panelStyle` | — | — | 제거된 속성 — `layoutStyle` 또는 `density`/`renderIndicator` |
|
|
76
76
|
|
|
@@ -107,3 +107,5 @@ import { Text } from "@hjmds/react-native/primitives";
|
|
|
107
107
|
| 항목별 접근성 문구 | 없음 | `accessibilityLabel`, `accessibilityHint`, `contentAccessibilityLabel` |
|
|
108
108
|
| 키보드 | ArrowUp/Down·Home·End로 trigger 이동(disabled 건너뜀) | 해당 없음 |
|
|
109
109
|
| 애니메이션 | 없음(`hidden` 전환) | `LayoutAnimation`, reduced motion이면 생략 |
|
|
110
|
+
|
|
111
|
+
2026-10-20 번뚝 평가 항목 이관에서 Web에도 Native와 같은 장식 indicator 슬롯과 항목 접근성 이름이 필요했다. 공통 버튼·키보드 이동·단일 펼침 엔진을 유지하면서 제품 점수 막대를 연결하도록 제공한다.
|
|
@@ -85,6 +85,7 @@ import { AlertDialog } from "@hjmds/react-native/overlays";
|
|
|
85
85
|
| 닫기 | — | — | 바깥 누름으로 닫히지 않는다. Escape(Web)·뒤로(Native)는 취소로 끝난다. 처리 중(`busy`)에는 닫을 수 없다 |
|
|
86
86
|
| `request.onConfirm` 실패 | — | — | 열린 채로 `resolveErrorMessage` 또는 `fallbackErrorMessage`를 보여 준다 |
|
|
87
87
|
| Web `trigger` | `ReactNode` | — | 비제어면 필수 |
|
|
88
|
+
| `confirmDisabled` | boolean | false | checkout·미게시. 제품의 확인 불가 조건만 잠그며 외부 잠금만으로 취소·실패 세션을 초기화하지 않는다 |
|
|
88
89
|
| `size` | `small` · `medium` · `large` | Web `medium` · Native `small` | 각 플랫폼 recipe의 최대 폭·안쪽 여백을 선택한다 |
|
|
89
90
|
| Web `modalPriority` | `number` | `0` | 높은 우선순위 모달이 뒤에 열린 낮은 모달 위에서 동작한다 |
|
|
90
91
|
| Native `contentStyle` | 배치 key(margin·width·flex·`alignSelf`)만 | — | `HjmCompositionStyleProp` 배치 전용 타입. 색·radius·padding은 받지 않으며 크기는 `size`로 지정한다 |
|
|
@@ -149,3 +150,7 @@ import { AlertDialog } from "@hjmds/react-native/overlays";
|
|
|
149
150
|
삭제·Apple 인증·로컬 정리의 성공을 뜻하지 않는다. 처리 결과는 제품 상태 화면이 계속 보여 준다.
|
|
150
151
|
- Native에서 매우 긴 확인 문구는 본문이 스크롤되도록 바뀌었지만 큰 글자 실기기 검증은 끝나지 않았다([Dialog](../../dialog.md)).
|
|
151
152
|
문구를 자르거나 글자 크기 상한으로 피하지 않는다.
|
|
153
|
+
|
|
154
|
+
### 외부 확인 잠금 (checkout, 미게시)
|
|
155
|
+
|
|
156
|
+
`confirmDisabled`(기본 false)는 요청 제한 시간 등 제품이 판단한 확인 불가 조건이다. Web·Native 모두 확인 행동만 차단하며 취소는 유지한다. 비동기 처리 중 중복 실행·닫힘·실패 상태는 기존 세션이 계속 소유한다. 잠금을 해제해도 열린 세션과 실패 안내를 초기화하지 않는다. Spint의 내 글 내리기 재시도 제한을 공통 확인창에서도 보존하기 위해 추가했다.
|
|
@@ -55,6 +55,7 @@ Native 사용 예는 없다(renderer 없음).
|
|
|
55
55
|
| --- | --- | --- | --- |
|
|
56
56
|
| `label` | `string` | 필수 | nav 접근성 이름 |
|
|
57
57
|
| `items` | `{ id: string, label: string }[]` | 필수 | 한 개 이상, id 고유·공백 없음 |
|
|
58
|
+
| `renderLeading` | `(item) => ReactNode` | 없음 | 미게시. 항목 시작의 장식 아이콘. 18px 슬롯이며 링크 이름에는 포함하지 않는다 |
|
|
58
59
|
| `onNavigate` | `(id: string, event: MouseEvent<HTMLAnchorElement>) => void` | — | 기본 동작 전에 호출. `event.preventDefault()`면 HJM 스크롤·초점·history를 건너뛴다 |
|
|
59
60
|
| `layoutStyle` | margin·width·flex·`alignSelf` | — | 바깥 배치 전용 |
|
|
60
61
|
| `orientation` | `vertical` · `horizontal` | `vertical` | 좁은 폭에서 줄바꿈하고 label을 자르지 않는다 |
|
|
@@ -66,7 +67,7 @@ Native 사용 예는 없다(renderer 없음).
|
|
|
66
67
|
|
|
67
68
|
| 항목 | 값 | 근거 |
|
|
68
69
|
| --- | --- | --- |
|
|
69
|
-
| 크기 | 링크 안쪽 여백 `spacing.xs` 8
|
|
70
|
+
| 크기 | 링크 안쪽 여백 `spacing.xs` 8, 최소 높이·너비 `control.minTouchTarget` 44, 아이콘–글자 간격 `spacing.xs` 8. 아이콘 슬롯 `glyph.sm` 18 | `anchorRecipe.link`, `.hjm-anchor__link` |
|
|
70
71
|
| 간격 | 항목 사이 `spacing.xxs` 4. 스토리는 Section 제목 아래 `Stack gap="md"`(16)로 [Anchor] → [본문 스크롤 영역]을 쌓는다 | `anchorRecipe.gap`, `showcase/web/src/patterns/Anchor.stories.tsx` |
|
|
71
72
|
| 순서·정렬 | 가리키는 본문 앞에 둔다. 넓은 문서 화면은 `vertical`로 본문 옆 열, 좁은 폭은 `horizontal`로 본문 위. 현재 위치는 세로형 시작 쪽·가로형 아래쪽 2px 선(`stroke.strong`, `border.focus`) | `anchorRecipe.current`, `.hjm-anchor*` |
|
|
72
73
|
| 고정·스크롤 | 스크롤 중에도 보이게 하려면 [Affix](affix.md)로 감싸고, 상단 고정 헤더가 있으면 `offset`을 그 높이로 맞춘다 | `getAnchorCurrentId`(`src/anchor.ts`) |
|
|
@@ -94,3 +95,5 @@ Native 사용 예는 없다(renderer 없음).
|
|
|
94
95
|
- DOM에 대상이 없는 항목은 현재 위치 후보에서 빠지고 기본 href 동작을 한다. 지연 렌더 섹션은 삽입되면 자동으로 관찰된다.
|
|
95
96
|
- modifier 클릭(새 탭 등)은 브라우저 기본 동작에 맡긴다. 스크롤·초점 이동이 일어나지 않는 것이 정상이다.
|
|
96
97
|
- 중첩 트리 목차·제목 자동 수집은 없다. 항목은 호스트가 넘긴다.
|
|
98
|
+
|
|
99
|
+
2026-10-19 다에리 설정 바로가기의 직접 링크/프레임을 이관하며 장식 leading과44px target을 추가했다. 기본 웹 링크와 hash·현재 구획·대상 초점 계약은 유지한다. Native의 측정된 ScrollView 구획 이동은 `ShortcutRail layout="wrap"`에서 host에 전달하며 Anchor의 웹 DOM 동작으로 흉내 내지 않는다.
|
|
@@ -43,10 +43,11 @@ import { Badge } from "@hjmds/react-native/data-display";
|
|
|
43
43
|
<Badge tone="success" size="small" label={t("order.status.done")} />
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
왼쪽 → 라벨 → 오른쪽 순서로 배치한다.
|
|
46
|
+
왼쪽 → 라벨 → 오른쪽 순서로 배치한다. 아이콘 콜백은 배지 글자와 같은 `color`와 `glyph.xs`(14) `size`를 받는다. Native는 실제 테마 색을, Web은 `currentColor`를 전달한다. 이 콜백 API는 미게시다. 아이콘은 장식이며 별도 누르기 동작을 넣지 않는다.
|
|
47
47
|
|
|
48
48
|
```tsx
|
|
49
|
-
|
|
49
|
+
// Web
|
|
50
|
+
<Badge tone="success" leading={({ color, size }) => <Trophy size={size} color={color} />} trailing={({ color, size }) => <Check size={size} color={color} />}>완료</Badge>
|
|
50
51
|
```
|
|
51
52
|
|
|
52
53
|
## 축과 기본값
|
|
@@ -56,12 +57,14 @@ import { Badge } from "@hjmds/react-native/data-display";
|
|
|
56
57
|
| `tone` | `neutral` · `strong` · `brand` · `info` · `success` · `warning` · `attention` · `danger` | `neutral` | 단색 배경과 흰색 굵은 글자를 사용한다. `neutral`은 회색, `strong`은 더 진한 회색, 나머지는 각 의미의 solid fill이다. `outline`은 투명 배경과 의미색 글자다 |
|
|
57
58
|
| `variant` | `filled` · `outline` | `filled` | — |
|
|
58
59
|
| `size` | `small` · `medium` | `medium` | — |
|
|
59
|
-
| `leading` · `trailing` | `ReactNode` | — | 라벨 왼쪽·오른쪽 아이콘. 양쪽 동시 사용 가능하며 보조기기에서 숨겨진다 |
|
|
60
|
+
| `leading` · `trailing` | `ReactNode \| (context: BadgeAdornmentContext) => ReactNode` | — | 라벨 왼쪽·오른쪽 아이콘. 양쪽 동시 사용 가능하며 보조기기에서 숨겨진다 |
|
|
60
61
|
| Web `children` · Native `label` | `ReactNode` · `string \| number`(필수) | — | 표시 라벨 |
|
|
61
62
|
| Native `accessibilityLabel` | `string` | `String(label)` | 숫자 라벨에 단위를 붙여 읽힐 때 쓴다 |
|
|
62
63
|
| `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native `style`·`labelStyle`은 제거된 속성 — layoutStyle 또는 tone/토큰 |
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
행동 콜백은 없다. 아이콘 렌더 콜백은 색과 크기만 받으며 배지는 누를 수 없는 표시다.
|
|
66
|
+
|
|
67
|
+
상태 채움의 글자·아이콘은 `onStatusFill`, 브랜드 채움은 `onPrimary`, 위험 채움은 `onDanger`를 쓴다. 브랜드의 전경색이 어두워도 상태 배지의 대비를 독립적으로 유지하기 위한 역할 분리다(미게시).
|
|
65
68
|
|
|
66
69
|
## 배치
|
|
67
70
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
6
|
- 적용: 1.12.1
|
|
7
|
-
- 검토일: 2026-10-
|
|
7
|
+
- 검토일: 2026-10-20
|
|
8
8
|
- 근거: [화면 제목과 마지막 행동](../../screen-chrome.md), recipe `bottomCtaRecipe`(`src/component-recipes.ts`)
|
|
9
9
|
- 스토리북: `배포/컴포넌트/동작/하단 실행 버튼`
|
|
10
10
|
|
|
@@ -62,9 +62,10 @@ const insets = useSafeAreaInsets();
|
|
|
62
62
|
|
|
63
63
|
| prop | 값 | 기본값 | 설명 |
|
|
64
64
|
| --- | --- | --- | --- |
|
|
65
|
-
| `primaryAction` | Web `{ label, onClick
|
|
65
|
+
| `primaryAction` | Web `{ label, onClick?, type?, form?, accessibilityLabel?, disabled?, loading?, loadingLabel?, size?, tone? }` · Native `{ label, onPress, accessibilityLabel?, accessibilityHint?, disabled?, loading?, loadingLabel?, size?, tone? }` | 필수, tone `primary`, size `medium` | Web `onClick: (event: MouseEvent<HTMLButtonElement>) => void`, Native `onPress: (event: GestureResponderEvent) => void`. Web의 기본 `type="button"`은 onClick이 필수다 |
|
|
66
|
+
| Web action `type` · `form` | `button` · `submit` · `reset`, HTML form ID | `button`, 없음 | 미게시. submit/reset은 onClick 없이 브라우저 기본 동작을 사용한다. Sheet 본문의 form과 footer 버튼을 같은 ID로 연결하면 Enter·입력 검증·폼 제출/초기화를 유지한다. Native에는 이 HTML 속성이 없다 |
|
|
66
67
|
| action `buttonRef` | Web `Ref<HTMLButtonElement>` · Native `Ref<View>` | — | 미게시. 그 행동의 버튼 자체를 가리킨다(컴포넌트 `ref`는 Web에서 바 `div`). 이 행동으로 연 Dialog·Sheet·`ImmersiveOverlay`가 닫힐 때 포커스를 돌려줄 곳으로 Native `returnFocusRef`에 넘긴다. Web Dialog는 연 요소로 스스로 돌아가므로 필요할 때만 쓴다 |
|
|
67
|
-
| `secondaryAction` | 같은 action 객체 또는 제품이 만든 `ReactNode` | tone `secondary` | 객체면 HJM Button으로 그린다. Web은 `label
|
|
68
|
+
| `secondaryAction` | 같은 action 객체 또는 제품이 만든 `ReactNode` | tone `secondary` | 객체면 HJM Button으로 그린다. Web은 문자열 `label`이 있는 action 객체로 판단하며 callback 없는 submit/reset도 지원한다 |
|
|
68
69
|
| `description` | `string` | — | 행동 위 caption 한 줄 |
|
|
69
70
|
| `accessibilityLabel` | `string` | — | 바 전체(Web `role="group"`, Native `toolbar`)의 이름 |
|
|
70
71
|
| `safeAreaBottom` | 0 이상의 유한수 | `0` | 음수·무한대는 `RangeError` |
|
|
@@ -74,13 +75,29 @@ const insets = useSafeAreaInsets();
|
|
|
74
75
|
|
|
75
76
|
## 배치
|
|
76
77
|
|
|
78
|
+
2026-10-20 번뚝 작성 화면은 BottomCTA에 HTML 제출 연결이 없어 버튼 묶음을 직접 유지했다.
|
|
79
|
+
Web은 기존 form의 onSubmit/IME·검증을 유지하고 아래처럼 footer의 버튼을 form ID로 연결한다.
|
|
80
|
+
onClick에서 requestSubmit을 흉내 내거나 빈 콜백을 넣을 필요가 없다. 여러 편집기가 함께 있으면 React useId로 ID를 만든다.
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
<Sheet title={title} open={open} onOpenChange={setOpen} closeLabel={closeLabel}
|
|
84
|
+
footer={<BottomCTA primaryAction={{ label: saveLabel, type: "submit", form: formId }} />}>
|
|
85
|
+
<form id={formId} onSubmit={submit}>
|
|
86
|
+
<TextField name="title" label={titleLabel} required />
|
|
87
|
+
</form>
|
|
88
|
+
</Sheet>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
독립형 바의 버튼·설명 최대 폭은 `layout.readingMaxWidth - spacing.md × 2`다. ScreenLayout의 최대 폭에는 본문 양쪽 여백이 포함되므로 그 안쪽 경계에 맞춘다(2026-10-20 바깥 여백 정렬 수정·미게시).
|
|
92
|
+
|
|
77
93
|
| 항목 | 값 | 근거 |
|
|
78
94
|
| --- | --- | --- |
|
|
79
95
|
| 크기 | 화면 폭을 꽉 채우는 하단 바. 최소 높이 64(Native는 64 + `safeAreaBottom`). 행동 버튼은 칸을 꽉 채우고 높이는 Button `size`를 따른다(기본 medium 44) | `bottomCtaRecipe.minHeight`, `.hjm-bottom-cta__actions .hjm-button`, `react-native/src/actions.tsx` |
|
|
80
|
-
| 간격 | 좌우 `
|
|
96
|
+
| 간격 | 좌우 `spacing.md` 16으로 ScreenLayout 본문 경계와 일치(2026-10-20 비행중 화면 리뷰에서 독립 하단 바만 4pt 더 들어간 문제 수정·미게시), 위 `spacing.sm` 12, 아래 `spacing.sm` 12와 하단 inset 중 큰 값(Web은 `env(safe-area-inset-bottom)`·`safeAreaBottom`도 비교). 설명↔행동, 행동 사이 `spacing.sm` 12. 위 경계는 `border.default` 1px(`stroke.default`), Native는 위로 드리우는 그림자(opacity 0.08, radius 8) | `bottomCtaRecipe`, `.hjm-bottom-cta` |
|
|
81
97
|
| 순서·정렬 | 위→아래 [설명 한 줄(caption, muted)] → 행동 줄. 행동 줄은 [보조][주] 순서로 같은 폭을 나눈다. 주 행동은 하나다 | `react/src/bottom-cta.tsx`, `react-native/src/actions.tsx` |
|
|
82
98
|
| 고정·스크롤 | 본문 스크롤 영역 아래에 붙는다. Web은 `flow`(문서 흐름 끝) 또는 `sticky`(아래 0에 붙음, z-index `layer.sticky` 100). Native는 제품 화면 레이아웃이 스크롤 영역 밖 아래에 둔다. 키보드가 열리는 폼은 `KeyboardAvoiding` 또는 `KeyboardDock`으로 감싼다 | `.hjm-bottom-cta[data-position="sticky"]`, `react-native/src/keyboard-controller.tsx` |
|
|
83
99
|
| `ScreenLayout` `footer` 안 | footer가 위 경계·`screenPatternRecipe.padding` 16·바탕·safe area를 맡으므로 BottomCTA는 바(경계·여백·바탕·그림자·최소 높이)를 그리지 않고 설명과 행동 줄만 그린다. 버튼 아래 여백은 `max(footer 아래 여백, safeAreaBottom)`이다 — 직접 넘긴 `safeAreaBottom`은 footer가 이미 준 여백을 넘는 만큼만 더한다(16 + 34가 아니라 34). Native는 화면 inset을 ScreenLayout `safeAreaBottom`으로 넘기는 것이 기본이다. 2026-10-17 화면 리뷰: footer 안의 BottomCTA가 경계를 두 겹 긋고 버튼을 한 번 더 들였으며 Native는 footer 안에 그림자 있는 흰 상자를 하나 더 그렸다 | 내부 `ScreenFooterContext`, Web `.hjm-bottom-cta[data-placement="screen-footer"]`, Native `BottomCTA` |
|
|
100
|
+
| `Sheet` `footer` 안 | 시트가 본문과 같은 좌우 여백·바탕·safe area를 소유한다. BottomCTA는 별도 여백·경계·그림자 없이 행동 줄을 footer 폭에 채운다. 직접 넘긴 `safeAreaBottom`은 시트가 확보한 아래 여백을 넘는 만큼만 더한다. 2026-10-20 선택의창 장소 편집의 입력칸/저장 버튼 폭 차이를 수정했다(미게시). 시트 본문에 둔 BottomCTA는 독립형 바다 | 내부 `ScreenFooterContext`, Web/Native `Sheet` |
|
|
84
101
|
| 좁은 폭·큰 글자 | Web은 행동 칸 기준 폭 132(`control.minTouchTarget` × 3)보다 좁으면 줄바꿈한다. 큰 글자에서는 세로로 쌓고 주 행동이 위에 온다 | `.hjm-bottom-cta__actions > div`, `.hjm-bottom-cta[data-large-text="true"]` |
|
|
85
102
|
|
|
86
103
|
```text
|
|
@@ -104,6 +121,14 @@ const startRef = useRef<View>(null);
|
|
|
104
121
|
<ImmersiveOverlay open={presenting} onOpenChange={setPresenting} returnFocusRef={startRef} label={t("display.title")} exitLabel={t("common.close")}>…</ImmersiveOverlay>
|
|
105
122
|
```
|
|
106
123
|
|
|
124
|
+
### 행동 아이콘 연결 (미게시)
|
|
125
|
+
|
|
126
|
+
`primaryAction`과 descriptor 형태의 `secondaryAction`은 `leading`·`trailing` ReactNode를
|
|
127
|
+
Button에 그대로 전달한다. 아이콘 크기·그림은 제품이 공급하고 장식 슬롯의 배치·간격·loading 교체는
|
|
128
|
+
공통 Button이 소유한다. 이름은 필수 `label`과 선택 `accessibilityLabel`이며 아이콘을 이름 대신 쓰지 않는다.
|
|
129
|
+
번뚝 이야기 하단이 아이콘 때문에 자체 버튼 행을 만들던 것을 기존 BottomCTA로 이관하려고 보완했다.
|
|
130
|
+
같은 폭의 주·보조 행동 배치와 ScreenLayout footer의 여백 소유는 유지한다.
|
|
131
|
+
|
|
107
132
|
## 꼭 지킬 것
|
|
108
133
|
|
|
109
134
|
- action `label`은 i18n 키로 넣고 비우지 않는다. Web은 빈 label에 `TypeError`를 던진다.
|
|
@@ -158,6 +158,9 @@ Web Provider는 같은 값을 `--hjm-bottom-navigation-inset` CSS 변수로도
|
|
|
158
158
|
행동만 하고 화면이 바뀌지 않으면 `primaryAction`이다. 원은 선택 여부와 관계없이 브랜드 면(`action.brand`)으로 칠하고,
|
|
159
159
|
선택 표시는 다른 항목처럼 라벨 굵기·색이 맡는다. 원은 `지름 − 표시 자리 높이`만큼 위로 올라가 세 라벨이 한 줄에 선다.
|
|
160
160
|
|
|
161
|
+
Native `inline`은 원이 막대 위로 솟는 높이도 상단 padding으로 확보해 막대 배경을 연결한다. 바깥 margin으로 장면 배경 띠를 노출하지 않는다. `overlay`는 같은 높이를 Provider에 포함해 알린다.
|
|
162
|
+
2026-10-20 다에리 Send footer와 가운데 Echo 원이 겹친 원인은 inline 막대가 돌출 높이를 배치에 반영하지 않은 것이었다(개발 소스 수정·미게시).
|
|
163
|
+
|
|
161
164
|
```tsx
|
|
162
165
|
// Native
|
|
163
166
|
<BottomNavigation
|
|
@@ -266,3 +269,8 @@ floating 그림자는 그리지 않는다. 옆면이 높이를 표현하므로
|
|
|
266
269
|
- `shape`·`iconEmphasis`: 1.31.0 채택 뒤에도 Utilverse 다섯 셸(iPhone 둥근 독·Android 알약·macOS 작은 독·Linux/Windows 각진 막대)의 틀과 선택 아이콘 올림(−3·1.06배, 나머지 0.82)을 표현할 공개 축이 없어 모든 셸을 floating 하나로 통일했다(2026-10-18, Utilverse ADR-0074). 모서리는 그 독의 radius token 그대로다. 안쪽 여백은 항목 density padding(8)이 이미 있어 원래 Surface 여백(폰 12·데스크톱 8)에서 한 단계(4)씩 줄였다. 올림은 4pt 격자에 맞춰 4로 했다.
|
|
267
270
|
|
|
268
271
|
- capsule 선택 알약·tile 선택 카드: 2026-10-17 정렬 실측(Web 402·1280, 실험 내비게이션 바 비교). 첫·끝 아이콘의 막대 끝 거리 차이 17~23px은 선택 항목의 `flex: 2` 알약 때문이며 항목 상자 양끝 여백은 5/5로 같았다. 가운데 항목이 14.5px 솟아 보인 값은 tile 선택 카드 상자를 다른 항목의 라벨과 비교한 측정 오차였고 아이콘 244·라벨 268이 모든 항목에서 같았다. 두 renderer에 이미 같은 규칙이 있어 코드는 바꾸지 않고 배치 표에 의도를 적었다.
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
개발 브라우저에서 Native renderer를 실행할 때도 현재 목적지의 선택 상태와 비활성 상태를 전달한다.
|
|
275
|
+
Tab은 탐색 막대에 한 번 진입하고, 방향키·Home·End는 포커스만 옮긴다. Enter·Space로 목적지를
|
|
276
|
+
활성화하며 실제 경로 선택은 계속 제품 navigator가 공급한다.
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
| --- | --- |
|
|
22
22
|
| 아이콘만 있는 행동, 도구성 행동(새로고침·추가·편집·삭제·공유·검색·필터·더보기) | [IconButton](icon-button.md) — 글자는 `label`로 |
|
|
23
23
|
| 화면 하단에 고정된 주 행동 | [BottomCTA](bottom-cta.md) |
|
|
24
|
-
| 다른 페이지·URL로 이동 | [Link](link.md) (`tone="link"` 버튼은 같은 화면 안의 행동용
|
|
24
|
+
| 다른 페이지·URL로 이동 | [Link](link.md), Web의 채워진 주 CTA는 `ButtonLink`(미게시). `tone="link"` 버튼은 같은 화면 안의 행동용 |
|
|
25
25
|
| 여러 선택지 중 하나를 고름 | [SegmentedControl](segmented-control.md), [RadioGroup](radio-group.md), [ToggleGroup](toggle-group.md), `Chip`(`selectionMode="single"`) |
|
|
26
26
|
| 여러 개를 고름 | `Chip`(`selectionMode="multiple"`), [CheckboxGroup](checkbox-group.md) |
|
|
27
27
|
| 설정 켜고 끄기 | [Switch](switch.md)(설정 목록은 [ListRow](list-row.md) + Switch) |
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
| 이름 | 역할 | Web | Native |
|
|
35
35
|
| --- | --- | --- | --- |
|
|
36
36
|
| `Button` | 기본 | `@hjmds/react`, `/actions` | `@hjmds/react-native`, `/actions` |
|
|
37
|
+
| `ButtonLink` | 동반(버튼형 URL 이동, 미게시) | `@hjmds/react`, `/actions` | — |
|
|
37
38
|
| `ClipboardButton` | 동반(복사) | `/clipboard` | — |
|
|
38
39
|
| `InlineConfirm` | 확장(버튼 자리에서 한 번 더 확인) | `/inline-confirm` | `/inline-confirm` |
|
|
39
40
|
| `ReactionPicker` | 확장(반응 고르기) | `/reaction-picker` | `/reaction-picker` |
|
|
@@ -67,13 +68,15 @@ import { Button } from "@hjmds/react-native/actions";
|
|
|
67
68
|
| `shape` | `rounded` · `pill` | `rounded` | `radius.md` · `full` |
|
|
68
69
|
| `align` | `center` · `leading` | `center` | `leading`은 꽉 찬 폭의 행 행동 |
|
|
69
70
|
| `edge` | `start` · `end` · `both` | — | 버튼이 놓인 컨테이너 가장자리(논리 방향). `ghost`면 자기 좌우 여백만큼 그 쪽으로 내밀어 라벨·trailing 아이콘이 여백 선에 온다(`small` 12, `medium` 16, `large` 24, `resolveButtonEdgeOverhang`; Web은 투명 테두리 1을 더한다). 틀이 보이는 tone과 `link`는 움직이지 않는다. 규칙과 근거는 [IconButton](icon-button.md#근거) |
|
|
71
|
+
| `appearance` | `flat` · `raised` | 프로필 controls.button.appearance / 없으면 flat | 미게시. framed primary/secondary/danger에 옆면·눌림을 제공한다. ghost/link는 항상 평면이며 루트 입력 목표는 움직이지 않는다 |
|
|
72
|
+
| `fullWidth` | boolean | false | 부모 폭을 채운다. Native와 같은 축으로 Web에도 제공(미게시). 줄 안의 비율은 부모 구성이 맡는다 |
|
|
70
73
|
| `selected` | `boolean` | — | 눌림 상태를 갖는 행동 토글(저장·북마크, 켜 둔 도구). Web `aria-pressed`, Native 접근성 state. 선택지 고르기·설정 켜고 끄기에는 쓰지 않는다 |
|
|
71
74
|
| `loading` | `boolean` | `false` | 기존 문구·아이콘을 시각적으로 숨기고 중앙 스피너 하나만 표시한다. 같은 children을 유지해 크기를 보존하고 누름을 막는다. 접근성 이름·포커스는 유지한다(Web `aria-disabled`, Native는 `disableWhileLoading`으로만 옛 disabled 동작) |
|
|
72
75
|
| Web `onClick` | `(event: MouseEvent<HTMLButtonElement>) => void` | — | `disabled`·`loading`·`aria-disabled`이면 부르지 않는다 |
|
|
73
76
|
| Native `onPress` | `(event: GestureResponderEvent) => void` | — | 같은 조건에서 부르지 않는다 |
|
|
74
77
|
| `leading` · `trailing` | `ReactNode` | — | 라벨 앞뒤 아이콘 |
|
|
75
78
|
| `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치. Web 꽉 찬 폭은 `layoutStyle={{ width: "100%" }}` |
|
|
76
|
-
| Native `fullWidth` | `boolean` | `false` | 부모 폭을
|
|
79
|
+
| Native `fullWidth` | `boolean` | `false` | 부모 폭을 채운다. 가로 행동 행에서도 폭을 유지한다(미게시 보완) |
|
|
77
80
|
| Native `loadingLabel` · `renderLoadingIndicator` | `ReactNode` · `(props: { color: string; size: "small" }) => ReactNode` | — | 로딩 중 읽기 문구·스피너 교체 |
|
|
78
81
|
| Native `growWithContent` | `boolean` | `false` | 이미지 등 사용자 콘텐츠가 recipe 높이를 넘어 늘어나게 한다. 문자열·숫자 라벨은 이 옵션 없이도 줄 수에 맞춰 늘어난다 |
|
|
79
82
|
| `ClipboardButton` | `value: string`, `labels: { idle; copied }`, `onCopy?: (value: string) => void`, `onCopyError?: (error: unknown) => void`, `feedbackDuration?`(ms, 2000) | — | Button props를 물려받는다(`tone` 기본 `secondary`, 1.13.0 변경). 쓰는 법은 [CodeBlock](code-block.md) |
|
|
@@ -180,3 +183,40 @@ Button의 loading은 표시와 UI 재실행을 막는 기능이다. 같은 프
|
|
|
180
183
|
- `selected` 버튼 나열 금지: 2026-10-16 사용자 지적 — 예제의 선택 버튼 줄이 테두리 없는 글자처럼 보이고 폭이 제각각이었다.
|
|
181
184
|
- footer 순서 범위: 2026-10-06 독립 지침 재구현에서 footer 순서를 모든 본문 행동에 강제하는 것으로 읽혔다.
|
|
182
185
|
- 선택 테두리 없음: 2026-10-10 사용자 요청으로 선택 시 추가되는 테두리를 제거했다.
|
|
186
|
+
|
|
187
|
+
### 입체 표현과 제품 기본값 (미게시)
|
|
188
|
+
|
|
189
|
+
다에리가 별도의 버튼 프레임·spinner·press/busy/selected 엔진을 구현하던 원인인 옆면 공백을
|
|
190
|
+
`appearance="raised"`로 해소했다. `defineHjmDesignProfile({ controls: { button: { appearance: "raised", shape: "pill" } } })`을
|
|
191
|
+
Provider에 넣으면 HJM 내부의 일반 버튼도 같은 기본값을 받는다. 개별 명시 prop은 프로필보다 우선한다.
|
|
192
|
+
브랜드 팔레트의 primary/dangerFill/borderControl을 옆면의 기초색으로 쓰며 공유 raised.sideShade25%를 적용한다.
|
|
193
|
+
제품에서 별도의 상태/옆면 색을 CSS로 덮지 않는다. FAB처럼 자체 frame을 가진 다른 컴포넌트는 자체 recipe를 유지한다.
|
|
194
|
+
|
|
195
|
+
Root → side + face → leading/label/trailing/spinner 순서다. small3/medium4/large5px(pt)의 옆면은
|
|
196
|
+
buttonRecipe.raised.depths가 소유한다. root의 누름·포커스 목표는 그대로 두고 face만 눌려 내려갔다가120ms로 복귀한다.
|
|
197
|
+
동작 줄이기는 전환 시간을0으로 한다. loading은 face를 유지하고 라벨/아이콘을 시각적으로 숨긴 채 중앙 spinner 하나를 표시한다.
|
|
198
|
+
`loading`과 `disabled`를 함께 넘겨도 busy 동안 포커스를 유지하고 실행을 막는다. Native의 기존 physical disabled 방식은
|
|
199
|
+
`disableWhileLoading` 명시 opt-in만 사용한다. 보조기술에는 busy+disabled를 함께 보고한다.
|
|
200
|
+
|
|
201
|
+
양 Showcase의 Button Raised는 이 공개 API의 실행 예제다. 특정 제품의 색·자산은 포함하지 않는다.
|
|
202
|
+
|
|
203
|
+
## 버튼형 이동 링크 (Web · 미게시)
|
|
204
|
+
|
|
205
|
+
`ButtonLink`는 주요 URL 이동에 Button의 tone/size/shape/appearance와 제품 프로필을 적용하는 실제 `<a>`다.
|
|
206
|
+
일반 문장/보조 이동에는 Link를 사용한다. 번뚝 공개 소개·가이드가 채워진 CTA를 자체 CSS로 그리던 문제 때문에
|
|
207
|
+
Button의 paint resolver를 공유한다. Button은 실제 실행·제출/로딩·토글, ButtonLink는 href/target/rel/download를 보존하는 이동이다.
|
|
208
|
+
버튼 안 링크 또는 링크 안 버튼을 만들지 않는다. Native에는 DOM anchor가 없어 이 Web companion을 추가하지 않으며 기존 Link의 onNavigate 계약을 유지한다.
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
import { ButtonLink } from "@hjmds/react/actions";
|
|
212
|
+
<ButtonLink href="/guides" shape="pill">가이드 보기</ButtonLink>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`leading`/`trailing`, `fullWidth`/`align`/`edge`/`layoutStyle`도 Button과 같다.
|
|
216
|
+
`disabled` 또는 `aria-disabled`는 href를 제거하고 Tab 순서에서 제외하며 click/auxclick 콜백과 이동을 막는다.
|
|
217
|
+
활성 상태는 기본 anchor이므로 Enter, 링크 복사, 새 탭 열기를 유지한다. loading/completion/selected는 이동 API에 제공하지 않는다.
|
|
218
|
+
|
|
219
|
+
`ButtonLink.renderAnchor`(미게시)는 Next 등 프레임워크의 내부 이동 어댑터를 연결한다.
|
|
220
|
+
2026-10-20 다에리의 지도/장소 이동에서 초안 보호·오프라인 차단을 유지하기 위해 추가했다.
|
|
221
|
+
전달받은 anchor props와 `ref`를 실제 링크에 그대로 연결하며, `href`가 없으면 내부 라우팅도 하지 않는다.
|
|
222
|
+
버튼의 색/크기/초점/비활성·수정키/새 탭 의미는 HJM이 소유하고 제품은 목적지·이동 처리를 연결한다.
|