@hjmds/design-contracts 1.19.0 → 1.21.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 (144) hide show
  1. package/dist/authoring-tools.d.ts +38 -0
  2. package/dist/authoring-tools.d.ts.map +1 -0
  3. package/dist/authoring-tools.js +35 -0
  4. package/dist/authoring-tools.js.map +1 -0
  5. package/dist/behaviors.d.ts +34 -3
  6. package/dist/behaviors.d.ts.map +1 -1
  7. package/dist/behaviors.js +2 -0
  8. package/dist/behaviors.js.map +1 -1
  9. package/dist/bottom-navigation.d.ts +10 -0
  10. package/dist/bottom-navigation.d.ts.map +1 -1
  11. package/dist/bottom-navigation.js +10 -0
  12. package/dist/bottom-navigation.js.map +1 -1
  13. package/dist/calendar.d.ts +9 -1
  14. package/dist/calendar.d.ts.map +1 -1
  15. package/dist/calendar.js +4 -2
  16. package/dist/calendar.js.map +1 -1
  17. package/dist/catalog.d.ts +148 -2
  18. package/dist/catalog.d.ts.map +1 -1
  19. package/dist/catalog.js +6 -2
  20. package/dist/catalog.js.map +1 -1
  21. package/dist/chart.d.ts +398 -0
  22. package/dist/chart.d.ts.map +1 -0
  23. package/dist/chart.js +414 -0
  24. package/dist/chart.js.map +1 -0
  25. package/dist/collection-editing.d.ts +38 -0
  26. package/dist/collection-editing.d.ts.map +1 -0
  27. package/dist/collection-editing.js +53 -0
  28. package/dist/collection-editing.js.map +1 -0
  29. package/dist/communication.d.ts +29 -0
  30. package/dist/communication.d.ts.map +1 -0
  31. package/dist/communication.js +11 -0
  32. package/dist/communication.js.map +1 -0
  33. package/dist/component-definitions.d.ts +1 -0
  34. package/dist/component-definitions.d.ts.map +1 -1
  35. package/dist/component-definitions.js +1 -0
  36. package/dist/component-definitions.js.map +1 -1
  37. package/dist/component-recipes.d.ts +25 -0
  38. package/dist/component-recipes.d.ts.map +1 -1
  39. package/dist/component-recipes.js +13 -0
  40. package/dist/component-recipes.js.map +1 -1
  41. package/dist/data-table.d.ts +229 -5
  42. package/dist/data-table.d.ts.map +1 -1
  43. package/dist/data-table.js +242 -6
  44. package/dist/data-table.js.map +1 -1
  45. package/dist/data-workflows.d.ts +70 -0
  46. package/dist/data-workflows.d.ts.map +1 -0
  47. package/dist/data-workflows.js +109 -0
  48. package/dist/data-workflows.js.map +1 -0
  49. package/dist/dataviz.d.ts +7 -6
  50. package/dist/dataviz.d.ts.map +1 -1
  51. package/dist/dataviz.js +7 -6
  52. package/dist/dataviz.js.map +1 -1
  53. package/dist/gooey-navigation.d.ts +7 -0
  54. package/dist/gooey-navigation.d.ts.map +1 -1
  55. package/dist/gooey-navigation.js +10 -0
  56. package/dist/gooey-navigation.js.map +1 -1
  57. package/dist/index.d.ts +2 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +1 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/media-transport.d.ts +16 -0
  62. package/dist/media-transport.d.ts.map +1 -0
  63. package/dist/media-transport.js +17 -0
  64. package/dist/media-transport.js.map +1 -0
  65. package/dist/recipes.d.ts +1 -0
  66. package/dist/recipes.d.ts.map +1 -1
  67. package/dist/recipes.js +1 -0
  68. package/dist/recipes.js.map +1 -1
  69. package/dist/semantic-colors.d.ts +9 -0
  70. package/dist/semantic-colors.d.ts.map +1 -1
  71. package/dist/semantic-colors.js +5 -0
  72. package/dist/semantic-colors.js.map +1 -1
  73. package/dist/showcase.d.ts.map +1 -1
  74. package/dist/showcase.js +3 -1
  75. package/dist/showcase.js.map +1 -1
  76. package/dist/social-compositions.d.ts +19 -0
  77. package/dist/social-compositions.d.ts.map +1 -0
  78. package/dist/social-compositions.js +33 -0
  79. package/dist/social-compositions.js.map +1 -0
  80. package/dist/streaming-answer.d.ts +18 -0
  81. package/dist/streaming-answer.d.ts.map +1 -0
  82. package/dist/streaming-answer.js +61 -0
  83. package/dist/streaming-answer.js.map +1 -0
  84. package/dist/task-controls.d.ts +31 -0
  85. package/dist/task-controls.d.ts.map +1 -0
  86. package/dist/task-controls.js +25 -0
  87. package/dist/task-controls.js.map +1 -0
  88. package/dist/toggle-feedback.d.ts +22 -0
  89. package/dist/toggle-feedback.d.ts.map +1 -0
  90. package/dist/toggle-feedback.js +32 -0
  91. package/dist/toggle-feedback.js.map +1 -0
  92. package/dist/transfer-task.d.ts +13 -0
  93. package/dist/transfer-task.d.ts.map +1 -0
  94. package/dist/transfer-task.js +13 -0
  95. package/dist/transfer-task.js.map +1 -0
  96. package/dist/version.d.ts +1 -1
  97. package/dist/version.js +1 -1
  98. package/dist/version.js.map +1 -1
  99. package/docs/authoring-tools.md +31 -0
  100. package/docs/bottom-navigation.md +11 -0
  101. package/docs/calendar.md +3 -1
  102. package/docs/catalog-freeze.json +5 -0
  103. package/docs/chart.md +206 -0
  104. package/docs/collection-editing.md +83 -0
  105. package/docs/communication.md +35 -0
  106. package/docs/data-table.md +56 -6
  107. package/docs/data-workflows.md +26 -0
  108. package/docs/generated/component-maturity.md +6 -5
  109. package/docs/generated/renderer-evidence.json +95 -5
  110. package/docs/generated/renderer-evidence.md +6 -3
  111. package/docs/generated/showcase-manifest.json +66 -3
  112. package/docs/gooey-navigation.md +20 -0
  113. package/docs/library-gap-analysis.md +4 -2
  114. package/docs/media-transport.md +37 -0
  115. package/docs/social-compositions.md +51 -0
  116. package/docs/streaming-answer.md +57 -0
  117. package/docs/task-controls.md +38 -0
  118. package/docs/toggle-feedback.md +56 -0
  119. package/docs/transfer-task.md +33 -0
  120. package/docs/usage/README.md +15 -4
  121. package/docs/usage/components/activity-chip.md +2 -2
  122. package/docs/usage/components/bottom-navigation.md +14 -1
  123. package/docs/usage/components/button.md +39 -0
  124. package/docs/usage/components/calendar.md +4 -2
  125. package/docs/usage/components/chart.md +277 -0
  126. package/docs/usage/components/data-table.md +92 -18
  127. package/docs/usage/components/screen-layout.md +3 -1
  128. package/docs/usage/components/tabs.md +25 -0
  129. package/docs/usage/components/toast.md +1 -1
  130. package/docs/usage/compositions/action-feedback.md +18 -2
  131. package/docs/usage/compositions/activity-status.md +2 -2
  132. package/docs/usage/compositions/authoring-tools.md +90 -0
  133. package/docs/usage/compositions/collection-editing.md +110 -0
  134. package/docs/usage/compositions/communication.md +100 -0
  135. package/docs/usage/compositions/data-workflows.md +91 -0
  136. package/docs/usage/compositions/media-transport.md +87 -0
  137. package/docs/usage/compositions/social-compositions.md +91 -0
  138. package/docs/usage/compositions/streaming-answer.md +97 -0
  139. package/docs/usage/compositions/task-controls.md +93 -0
  140. package/docs/usage/compositions/toggle-feedback.md +84 -0
  141. package/docs/usage/compositions/transfer-task.md +86 -0
  142. package/docs/usage/compositions/web-additions.md +2 -2
  143. package/docs/usage/screens/common-shell.md +4 -0
  144. package/package.json +67 -1
@@ -0,0 +1,51 @@
1
+ # 팔로우 프로필과 행사 참석 응답
2
+
3
+ 상태: 배포 · 적용: 미게시(1.20.0 이후) · 검토일: 2026-10-15
4
+
5
+ Lucian 054(팔로우 프로필), 059(참석 응답)의 실제 mutation·실패 복구를 소비 앱마다
6
+ 다시 조립하지 않도록 기존 Card, Avatar, Button, AnimatedStatistic과 action-session을 합성한다.
7
+ 사용법과 배치는 [구성 지침](usage/compositions/social-compositions.md)에 있다.
8
+
9
+ ## 선택과 경계
10
+
11
+ - `ProfileScreen`(`screen-flows`)은 summary·편집 행동을 놓는 화면 틀이다. 카드 내부의
12
+ follow 요청·낙관적 수치·실패 복구를 소유하지 않는다. `FollowProfileCard`를 summary나 본문에 넣을 수 있다.
13
+ - 단순 정보 카드·이벤트 목록은 기존 Card/Avatar/Statistic을 쓴다. 요청이 있는 이 두 구성만 새 optional
14
+ `@hjmds/{react,react-native}/social-compositions`에서 제공한다. canonical catalog를 확장하지 않는다.
15
+ - 비동기 엔진은 기존 `action-session`/`useCompositionAction`, 수치 전환은 `AnimatedStatistic`을 재사용한다.
16
+ Web은 기존 `statistic-motion`의 `@number-flow/react` peer가 필요하다. Native는 기존 ContentTransition을 쓴다.
17
+ 루트 entry에 이 선택 의존성을 끌어들이지 않는다.
18
+ - 사진·이름·문구·locale·세션·권한·팔로우/참석 API·캘린더는 제품 소유다. `avatar` 슬롯과 참가자별 `avatar`는
19
+ 제품 이미지 캐시 host도 수용한다. 없으면 기존 Avatar의 이름 fallback을 쓴다. 참가자 미리보기는 확인된 사람만 공급한다.
20
+
21
+ ## 계약
22
+
23
+ `FollowProfileState`는 현재 확인된 `following/count`, `EventAttendanceState`는
24
+ `responseId/count`다. count는 현재 사용자를 포함한 음이 아닌 안전 정수다.
25
+ `AttendanceChoice.attending`으로 어떤 응답이 실제 참석 인원에 포함되는지 제품이 지정한다.
26
+ 미정·불참 사이 전환은 인원을 바꾸지 않는다. null은 아직 응답하지 않은 상태다.
27
+ `resolveFollowProposal`/`resolveAttendanceProposal`은 원본을 바꾸지 않는 동결 proposal을 만든다.
28
+ 동일 응답은 delta 0, 참석 경계만 ±1이며, 잘못된 응답·중복 id·음수/overflow·참석자 0 모순을 거부한다.
29
+
30
+ 동작은 다음 순서다.
31
+
32
+ 1. 제품은 마지막 서버 확정 상태를 `state`로 전달한다. callback에서 실제 요청을 시작한다.
33
+ 2. 요청 중 제안 상태·수치를 한 번 캡처해 낙관적으로 표시한다. 새 서버 count가 들어와도 delta를 재적용하지 않는다.
34
+ 3. 같은 동작의 연속 호출과 참석·캘린더 간 경쟁 요청은 기존 session이 직렬화한다.
35
+ 4. 성공 callback은 서버 snapshot으로 제품 state를 갱신한 뒤 resolve한다. HJM이 임의로 성공 count를 확정하지 않는다.
36
+ 5. 거절하면 현재 confirmed props로 복원하고 오류를 안내한다. 같은 버튼으로 명시적 재시도한다.
37
+ 6. `profileId`/`eventId`가 바뀌면 내부 작업 범위가 remount된다. 오래된 응답은 새 엔티티의 오류·캘린더 완료를 바꾸지 않는다.
38
+ 이미 서버가 수락한 작업을 취소하는 의미는 아니다. 제품 callback도 이전 엔티티의 결과를 맞는 캐시에 기록해야 한다.
39
+
40
+ 캘린더 완료는 `onAddToCalendar`의 실제 성공 후만 표시한다. 파일 생성이나 공유창 열기를
41
+ 기기 캘린더 등록 완료로 resolve하지 않는다. 실패·권한 거절은 reject한다. 성공 후 같은 mount에서는
42
+ 중복 추가를 막는다. remount 이후 중복 방지·이미 추가된 일정 조회·삭제/복구는 제품 어댑터의 책임이다.
43
+ 스토리의 캘린더 callback은 호출 횟수를 기록하는 명시적 예시 어댑터이며 기기 캘린더 저장 증거가 아니다.
44
+
45
+ ## 검증 범위
46
+
47
+ - 계약: immutable delta, 참석 경계, 잘못된 count/choice 거부.
48
+ - Web: 실제 DOM 버튼의 중복 클릭·거절 복구·서버 확정 count·엔티티 교체·캘린더 완료 시점,
49
+ 두 제품 팔레트와 light/dark의 375px 본문 폭.
50
+ - Native: host mock에서 중복 press·실패 복구·엔티티 교체·캘린더 실제 Promise 완료 시점.
51
+ - 기기 캘린더·로그인·제품 서버·Release 실기기·npm 게시·제품 적용은 이 내부 검사의 범위가 아니다.
@@ -0,0 +1,57 @@
1
+ # 스트림 응답과 중지 계약
2
+
3
+ 검토일: 2026-10-15 · Lucian 086 · Storybook 배포 · 미게시
4
+
5
+ `@hjmds/{design-contracts,react,react-native}/streaming-answer` 선택 subpath를 제공한다.
6
+ 고정 답변을 글자 단위로 재생하거나 타이머로 완료시키지 않고 실제 제공자 이벤트를 소비한다.
7
+
8
+ ## 공개 계약
9
+
10
+ | 이름 | 역할 |
11
+ | --- | --- |
12
+ | StreamingAnswer | 실제 응답 대기·delta 표시·중지 요청·재생성·실패 표시 |
13
+ | AnswerGenerationContext | 요청 AbortSignal·시작 시점 previousText·regenerate 여부 |
14
+ | AnswerStreamEvent | `{type:"delta",text:string}` 또는 `{type:"done"}` |
15
+ | consumeAnswerStream | 이벤트 소비·명시적 완료 검사·iterator 정리·부분 텍스트 보존 |
16
+ | AnswerStreamResult | 확인된 `complete` 또는 `stopped`와 실제 텍스트 |
17
+
18
+ `onGenerate(context)`는 AsyncIterable 또는 그것을 담은 Promise를 반환한다. 제품 adapter는 인증·입력·모델·provider protocol·저장·재시도 정책을 소유한다. HJM은 provider SDK를 직접 호출하지 않는다.
19
+
20
+ ## 응답과 완료
21
+
22
+ 1. 생성 버튼은 실제 adapter를 호출하고 `thinking` 상태로 기다린다. 첫 유효 delta 전에는 기존 텍스트를 유지한다.
23
+ 2. 비어 있지 않은 첫 delta부터 이전 답변을 새 응답으로 교체하고 이후 실제 delta만 이어 붙인다. 새 부분 응답과 이전 전체 답변을 합치지 않는다.
24
+ 3. 명시적인 `{type:"done"}`이 있어야 성공한다. 정상 iterator 종료만 있고 done 이벤트가 없으면 잘린 응답으로 오류 처리한다.
25
+ 4. delta 없이 done만 온 성공 응답은 실제 빈 응답이므로 결과를 `""`로 확정한다. 이전 답변을 새 성공 결과처럼 재사용하지 않는다.
26
+ 5. 제공자 오류 또는 불완전 종료 시 마지막 실제 부분 응답을 보존한다. 새 delta가 오기 전 실패하면 기존 답변을 보존한다. 새 delta를 받은 뒤 실패하면 새 부분 응답을 남긴다.
27
+
28
+ `initialText`는 해당 answerId 본문의 초기값이다. 같은 ID에서 initialText를 바꾸는 controlled 갱신 API는 아니다. 응답 항목이 바뀌면 제품이 실제 새 `answerId`를 지정한다.
29
+
30
+ ## 중지와 iterator 정리
31
+
32
+ - 중지 버튼은 실제 요청 signal을 abort하고 `stopping`을 표시한다. 클릭만으로 `stopped`를 확정하지 않는다.
33
+ - adapter는 signal을 실제 fetch·provider transport와 `onGenerate`의 초기 대기에도 연결해야 한다. signal을 무시한 채 끝나지 않는 Promise/iterator를 HJM이 완료시킬 수는 없다.
34
+ - consumer는 iterator `return()`을 기다린다. finally에서 값을 yield하여 `done:false`가 나온 경우에는 `next()`를 계속 받아 실제 종료까지 정리한다. 정리 중 나오는 delta는 화면에 붙이지 않는다.
35
+ - iterator 종료·정리 완료 또는 signal에 대응한 transport AbortError를 확인한 후 중지를 확정한다. 예상하지 못한 정리 오류는 오류로 남긴다.
36
+ - 최종 상태가 결정되기 전에는 중복 생성·재생성을 막는다. 중지·실패 후에도 마지막 실제 텍스트를 남겨 사용자가 읽거나 다시 생성할 수 있다.
37
+ - answerId 변경·unmount는 이전 signal을 abort하고 이전 응답의 화면 갱신을 무시한다. 실제 서버 작업 취소·비용·저장된 응답 처리는 제품 adapter의 책임이다.
38
+
39
+ ## 재생성
40
+
41
+ `onGenerate`는 시작 시점의 `previousText`와 `regenerate`를 받는다. 이전 시도/기존 답변이 있으면 재생성 동작으로 표시한다. 제품은 실제 대화·메시지 ID·요청 세대에 연결해 새 생성 요청을 구성한다. 이전 답변을 먼저 삭제하거나 동일 고정 문자열을 타이머로 다시 재생하지 않는다.
42
+
43
+ ## 표시와 접근성
44
+
45
+ 모든 버튼·상태 문구와 제목은 제품이 전달한다. 대기는 해당 구성 영역 중앙에 Progress 또는 선택적 `thinkingIndicator`로 표시한다. 오류·중지·완료 안내도 해당 영역 가운데 정렬한다. Web은 상태/오류 role을, Native는 alert와 live region을 사용한다. 본문은 기본 Text로 표시하며 Web은 줄바꿈과 긴 문자열 줄바꿈을 보존한다. Markdown 등 별도 표현은 `renderText`로 제품이 제공한다.
46
+
47
+ ## 실행 예제의 범위
48
+
49
+ Web `showcase/web/src/patterns/StreamingAnswer.stories.tsx`와 Native `showcase/native/src/StreamingAnswer.stories.tsx`는 실제 `text/plain` URL을 입력받아 요청한다. Web은 fetch, Native는 `expo/fetch`로 실제 스트림 body를 읽는다. 기본 URL·고정 응답·가짜 지연을 넣지 않는다.
50
+
51
+ `showcase/shared/streaming-answer-example.ts`의 `readTextAnswer`는 성공한 plain UTF-8 응답을 TextDecoder로 읽고 실제 byte chunk를 delta로 전달한다. 읽기 종료와 decoder flush 뒤에만 done을 보낸다. signal에 reader 취소를 연결하고 finally에서 listener·reader lock을 정리한다.
52
+
53
+ 이 plain-text adapter의 transport 종료는 예제 응답의 완료 조건이다. 이를 AI/SSE/WebSocket 제공자의 완료 조건으로 복사하지 않는다. 실제 제품 adapter는 해당 protocol의 delta·완료·오류 이벤트를 파싱하고 명시적인 provider 완료를 done으로 변환해야 한다. AI 인증·프롬프트·세션·모델 연결·운영 저장·실제 AI 응답 검증은 예제에 포함되지 않는다.
54
+
55
+ ## 근거와 확인 범위
56
+
57
+ 원본 086의 고정 답변 타이머 재생을 실제 AsyncIterable 계약으로 바꾸었다. 계약 검사는 실제 delta·명시적 완료·불완전 종료·빈 재생성·finally-yield 정리를 다룬다. Web browser 검사는 실제 UTF-8 응답 byte 수신·중지·실패 보존을, Native host 검사는 실제 이벤트 처리와 ID 변경 시 abort·늦은 응답 차단을 다룬다. 검사 실행 결과는 도입 기록에 따로 남긴다. Native 기기·실제 AI provider·npm 게시·제품 적용은 별도 확인 범위다.
@@ -0,0 +1,38 @@
1
+ # 선택 뒤 실제 작업을 실행하는 구성
2
+
3
+ 검토일: 2026-10-15 · Storybook 배포 · 미게시(1.20.0 이후) · Web/Native
4
+
5
+ Lucian 010·036·037·056의 공통 작업 흐름을 기존 Menu/Button/PasswordField/Progress/RadioGroup/Card로 합성한다.
6
+ 새 메뉴·입력·작업 상태 엔진을 만들지 않는다. 공개 진입점은 `@hjmds/{react,react-native}/task-controls`,
7
+ 계산·타입은 `@hjmds/design-contracts/task-controls`다.
8
+
9
+ | API | 의미와 기존 API를 직접 쓰는 경우 |
10
+ | --- | --- |
11
+ | SplitAction | 선택 모드와 주요 실행을 나누고 실행 중 잠금·실패 복구를 묶음. 단순 더보기 목록은 Menu |
12
+ | AccountSwitcher | 확인된 계정 표시, 세션 전환 callback, 실패 후 현재 계정 유지. 단순 필드 값은 Select |
13
+ | PasswordRulesField | 기존 PasswordField의 공개 입력·자동완성·보기 계약 + 제품 규칙 목록·충족 Progress. 규칙 표시가 필요 없으면 PasswordField |
14
+ | PricingChoice | controlled RadioGroup 요금 주기 선택 + 실제 가격·혜택·계속 행동과 실패 복구. 결제 없는 일반 선택은 RadioGroup |
15
+
16
+ ## 데이터와 실제 확정
17
+
18
+ 모든 옵션은 중복 없는 stable id를 갖고 현재 id는 목록에 있어야 한다. disabled 옵션은 실행하지 않는다.
19
+ SplitAction의 onModeChange는 실행하지 않는다. onExecute에는 누른 시점의 modeId를 전달하며 Promise resolve/reject를 기다린다.
20
+ AccountSwitcher의 accountId는 서버에서 확인된 현재 세션이다. 메뉴를 고른 것만으로 바꾸지 않고 제품 onSwitch가 실제 세션 전환 뒤 controlled prop을 갱신한다.
21
+ 기존 메뉴를 먼저 닫은 뒤 실제 실행/전환 callback을 호출한다. 실패해도 현재 선택·계정은 보존하고 같은 행동으로 재시도한다.
22
+ 공통 action-session을 사용해 진행 중 중복 요청과 unmount 뒤 응답을 분리한다. 제품은 권한·요청 멱등성·외부 세션 경합을 소유한다.
23
+ 다른 엔티티/인증 세션으로 재사용할 때 제품 host key를 바꿔 이전 작업의 수명을 끝낸다.
24
+
25
+ PasswordRule은 pure test(value) predicate와 현지화 label이다. 충족 개수는 정책 조건의 수이며 엔트로피/유출 이력/실제 보안 강도가 아니다.
26
+ 비밀번호를 저장·로그·전송하지 않는다. 서버 규칙 검증은 제품 소유다. 입력은 controlled이며 실패 시 초안을 자동 삭제하지 않는다.
27
+
28
+ PriceOption은 실제 `amountMinor`, ISO 4217 형식의 `currency`, `interval`(month/year), 양의 `intervalCount`, 실제 benefits를 받는다.
29
+ 표시 formatPrice는 해당 가격과 과금 주기를 그대로 표현해야 한다. 월 환산액·할인·혜택을 추정하지 않는다.
30
+ 선택 변경으로 결제를 실행하지 않는다. onContinue에 동결된 옵션 snapshot(혜택 배열 포함)을 전달하며 결제·동의·스토어 상품 검증은 실제 제품 흐름이 소유한다.
31
+ Showcase 가격·계정은 fixture이며 실제 결제·인증을 수행했다는 증거가 아니다.
32
+
33
+ ## 문구·상태·플랫폼
34
+
35
+ 제목·버튼·실패·규칙별 상태 문구·가격 formatter는 제품에서 공급한다. 실패 문구는 해당 구성 내부 중앙에 표시한다.
36
+ Web Menu는 anchored popover, Native Menu는 기존 Modal과 dismissLabel을 사용한다. 선택 강조와 실제 세션 확정을 동일시하지 않는다.
37
+ Native에는 OS/기기 키보드·자동완성 계약이 기존 PasswordField를 통해 유지된다.
38
+ Web/Native 공개 이름과 데이터 계약은 같으며 입력 prop의 host 타입은 각 기존 PasswordFieldProps를 따른다.
@@ -0,0 +1,56 @@
1
+ # 선택 버튼의 실제 반응과 수치
2
+
3
+ 상태: 배포 · 적용: 미게시(1.20.0 이후) · 검토일: 2026-10-15
4
+
5
+ Lucian 006 구독 리플·012 북마크 리본·022 선택한 좋아요 burst는 같은 counted selection이다.
6
+ 구독·북마크·좋아요의 별도 상태 엔진을 만들지 않고, 기존 Button.selected와 AnimatedStatistic,
7
+ 기존 action-session의 낙관적 제안·직렬화·실패 복구를 한 optional 구성으로 합성한다.
8
+ 배치와 코드는 [사용 지침](usage/compositions/toggle-feedback.md)에 있다.
9
+
10
+ ## 선택 기준과 의존성
11
+
12
+ - 단순 selected 표현은 기존 `Button`/`IconButton`, 프로필 정보가 같이 필요하면
13
+ `FollowProfileCard`, 메시지 반응 목록이면 `ReactionPicker`를 쓴다.
14
+ - `NotificationBell`은 읽지 않은 알림 인원의 일방향 알림이다. 사용자 구독 상태를 바꾸는
15
+ mutation과 구분한다. `Celebration`은 전체 뷰포트 canvas/Confetti 계층이라 버튼의 작은
16
+ 리플·리본·방사형 burst를 대신하지 않는다. 새 confetti 엔진이나 전역 canvas를 추가하지 않는다.
17
+ - `ToggleFeedbackButton`은 `@hjmds/{react,react-native}/toggle-feedback`에서만 제공한다.
18
+ 카탈로그에 세 도메인 이름을 추가하지 않는다. Web은 AnimatedStatistic의 기존
19
+ `@number-flow/react`, Native는 기존 `react-native-svg` peer를 사용한다.
20
+ - 아이콘은 제품 `icon` 또는 `renderIcon({selected,pending})` 슬롯을 우선 사용한다.
21
+ 기본 도형은 해당 effect의 벨·북마크·하트이며 장식 레이어다. 문구·locale·브랜드는 제품과 HJM semantic token 소유다.
22
+
23
+ ## 확정 상태와 요청
24
+
25
+ `ToggleFeedbackState`의 selected/count는 제품 서버가 확인한 상태다. count는 현재 사용자를
26
+ 포함하는 음이 아닌 안전 정수다. `resolveToggleFeedbackProposal`은 중복 선택 delta=0,
27
+ 선택 경계 delta=±1인 동결 snapshot을 만들며 불가능한 count·overflow를 거부한다.
28
+ 이 snapshot을 pending 동안만 표시한다. 새 confirmed count가 도착해도 delta를 다시 더하지 않는다.
29
+
30
+ `onSelectedChange`는 실제 요청을 실행하고 서버 결과로 제품 state를 갱신한 뒤 resolve한다.
31
+ 거절하면 현재 confirmed props로 돌아가며 오류를 읽어 주고 다시 선택할 수 있다.
32
+ 기존 private `useCompositionAction<T=string>`이 shared action-session을 재사용하므로
33
+ 동일 tick 연속 호출·중복 서버 요청을 막는다. default string은 기존 소비처와 호환된다.
34
+ profile/event 구성도 동일 typed helper를 쓰며 문자열 JSON 직렬화가 필요 없다.
35
+ `entityId` 교체는 내부 범위를 remount해 이전 응답이 새 카드 오류를 바꾸지 않게 한다.
36
+ 실제 서버 취소·캐시 반영 위치·인증·idempotency는 제품이 책임진다.
37
+
38
+ ## 모션
39
+
40
+ - 선택하지 않음→선택에서만 효과를 한 번 시작한다. 초기 selected, 해제, 테마/활성 복귀는 재생하지 않는다.
41
+ - ripple: SVG circle 반경이 확대되면서 사라진다. ribbon: 아이콘이 위로 당겨졌다 제자리로 돌아온다.
42
+ burst: 하트 주변 8방향 선분이 퍼지며 사라진다. 기본 아이콘의 최종 채움은 selected와 함께 유지된다.
43
+ - 총 시간은 `toggleFeedbackRecipe.duration = motion.slow = 320ms`. 공통 geometry resolver를
44
+ Web requestAnimationFrame과 Native Animated.Value가 host SVG/transform에만 적용한다.
45
+ 프레임마다 React state를 갱신하거나 가짜 count를 확정하지 않는다.
46
+ - reducedMotion, `active=false`, document hidden/AppState background에서는 중간 장식을 제거한다.
47
+ 앱이 돌아와도 소비된 선택 효과를 다시 재생하지 않는다. 해제는 장식 없이 상태·수치만 바꾼다.
48
+ - counter 전환은 기존 AnimatedStatistic이 맡으며 접근성에는 최종 숫자만 전달한다.
49
+
50
+ ## 검증 범위
51
+
52
+ 계약 검사는 실제 count delta·동결과 세 효과 geometry를 확인한다. Web은 DOM에서 요청 거절·중복·
53
+ 엔티티 교체, 세 효과의 실제 host 변화와 프레임 React commit 부재, 백그라운드 정지·감소 모션,
54
+ 두 팔레트 light/dark의 좁은 본문을 검사한다. Native는 host mock으로 각 효과 시작·해제의 침묵,
55
+ 감소 모션·활성화·AppState 정지와 counted mutation 실패 복구를 확인한다.
56
+ 실기기 Release 성능·제품 서버·npm 게시·제품 적용은 이 로컬 검사의 증거가 아니다.
@@ -0,0 +1,33 @@
1
+ # 실제 전송 진행과 취소
2
+
3
+ 검토일: 2026-10-15 · Storybook 배포 · Web/Native · 미게시(1.20.0 이후)
4
+
5
+ Lucian 004·083의 시간 기반 진행 시연을 실제 전송량·작업 종료·취소 신호와 연결하기 위해 기존 Button/Progress를 합성한다.
6
+ 단순 외부 controlled 진행 표시만 필요하면 Progress를 직접 쓴다. TransferTask는 시작 요청·중복 방지·AbortSignal·실패 재시도가 함께 필요할 때 쓴다.
7
+ 공개 진입점은 `@hjmds/{react,react-native}/transfer-task`, 계약은 `@hjmds/design-contracts/transfer-task`다.
8
+
9
+ ## 실제 상태
10
+
11
+ onTransfer는 `{signal,report}`를 받는 실제 비동기 작업이며 실제 작업이 끝날 때 resolve해야 한다.
12
+ 처음에는 응답 대기(미정 진행)다. report({completed,total:null})은 실제 완료량을 유지하고 전체 크기가 미정임을 표현한다.
13
+ 전체량을 알게 되면 같은 Progress host에서 determinate로 바꾼다. 전체량을 얻었다고 completed를 늘리지 않는다.
14
+ completed는 유한한 비음수이고 한 작업 안에서 줄어들 수 없다. total은 null 또는 양의 유한 값이며 completed 이상이어야 한다.
15
+ 전송량이 total과 같더라도 Promise가 끝나기 전에는 성공이라고 표시하지 않는다. 업로드 수신·서버 확정 등 제품의 실제 완료 조건을 기다린다.
16
+ 성공·실패 뒤 늦게 들어온 report와 unmount 뒤 report는 무시한다. 실패 시 마지막 실제 전송량을 보존한다.
17
+
18
+ ## 취소와 실패
19
+
20
+ 취소 버튼은 AbortController를 요청하고 취소 확인 중으로 표시한다. 실제 adapter가 AbortError를 reject해야 취소됨으로 확정한다.
21
+ 취소 요청과 경합한 실제 resolve는 완료로 표시한다. 요청만으로 취소 성공을 꾸미지 않는다.
22
+ onTransfer가 abort를 무시하면 계속 확인 중이다. 제품은 실제 네트워크/작업의 취소를 구현해야 한다.
23
+ 취소/실패 뒤 새 시작은 새 신호·새 카운터로 진행한다. 재실행의 멱등성·부분 파일/서버 데이터 처리는 제품 소유다.
24
+ 숨긴/교체한 host는 key로 수명을 끝내며 unmount 때 실제 signal을 abort한다. 다른 작업의 상태를 이 컴포넌트에 재사용하지 않는다.
25
+
26
+ ## 소유권과 검증 경계
27
+
28
+ 문구와 실제 byte/item 등 단위 formatter는 제품에서 공급한다. 컴포넌트는 네트워킹·저장·재시도 타이머를 내장하지 않는다.
29
+ Progress의 기존 접근성·로딩 크기 유지 Button 계약을 재사용한다. 실패/취소/완료 문구는 구성 내부 중앙에 배치한다.
30
+ Showcase는 기존 영상 asset의 실제 응답 body를 읽는다. Web fetch, Native 기존 Expo의 expo/fetch streaming adapter를 사용한다.
31
+ Content-Length가 압축된 크기이면 decoded stream의 분모로 쓰지 않는다. fixture도 가짜 퍼센트·시간 증가를 사용하지 않는다.
32
+ 응답 byte 수신과 OS 파일 저장·공유·서버 확정은 별개다. Showcase는 파일 저장/제출을 수행했다고 주장하지 않는다.
33
+ Native 기기 네트워크 실행과 소비 제품 연결은 별도 확인 대상이다.
@@ -6,7 +6,7 @@
6
6
  버튼 위치에서 시작해 구성·컴포넌트 지침으로 내려가고, 값은 토큰 지침에서 고른다.
7
7
  [선택 기준](SELECTION.md)으로 그대로 사용·공개 슬롯 확장·제품 전용 구성을 고른다. 설치 버전의 지침을 본다: `node_modules/@hjmds/design-contracts/docs/usage/`.
8
8
  제공 기준은 확인한 기본 API 버전이며 최초 출시 버전이 아니다. 구성·화면은 각 API 지침의 제공 버전을 확인한다.
9
- API 안정성은 1.19.0의 생성 manifest에서 읽은 정규(canonical) API 기준이다. 동반·선택형 확장은 개별 계약을 확인한다.
9
+ API 안정성은 1.20.0의 생성 manifest에서 읽은 정규(canonical) API 기준이다. 동반·선택형 확장은 개별 계약을 확인한다.
10
10
  Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품 반영을 의미하지 않는다.
11
11
  `분류`는 Storybook 제목 `<배포|실험>/<단계>/<분류>/<항목>`의 셋째 마디이고, 토큰·구성·화면은 Storybook 메뉴와 같은 순서다.
12
12
 
@@ -33,7 +33,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
33
33
  | 지침 | 분류 | 언제 쓰나 | 제공 기준 | 지원 | API 안정성 | Storybook |
34
34
  | --- | --- | --- | --- | --- | --- | --- |
35
35
  | [Accordion](components/accordion.md) | 데이터 표시 | 서로 관계가 있는 여러 접힘 항목을 한 그룹으로 보일 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
36
- | [ActivityChip](components/activity-chip.md) | 상태와 알림 | 백그라운드에서 돌고 있는 작업 하나(검수·업로드·동기화)의 상태를 헤더의 빈 자리에 짧게 보여 주고, 누르면 그 작업의 진행 패널(Toast 패널)을 여닫을 때 쓴다. | 미게시(1.17.2 이후) | Web · Native | 개별 API 계약 참조 | 실험 |
36
+ | [ActivityChip](components/activity-chip.md) | 상태와 알림 | 백그라운드에서 돌고 있는 작업 하나(검수·업로드·동기화)의 상태를 헤더의 빈 자리에 짧게 보여 주고, 누르면 그 작업의 진행 패널(Toast 패널)을 여닫을 때 쓴다. | 미게시(1.17.2 이후) | Web · Native | 개별 API 계약 참조 | 배포 |
37
37
  | [ActivityHeatmap](components/activity-heatmap.md) | 데이터 표시 | 최대 1년(366일) 범위의 일별 활동량을 한눈에 보여 주는 읽기 전용 개요에 쓴다. | 1.12.1 | Web · Native | 개별 API 계약 참조 | 배포 |
38
38
  | [Affix](components/affix.md) | 기반 기능 | Web에서 스크롤하는 동안 요약·필터·저장 버튼 같은 작은 영역을 가장 가까운 스크롤 조상의 상단에 붙여 두고, 부모가 끝나면 함께 풀리게 할 때 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
39
39
  | [Agreement](components/agreement.md) | 입력 | 가입·결제·서비스 시작 앞의 약관 동의 묶음에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
@@ -54,6 +54,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
54
54
  | [Card](components/card.md) | 데이터 표시 | 문서 metadata·미리보기·내보내기 상태는 실험 구성 DocumentResource를 먼저 대조한다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
55
55
  | [Carousel](components/carousel.md) | 데이터 표시 | 한 번에 카드 하나만 보이고 사용자가 순서대로 넘겨 보는 유한한 묶음에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
56
56
  | [Celebration](components/celebration.md) | 구성/직접 조작과 모션 | 목표 달성, 첫 완료처럼 드물게 일어나는 성공 순간에 한 번 터지는 색종이 효과에 쓴다. | 1.12.1 | Web · Native | 개별 API 계약 참조 | 배포 |
57
+ | [Chart](components/chart.md) | 데이터 표시 | 수치의 흐름·비교·몫·모양·관계를 그림으로 읽게 할 때 쓴다. | 미게시(1.20.0 이후) | Web · Native | Web beta / Native beta | 배포 |
57
58
  | [ChatMessage](components/chat-message.md) | 구성/정보 표시 | DM·대화 타임라인의 메시지 한 개에 쓴다. | 1.17.1 | Web · Native | 개별 API 계약 참조 | 배포 |
58
59
  | [ChatScreen](components/chat-screen.md) | 화면/소통 | DM·대화방처럼 헤더, 메시지 타임라인, 하단 작성창으로 이루어진 화면 한 장에 쓴다. | 1.17.1 | Web · Native | 개별 API 계약 참조 | 배포 |
59
60
  | [Checkbox](components/checkbox.md) | 입력 | 독립된 예/아니오 하나를 고르는 항목에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
@@ -69,7 +70,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
69
70
  | [ContentTransition](components/content-transition.md) | 시각 효과 | 같은 자리의 내용이 상태에 따라 바뀔 때(필터 결과 패널, 단계별 본문) 새 내용이 짧게 나타나도록 감싼다. | 1.12.1 | Web · Native | 개별 API 계약 참조 | 배포 |
70
71
  | [ContextMenu](components/context-menu.md) | 탐색 | Web에서 제품이 소유한 영역(카드·목록 행·캔버스)의 우클릭·길게 누르기·Shift+F10에 명령 목록을 띄울 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native unsupported | 배포 |
71
72
  | [CounterBadge](components/counter-badge.md) | 데이터 표시 | 읽지 않은 알림·메시지·장바구니 수처럼 **셀 수 있는 개수**를 아이콘·행 옆에 작게 보일 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
72
- | [DataTable](components/data-table.md) | 데이터 표시 | 여러 행의 데이터를 열로 맞춰 훑고, 열 기준으로 정렬하거나 행을 골라 일괄 작업할 때 쓴다(Web). 정렬·필터 실행, 페이지 나누기는 제품이 소유한다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
73
+ | [DataTable](components/data-table.md) | 데이터 표시 | 여러 행의 데이터를 열로 맞춰 훑고, 열 기준으로 정렬하거나 행을 골라 일괄 작업할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native beta | 배포 |
73
74
  | [DatePicker](components/date-picker.md) | 입력 | 폼·필터 자리에서 날짜 **하나**를 고를 때 쓴다(생년월일, 방문일, 시작일 필터). 평소에는 필드 트리거만 보이고, 누르면 Web은 필드에 붙은 팝오버, Native는 Sheet 안에 같은 달력 격자를 연다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
74
75
  | [DateRangePicker](components/date-range-picker.md) | 입력 | 시작~끝 날짜 **구간**을 고를 때 쓴다(통계 기간, 예약, 검색 필터). 필드 트리거나 오버레이 없이 달력 격자를 그 자리에 펼쳐 둔다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
75
76
  | [DescriptionList](components/description-list.md) | 데이터 표시 | 라벨-값 쌍의 묶음을 보여 줄 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
@@ -183,10 +184,13 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
183
184
  | [단계별 드로어](compositions/family-drawer.md) | 입력과 작성 | 초대 → 설정 → 확인처럼 짧은 단계 2~5개를 현재 화면을 떠나지 않고 하단 시트 안에서 차례로 진행할 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
184
185
  | [닫았다 열고 초안 이어쓰기](compositions/interaction-flow-draft.md) | 입력과 작성 | 메모·댓글처럼 시트에서 쓰던 글을 저장하지 않고 닫았다가 다시 열었을 때, 쓰던 초안을 그대로 이어 쓰게 할 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
185
186
  | [댓글 작성](compositions/purpose-input-comment.md) | 입력과 작성 | 게시물·기록 아래에서 댓글이나 특정 댓글에 대한 답글을 남기고, 실패하면 글과 답글 대상을 그대로 남겨 다시 등록하게 할 때 쓴다. | 구성 API 지침 참조 | Web · Native | 구성 API별 | 배포 |
187
+ | [데이터 해석과 가져오기](compositions/data-workflows.md) | 입력과 작성 | CSV 열 연결, 순차 퍼널, 가입 시점별 잔존율, 지표 상관, 목표, 분포 구간 탐색에 쓴다. | 미게시(1.20.0 이후) | Web · Native | 구성 API별 | 배포 |
186
188
  | [메시지 작성](compositions/purpose-input-message.md) | 입력과 작성 | 대화 화면 하단에서 글과 사진 여러 장을 함께 보내고, 실패하면 글·사진·답장 대상을 그대로 남겨 다시 보내게 할 때 쓴다. | 구성 API 지침 참조 | Web · Native | 구성 API별 | 배포 |
189
+ | [목록 편집과 복구](compositions/collection-editing.md) | 입력과 작성 | 목록의 제목을 바로 고치거나, 입력 행을 늘리거나, 여러 항목에 같은 행동을 하거나, 빈 검색에서 조건을 풀거나, 표의 열을 설정할 때 쓴다. | 미게시(1.20.0 이후) | Web · Native | 구성 API별 | 배포 |
187
190
  | [버튼에서 이어지는 편집](compositions/origin-dialog.md) | 입력과 작성 | 현재 화면의 항목을 짧게 편집하고 돌아올 때 출발 위치를 시각적으로 연결한다. | 1.14.0 | Web · Native | 구성 API별 | 배포 |
188
191
  | [빠른 메모 작성](compositions/floating-action-button.md) | 입력과 작성 | 스크롤되는 기록 목록 위에 떠 있는 생성 버튼으로 짧은 입력 대화상자를 열고, 저장하면 새 항목을 목록 맨 위에 넣을 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
189
192
  | [선택 내용 검토와 수정](compositions/reference-review.md) | 입력과 작성 | 선택 내용을 검토하고 수정 후 명시적으로 확정 흐름이 필요할 때 쓴다. | 구성 API 지침 참조 | Web · Native | 구성 API별 | 배포 |
193
+ | [선택과 실제 실행](compositions/task-controls.md) | 입력과 작성 | 선택만 하는 화면과 선택 뒤 실제 요청을 실행하는 화면의 상태를 혼동하지 않도록 도입했다. | 미게시(1.20.0 이후) | Web · Native | 구성 API별 | 배포 |
190
194
  | [인증번호 확인과 다시 입력](compositions/stea-otp-verify.md) | 입력과 작성 | 문자·메일로 받은 숫자 인증번호를 입력하고 서버 확인을 기다린 뒤, 틀리면 남은 횟수를 보여 주고 다시 받게 하는 흐름에 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
191
195
  | [입력 시트](compositions/input-sheet.md) | 입력과 작성 | 현재 화면 위에 하단 시트를 띄워 짧은 입력(이름 바꾸기, 메모 한 줄)을 받고, 키보드가 올라와도 본문을 스크롤하며 완료 버튼에 닿게 할 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
192
196
  | [입력을 유지하는 도구](compositions/context-toolbar.md) | 입력과 작성 | 작성 중인 입력을 보존한 채 선택적 도구를 펼쳐야 할 때 쓴다. | 1.14.0 | Web · Native | 구성 API별 | 배포 |
@@ -197,6 +201,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
197
201
  | [사진 촬영과 앨범 선택](compositions/photo-source.md) | 선택과 필터 | 명시적으로 선택 후 플랫폼 picker 실행 흐름이 필요할 때 쓴다. | 구성 API 지침 참조 | Web · Native | 구성 API별 | 배포 |
198
202
  | [선택 후 적용·취소](compositions/interaction-flow-apply.md) | 선택과 필터 | 표시 방식·정렬·필터처럼 시트에서 여러 번 바꿔 본 뒤 적용을 눌러야 화면에 반영되고, 취소하거나 닫으면 기존 선택을 유지해야 할 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
199
203
  | [시간 선택](compositions/time-selection.md) | 선택과 필터 | 알림 시각·마감 시각처럼 하루 안의 시각 하나를 시·분 두 Select로 나눠 고르게 할 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
204
+ | [펼치는 검색과 실제 메시지 답장](compositions/communication.md) | 선택과 필터 | 상단 검색 버튼에서 검색 영역을 펼치거나 채팅의 실제 메시지를 선택해 답장을 전송할 때 쓴다. | 미게시(1.20.0 이후) | Web · Native | 구성 API별 | 배포 |
200
205
  | [보관함과 페이지 이동](compositions/web-navigation.md) | 탐색과 이동 | Web에서 상위 보관함 → 하위 모음으로 들어가고, 그 모음의 긴 목록을 페이지 단위로 넘겨 보는 탐색에 쓴다. | 1.12.1 | Web | 구성 API별 | 배포 |
201
206
  | [펼침과 메뉴](compositions/disclosure.md) | 탐색과 이동 | Web에서 내용을 숨겼다 펼치거나(Collapsible), 대상에 붙은 작업 메뉴를 우클릭·키보드로 열거나(ContextMenu), 데스크톱 앱처럼 상단 메뉴 막대를 두는(Menubar) 세 방식을 각각 보여 주는 모음이다. | 1.12.1 | Web | 구성 API별 | 배포 |
202
207
  | [대화 메시지](compositions/common-message.md) | 정보 표시 | 말풍선 하나하나에 반응·답장·원문 이동·전송 실패 후 다시 보내기를 붙일 때 쓴다. | 구성 API 지침 참조 | Web · Native | 구성 API별 | 배포 |
@@ -210,19 +215,25 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
210
215
  | [질감 비교](compositions/texture-comparison.md) | 정보 표시 | 기존 반복 점 grain과 불규칙한 정적 noise를 같은 배경·강도로 비교할 때 쓴다. | 1.14.0 | Web · Native | 구성 API별 | 배포 |
211
216
  | [추가해도 유지되는 목록](compositions/live-list.md) | 정보 표시 | 입력 중인 목록에 새 데이터가 추가되거나 순서가 바뀌어도 기존 초안과 항목의 정체성을 유지할 때 쓴다. | 1.14.0 | Web · Native | 구성 API별 | 배포 |
212
217
  | [카드 묶음과 긴 목록](compositions/data-layouts.md) | 정보 표시 | 많은 항목을 화면에 늘어놓을 방식을 고를 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
218
+ | [팔로우 프로필과 행사 참석 응답](compositions/social-compositions.md) | 정보 표시 | 프로필 팔로우나 행사 참석 응답의 실제 요청과 수치의 실패 복구가 필요할 때 사용한다. | 미게시(1.20.0 이후) | Web · Native | 구성 API별 | 배포 |
213
219
  | [그림과 시작 안내](compositions/illustrated-outcome.md) | 피드백과 복구 | 빈 목록에서 시작을 안내하고 짧은 온보딩을 거쳐 결과를 보여 줄 때 쓴다. | 1.14.0 | Web · Native | 구성 API별 | 배포 |
214
- | [백그라운드 작업 상태](compositions/activity-status.md) | 피드백과 복구 | 사용자가 다른 화면을 보는 동안 계속되는 작업 하나(검수·생성·업로드)의 진행을 헤더의 작은 칩으로 알리고, 칩을 누르면 같은 작업의 패널 Toast가 열려 단계와 결과를 보여 줄 때 쓴다. | 미게시(1.17.2 이후) | Web · Native | 구성 API별 | 실험 |
220
+ | [백그라운드 작업 상태](compositions/activity-status.md) | 피드백과 복구 | 사용자가 다른 화면을 보는 동안 계속되는 작업 하나(검수·생성·업로드)의 진행을 헤더의 작은 칩으로 알리고, 칩을 누르면 같은 작업의 패널 Toast가 열려 단계와 결과를 보여 줄 때 쓴다. | 미게시(1.17.2 이후) | Web · Native | 구성 API별 | 배포 |
215
221
  | [버튼 완료 피드백](compositions/action-feedback.md) | 피드백과 복구 | 입력을 유지하며 저장의 진행·성공·재시도 가능 실패를 보여 줄 때 쓴다. | 1.14.0 | Web · Native | 구성 API별 | 배포 |
216
222
  | [변경 저장과 이탈 확인](compositions/reference-settings.md) | 피드백과 복구 | 저장값과 편집 초안을 비교해 이탈 확인 흐름이 필요할 때 쓴다. | 구성 API 지침 참조 | Web · Native | 구성 API별 | 배포 |
217
223
  | [보관과 실행 취소](compositions/action-recovery-undo.md) | 피드백과 복구 | 보관·숨기기·목록에서 빼기처럼 제품이 역연산을 제공하는 작업 뒤에, 같은 자리에서 실행 취소를 주고 그 복구 요청이 성공해야 화면을 되돌릴 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
218
224
  | [선택과 오류 복구](compositions/upload-recovery.md) | 피드백과 복구 | 파일 선택과 전송 상태의 취소·재시도를 연결할 때 쓴다. | 1.14.0 | Web · Native | 구성 API별 | 배포 |
225
+ | [스트림 응답과 중지](compositions/streaming-answer.md) | 피드백과 복구 | 실제 provider가 보내는 응답 delta를 표시하고 중지·재생성·오류를 같은 영역에서 제공할 때 쓴다. | 미게시(1.20.0 이후) | Web · Native | 구성 API별 | 배포 |
219
226
  | [저장과 재시도](compositions/action-recovery-save.md) | 피드백과 복구 | 입력한 내용을 서버에 저장하는 폼 한 덩어리에서 저장 중 중복 실행을 막고, 실패하면 입력을 지우지 않은 채 제출했던 값 그대로 다시 보낼 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
227
+ | [전송 진행과 취소](compositions/transfer-task.md) | 피드백과 복구 | 실제 다운로드·업로드·작업의 시작/대기/진행·취소·실패 복구를 한 구성으로 제공할 때 쓴다. | 미게시(1.20.0 이후) | Web · Native | 구성 API별 | 배포 |
220
228
  | [중단해도 남는 현재 상태](compositions/expo-interactions.md) | 피드백과 복구 | 버튼으로 상태를 빠르게 바꾸거나 전환 도중 내용을 닫아도 현재 상태가 바로 보이고 남아야 하는 영역에 쓴다. | 1.12.1 | Native | 구성 API별 | 배포 |
221
229
  | [즉시 반영과 복구](compositions/action-recovery-optimistic.md) | 피드백과 복구 | 북마크·좋아요·알림 켜기처럼 되돌려도 피해가 없는 저위험 토글을 누르는 즉시 화면에 반영하고, 서버가 실패하면 직전 확인 값으로 되돌릴 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
222
230
  | [처리 단계와 재시도](compositions/stea-order-progress.md) | 피드백과 복구 | 주문·신청처럼 서버가 단계를 하나씩 확정하는 처리 과정을 보여 주고, 확정에 실패하면 같은 단계를 다시 요청하게 할 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
223
231
  | [캐릭터와 시작 행동](compositions/stea-pixel-empty.md) | 피드백과 복구 | 아직 만든 것이 없는 첫 빈 화면에 제품 캐릭터를 움직여 보이고 첫 행동 하나로 이끌 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
224
232
  | [끌기·밀기·화면 전환](compositions/interaction-adapters.md) | 직접 조작과 모션 | 순서 바꾸기·행 작업·내용 전환·카드 넘기기·달성 축하·카드 확대 화면 전환 같은 선택형 상호작용 어댑터를 한 화면에서 함께 쓸 때, 각 어댑터를 어디에 놓고 무엇으로 감싸야 하는지 확인하는 구성이다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
225
233
  | [높이가 이어지는 패널](compositions/adaptive-content.md) | 직접 조작과 모션 | 패널의 길이가 달라질 때 아래 행동이 새 높이로 이동해야 하는 작은 내용 영역에 쓴다. | 1.14.0 | Web · Native | 구성 API별 | 배포 |
234
+ | [미디어 재생 제어](compositions/media-transport.md) | 직접 조작과 모션 | 실제 오디오/영상 엔진의 재생·일시 정지·유한 길이 탐색을 같은 구조로 제공할 때 쓴다. | 미게시(1.20.0 이후) | Web · Native | 구성 API별 | 배포 |
235
+ | [베지어·단축키 편집 도구](compositions/authoring-tools.md) | 직접 조작과 모션 | 제품 안의 모션 편집기에서 두 제어점의 곡선을 조정하거나 웹 앱의 사용자 단축키를 실제로 등록할 때 쓴다. | 미게시(1.20.0 이후) | Web · Native | 구성 API별 | 배포 |
236
+ | [선택 반응 버튼](compositions/toggle-feedback.md) | 직접 조작과 모션 | 구독·북마크·좋아요에서 선택 요청과 실제 인원 수의 실패 복구, 국소 모션이 함께 필요할 때 쓴다. | 미게시(1.20.0 이후) | Web · Native | 구성 API별 | 배포 |
226
237
  | [선택 배경 이동](compositions/selection-motion.md) | 직접 조작과 모션 | 짧은 단일 선택의 현재 항목을 이어지는 배경으로 보여 줄 때 쓴다. | 1.14.0 | Web · Native | 구성 API별 | 배포 |
227
238
  | [숫자 변화와 메뉴 변형](compositions/optional-motion.md) | 직접 조작과 모션 | 선택 설치 모션(숫자 자리 단위 변화, 메뉴 형태 변환)을 기존 컴포넌트 자리에 끼워 넣을 때 쓴다. | 1.12.1 | Web | 구성 API별 | 배포 |
228
239
  | [이미지·시트·키보드 조작](compositions/optional-adapters.md) | 직접 조작과 모션 | Native 앱 한 화면에서 이미지 확대 보기, 끌어서 높이를 바꾸는 시트, OS 길게 누르기 메뉴, 키보드를 따라 올라가는 하단 행동을 함께 쓸 때 provider 중첩 순서와 각 요소의 자리를 확인하는 구성이다. | 1.12.1 | Native | 구성 API별 | 배포 |
@@ -1,12 +1,12 @@
1
1
  # ActivityChip
2
2
 
3
3
  - 단계: 컴포넌트
4
- - 상태: 실험
4
+ - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 미게시(1.17.2 이후)
7
7
  - 검토일: 2026-10-10
8
8
  - 근거: [ActivityChip 계약](../../activity-chip.md), `src/activity-chip.ts`(`activityChipRecipe`)
9
- - 스토리북: `실험/컴포넌트/상태와 알림/작업 상태 칩`, `실험/구성/피드백과 복구/백그라운드 작업 상태`
9
+ - 스토리북: `배포/컴포넌트/상태와 알림/작업 상태 칩`, `배포/구성/피드백과 복구/백그라운드 작업 상태`
10
10
 
11
11
  ## 언제 쓰나
12
12
 
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-10
7
+ - 검토일: 2026-10-13
8
8
  - 근거: [BottomNavigation](../../bottom-navigation.md), recipe `bottomNavigationRecipe`
9
9
  - 스토리북: `배포/컴포넌트/탐색/하단 탐색`
10
10
 
@@ -74,6 +74,8 @@ import { BottomNavigation } from "@hjmds/react-native/navigation";
74
74
  | `configuration.direction` | `ltr` · `rtl` | Provider 환경(없으면 `ltr`) | — |
75
75
  | `configuration.keyboardBehavior` | `hide` · `remain` | `hide` | 기본은 소프트 키보드가 열리면 막대를 숨긴다 |
76
76
  | `configuration.labels` | `visible` · `hidden` | `visible` | `hidden`은 아이콘만 남기고 목적지 이름을 화면에서 뺀다. 이름은 항목의 접근성 이름으로 그대로 읽힌다. 아이콘을 칸 가운데에 둔다. 선택 표시는 아이콘 굵기·확대(와 제품 선택 표시)만 남으므로 의도한 아이콘 전용 막대에만 쓴다 |
77
+ | `configuration.selection` | `emphasis` · `tile` | `emphasis` | `tile`은 선택 목적지 전체를 카드로 채운다. floating·equal·visible labels 전용 |
78
+ | `configuration.surface` | `flat` · `raised` | `flat` | `raised`는 `selection: "tile"` 전용 두께 있는 막대다. surfaceAlt 바탕·borderControl 옆면(4)·선택 카드 옆면(3) |
77
79
  | `primaryAction` | `ReactNode`(Button/IconButton) | — | `configuration` 밖의 별도 prop. 생성 같은 비목적지 행동이며 `center-gap` 또는 `capsule`과 짝짓는다 |
78
80
  | 항목 `badge` | `{ count, max?, accessibilityLabel }` | `max` 99 | 숫자만. 점 배지는 없다. `count`가 0이면 그리지 않는다 |
79
81
  | `onActivate` | `(activation: { key: Key; reason: "navigate" \| "reselect" }) => void` | Web 선택 · Native 필수 | 이동 의도만 알린다. 선택은 바꾸지 않는다. `reselect`는 이미 선택된 항목을 다시 누른 경우 |
@@ -152,3 +154,14 @@ Web·Native 모두 막대에는 surfaceAccent, 선택한 항목에는 primary/on
152
154
  연한 선택 카드가 도크와 구분되지 않는 사용자 피드백에 따라 선택한 카드 전체를 브랜드 색으로 채운다.
153
155
  안쪽 여백과 카드 사이 간격은 spacing.xxs, 카드 모서리는 radius.lg다.
154
156
  라우팅·누름 방지·키보드·접근성·배지는 기존 BottomNavigation 계약을 그대로 따른다.
157
+
158
+ ### 두께 있는 팻말 (2026-10-13)
159
+
160
+ Spint가 하단 탐색을 들판 게임의 나무 팻말처럼 보이게 요청해 `selection: "tile"`에 `surface: "raised"`를 추가했다.
161
+ 기본 `surface: "flat"`은 기존 선택 카드 외형을 유지한다. `raised`는 `selection: "tile"`에서만 허용된다.
162
+ 막대 바탕은 surfaceAlt(`surface.sunken`), 테두리는 borderControl이며 아래 테두리가 4로 두꺼워져 옆면이 된다.
163
+ floating 그림자는 그리지 않는다. 옆면이 높이를 표현하므로 그림자를 함께 두면 두 물체로 보인다.
164
+ 선택한 카드는 primary 위에 아래 테두리 3을 text 45% 반투명으로 겹쳐 더 진한 옆면을 만든다.
165
+ 팔레트에 "눌린 브랜드" 역할이 없어서 새 역할을 추가하지 않고 기존 역할을 겹쳐 쓴다.
166
+ 선택하지 않은 카드도 같은 두께의 투명 테두리를 둔다. 그래서 선택이 바뀌어도 아이콘·라벨 줄이 움직이지 않는다.
167
+ 제품은 나무 같은 색을 surfaceAlt·borderControl 팔레트 역할로 공급한다. deprecated 시각 style로 덮지 않는다.
@@ -125,3 +125,42 @@ import { Button } from "@hjmds/react-native/actions";
125
125
 
126
126
  2026-10-10 사용자 요청으로 선택 시 추가되는 테두리를 제거했다. `selected` 접근성 상태와
127
127
  contentBrand 글자·아이콘 색은 유지하며 키보드 focus outline은 선택과 별도로 유지한다.
128
+
129
+ ## 저장 확정 라벨과 프레임 예약 (미게시 · 2026-10-15)
130
+
131
+ `completion={{ confirmed, label }}`는 실제 mutation의 확정 결과를 표시한다. 두 라벨의
132
+ 공간을 처음부터 예약하므로 `children`을 저장 중/완료 문구로 교체하지 않는다.
133
+ `confirmed`는 현재 초안과 마지막 서버 확정 값이 같은지 제품이 판단해 전달한다.
134
+ `loading`이 true이면 확정 라벨보다 중앙 로딩을 우선한다. 타이머로 완료로 바꾸지 않는다.
135
+ 버튼은 별도 live region을 만들지 않으므로 결과 안내가 필요하면 기존 결과 영역 하나로 알린다.
136
+
137
+ ```tsx
138
+ // Web
139
+ <Button loading={feedback.pending} disabled={!feedback.canSubmit}
140
+ completion={{ confirmed: feedback.currentSaved, label: t("saved") }} onClick={save}>
141
+ {t("save")}
142
+ </Button>
143
+ ```
144
+
145
+ ```tsx
146
+ // Native
147
+ <Button loading={feedback.pending} disabled={!feedback.canSubmit}
148
+ completion={{ confirmed: feedback.currentSaved, label: t("saved") }} onPress={save}>
149
+ {t("save")}
150
+ </Button>
151
+ ```
152
+
153
+ 이 옵션의 children은 문자열·숫자 또는 그것들만의 배열이어야 한다. 아이콘은 기존
154
+ leading/trailing을 쓴다. 임의 JSX를 숨겨 복제하면 내부 상태·포커스가 중복되므로 허용하지
155
+ 않는다. 옵션 없는 Button의 기존 JSX children 계약은 유지한다.
156
+ Web은 두 숨김 라벨을 같은 grid 영역에 놓아 두 라벨의 최대 고유 폭·높이를 예약한다.
157
+ Native는 두 숨김 라벨을 같은 flow에 항상 두어 Yoga의 최대 고유 폭을 사용하고,
158
+ 현재 recipe 행 높이×줄 상한을 초기 높이로 예약한다. 각 라벨의 native layout 높이가
159
+ 더 크면 최댓값을 반영한다. 따라서 짧은 라벨만 있어도 Native slot은 기존 줄 상한만큼
160
+ 공간을 예약할 수 있다. 폭·글꼴·라벨이 바뀌면 두 라벨을 다시 측정한다.
161
+ 상태만 바뀌는 동안에는 같은 라벨 쌍과 아이콘을 유지해야 프레임이 고정된다.
162
+
163
+ Button의 loading은 표시와 UI 재실행을 막는 기능이다. 같은 프레임의 중복 실행·늦은 응답
164
+ 무효화는 기존 action-session 또는 제품 mutation이 맡는다. 저장 실패 뒤에는 입력과
165
+ 마지막 확정 값을 유지한다. [버튼 완료 피드백](../compositions/action-feedback.md)의
166
+ `resolveSaveFeedback` 조합을 사용한다.
@@ -73,18 +73,20 @@ import { Calendar } from "@hjmds/react-native/calendar";
73
73
  | `composeAccessibleName` | `(info: { date: string; isToday: boolean; isSelected: boolean; disabled: boolean; content?: Content }) => string` | 필수 | 날짜 칸의 접근성 이름 |
74
74
  | `renderCellContent` | `(cell: ResolvedCalendarDateCell<Content>) => ReactNode` | — | `cell`은 셀 descriptor에 `date`·`row`·`column`·`isToday`·`isSelected`·`selectable`·`accessibleName`이 더해진 값. 결과는 접근성에서 숨는다 |
75
75
  | Web `onNavigateBeyondGrid` | `(detail: { date: string; intent: "next-day" \| "previous-day" \| "next-week" \| "previous-week" \| "first-of-week" \| "last-of-week"; overflow: "before" \| "after" }, focusDate: (date: string) => void) => void` | — | 방향키가 격자 밖으로 나갈 때. 제품이 다음 달 grid를 넘긴 뒤 `focusDate`를 부른다 |
76
- | `size` | `medium` · `large` | `medium` | — |
76
+ | `size` | `compact` · `medium` · `large` | `medium` | — |
77
77
  | `ref` | `{ focusDate(date: string): void }` | — | 날짜에 초점을 요청한다 |
78
78
  | Web `autoFocus` | `boolean` | `false` | — |
79
79
  | `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native도 같은 배치 슬롯을 받는다 |
80
80
 
81
81
  ## 배치
82
82
 
83
+ 2026-10-14 야잘알 경기 달력에서 날짜 원이 승패 라벨보다 과하게 커 보인 사용자 피드백으로 `compact`를 추가했다. 날짜 표시는 28px 원으로 줄이고, 실제 터치 영역과 7열 최소 폭은 기존 44px 기준을 유지한다.
84
+
83
85
  Native `renderCellContent` 최소 높이는 Provider의 `tokens.typography.label.lineHeight × textScale`다. 날짜의 원형 geometry와 선택 계약은 별도다.
84
86
 
85
87
  | 항목 | 값 | 근거 |
86
88
  | --- | --- | --- |
87
- | 크기 | 날짜 칸 44(`medium` `control.minTouchTarget`, `large` `glyph.xxl` 44 — large는 글자만 `bodyLarge`로 커진다). 격자 최소 폭 7 × 44 = 308. 월 이동 버튼 44 원(`control.buttonHeight.medium`). 셀 `content`는 날짜 아래 label 한 줄 높이 | `calendarRecipe.sizes`·`header.navButton`, `.hjm-calendar__grid` |
89
+ | 크기 | 날짜 칸은 모든 size에서 최소 44, 선택·오늘 원은 `compact` 28 / 나머지 44(`medium` `control.minTouchTarget`, `large` `glyph.xxl` 44 — large는 글자만 `bodyLarge`로 커진다). 격자 최소 폭 7 × 44 = 308. 월 이동 버튼 44 원(`control.buttonHeight.medium`). 셀 `content`는 날짜 아래 label 한 줄 높이 | `calendarRecipe.sizes`·`header.navButton`, `.hjm-calendar__grid` |
88
90
  | 간격 | 머리↔격자, 머리 안 `spacing.xs` 8. 요일 줄 위아래 `spacing.xs` 8. 날짜↔`content` `spacing.xxs` 4. Web 격자 바깥 `spacing.xxs` 4 | `calendarRecipe.header.gap`, `.hjm-calendar*` |
89
91
  | 순서·정렬 | 위→아래 [이전 월][월 제목(가운데, title)][다음 월] → 요일 줄 → 주 단위 7열. 화면 본문에 인라인으로 두며 팝업·시트를 열지 않는다 | `.hjm-calendar__header`, `calendarRecipe` 주석(`shared`) |
90
92
  | 고정·스크롤 | 폭이 308보다 좁으면 격자가 가로 스크롤한다(Web `.hjm-calendar__viewport` `overflow-x: auto`, Native `ScrollView horizontal`) | `.hjm-calendar__viewport`, `react-native/src/calendar.tsx` |