@hjmds/design-contracts 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agreement.d.ts +183 -0
- package/dist/agreement.d.ts.map +1 -0
- package/dist/agreement.js +137 -0
- package/dist/agreement.js.map +1 -0
- package/dist/alert-dialog.d.ts +1 -1
- package/dist/alert-dialog.d.ts.map +1 -1
- package/dist/alert-dialog.js.map +1 -1
- package/dist/anchor.d.ts +62 -0
- package/dist/anchor.d.ts.map +1 -0
- package/dist/anchor.js +43 -0
- package/dist/anchor.js.map +1 -0
- package/dist/asset.d.ts +94 -0
- package/dist/asset.d.ts.map +1 -0
- package/dist/asset.js +60 -0
- package/dist/asset.js.map +1 -0
- package/dist/base-recipes.d.ts +19 -0
- package/dist/base-recipes.d.ts.map +1 -1
- package/dist/base-recipes.js +17 -0
- package/dist/base-recipes.js.map +1 -1
- package/dist/behaviors.d.ts +346 -3
- package/dist/behaviors.d.ts.map +1 -1
- package/dist/behaviors.js +35 -1
- package/dist/behaviors.js.map +1 -1
- package/dist/bottom-info.d.ts +58 -0
- package/dist/bottom-info.d.ts.map +1 -0
- package/dist/bottom-info.js +48 -0
- package/dist/bottom-info.js.map +1 -0
- package/dist/carousel.d.ts +3 -3
- package/dist/carousel.js +1 -1
- package/dist/carousel.js.map +1 -1
- package/dist/catalog.d.ts +1294 -239
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +65 -25
- package/dist/catalog.js.map +1 -1
- package/dist/collapsible.d.ts +56 -0
- package/dist/collapsible.d.ts.map +1 -0
- package/dist/collapsible.js +37 -0
- package/dist/collapsible.js.map +1 -0
- package/dist/component-definitions.d.ts +16 -0
- package/dist/component-definitions.d.ts.map +1 -1
- package/dist/component-definitions.js +16 -0
- package/dist/component-definitions.js.map +1 -1
- package/dist/component-recipes.d.ts +76 -4
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +38 -1
- package/dist/component-recipes.js.map +1 -1
- package/dist/context-menu.d.ts +53 -0
- package/dist/context-menu.d.ts.map +1 -0
- package/dist/context-menu.js +44 -0
- package/dist/context-menu.js.map +1 -0
- package/dist/counter-badge-recipe.d.ts +5 -0
- package/dist/counter-badge-recipe.d.ts.map +1 -1
- package/dist/counter-badge-recipe.js +5 -0
- package/dist/counter-badge-recipe.js.map +1 -1
- package/dist/dataviz.d.ts +76 -0
- package/dist/dataviz.d.ts.map +1 -0
- package/dist/dataviz.js +58 -0
- package/dist/dataviz.js.map +1 -0
- package/dist/date-range.d.ts +56 -0
- package/dist/date-range.d.ts.map +1 -0
- package/dist/date-range.js +79 -0
- package/dist/date-range.js.map +1 -0
- package/dist/design-system-provider.d.ts +24 -0
- package/dist/design-system-provider.d.ts.map +1 -1
- package/dist/design-system-provider.js +21 -0
- package/dist/design-system-provider.js.map +1 -1
- package/dist/floating-action-button.d.ts +1 -1
- package/dist/floating-action-button.d.ts.map +1 -1
- package/dist/floating-action-button.js +3 -1
- package/dist/floating-action-button.js.map +1 -1
- package/dist/formatters.d.ts +37 -0
- package/dist/formatters.d.ts.map +1 -0
- package/dist/formatters.js +69 -0
- package/dist/formatters.js.map +1 -0
- package/dist/heading.d.ts +82 -0
- package/dist/heading.d.ts.map +1 -0
- package/dist/heading.js +48 -0
- package/dist/heading.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/menubar.d.ts +133 -0
- package/dist/menubar.d.ts.map +1 -0
- package/dist/menubar.js +80 -0
- package/dist/menubar.js.map +1 -0
- package/dist/native-platform.d.ts +88 -0
- package/dist/native-platform.d.ts.map +1 -0
- package/dist/native-platform.js +70 -0
- package/dist/native-platform.js.map +1 -0
- package/dist/popover.d.ts +19 -4
- package/dist/popover.d.ts.map +1 -1
- package/dist/popover.js +3 -0
- package/dist/popover.js.map +1 -1
- package/dist/progress-recipe.d.ts +14 -0
- package/dist/progress-recipe.d.ts.map +1 -1
- package/dist/progress-recipe.js +14 -1
- package/dist/progress-recipe.js.map +1 -1
- package/dist/provider-button.d.ts +140 -0
- package/dist/provider-button.d.ts.map +1 -0
- package/dist/provider-button.js +83 -0
- package/dist/provider-button.js.map +1 -0
- package/dist/recipes.d.ts +15 -1
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +14 -0
- package/dist/recipes.js.map +1 -1
- package/dist/sheet.d.ts +16 -0
- package/dist/sheet.d.ts.map +1 -1
- package/dist/sheet.js +33 -0
- package/dist/sheet.js.map +1 -1
- package/dist/sidebar.d.ts +168 -0
- package/dist/sidebar.d.ts.map +1 -0
- package/dist/sidebar.js +98 -0
- package/dist/sidebar.js.map +1 -0
- package/dist/skip-nav.d.ts +67 -0
- package/dist/skip-nav.d.ts.map +1 -0
- package/dist/skip-nav.js +47 -0
- package/dist/skip-nav.js.map +1 -0
- package/dist/tags-input.d.ts +157 -0
- package/dist/tags-input.d.ts.map +1 -0
- package/dist/tags-input.js +108 -0
- package/dist/tags-input.js.map +1 -0
- package/dist/text-formats.d.ts +85 -0
- package/dist/text-formats.d.ts.map +1 -0
- package/dist/text-formats.js +43 -0
- package/dist/text-formats.js.map +1 -0
- package/dist/toggle-group.d.ts +111 -0
- package/dist/toggle-group.d.ts.map +1 -0
- package/dist/toggle-group.js +78 -0
- package/dist/toggle-group.js.map +1 -0
- package/dist/top.d.ts +113 -0
- package/dist/top.d.ts.map +1 -0
- package/dist/top.js +72 -0
- package/dist/top.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/agreement.md +33 -0
- package/docs/anchor.md +59 -59
- package/docs/ant-design-coverage.md +5 -3
- package/docs/architecture.md +10 -0
- package/docs/asset.md +26 -0
- package/docs/bottom-info.md +15 -0
- package/docs/breadcrumb.md +15 -15
- package/docs/button-label.md +20 -0
- package/docs/calendar.md +82 -154
- package/docs/carousel.md +27 -2
- package/docs/cascader.md +17 -0
- package/docs/chart.md +33 -0
- package/docs/clipboard.md +13 -0
- package/docs/collapsible.md +19 -0
- package/docs/command-palette.md +19 -0
- package/docs/confirm-popover.md +11 -1
- package/docs/context-menu.md +23 -0
- package/docs/data-table.md +22 -0
- package/docs/date-range.md +31 -0
- package/docs/density.md +23 -0
- package/docs/expansion-roadmap.md +16 -3
- package/docs/floating-action-button.md +35 -3
- package/docs/formatters.md +18 -0
- package/docs/generated/component-maturity.md +39 -23
- package/docs/generated/renderer-evidence.json +2921 -642
- package/docs/generated/renderer-evidence.md +53 -6
- package/docs/generated/showcase-manifest.json +1087 -64
- package/docs/heading.md +25 -0
- package/docs/identity.md +5 -0
- package/docs/layout.md +1 -1
- package/docs/library-gap-analysis.md +29 -9
- package/docs/list-row.md +14 -0
- package/docs/mentions.md +18 -0
- package/docs/menubar.md +22 -0
- package/docs/native-platform.md +36 -0
- package/docs/overlay-stack.md +24 -0
- package/docs/pagination.md +16 -8
- package/docs/popover.md +42 -13
- package/docs/product-audit-2026-09-15.md +88 -0
- package/docs/progress.md +16 -0
- package/docs/provider-button.md +33 -0
- package/docs/rating.md +19 -0
- package/docs/react-native-completion.md +347 -0
- package/docs/screen-chrome.md +52 -0
- package/docs/side-panel.md +23 -2
- package/docs/sidebar.md +30 -0
- package/docs/skip-nav.md +20 -0
- package/docs/splitter.md +22 -2
- package/docs/stable-promotion.md +51 -0
- package/docs/tags-input.md +25 -0
- package/docs/text-formats.md +15 -0
- package/docs/theming.md +84 -0
- package/docs/time-picker.md +22 -1
- package/docs/toast.md +18 -0
- package/docs/toggle-group.md +21 -0
- package/docs/top.md +32 -0
- package/docs/tour.md +23 -2
- package/docs/transfer-list.md +20 -0
- package/docs/tree-select.md +18 -0
- package/docs/tree.md +21 -0
- package/package.json +120 -6
package/docs/calendar.md
CHANGED
|
@@ -1,154 +1,82 @@
|
|
|
1
|
-
# Calendar
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
- 모양을 검증한다(7의 배수, 요일 라벨 7개, 날짜 형식, 중복 없음).
|
|
85
|
-
- 셀마다 `row`/`column`/`isToday`/`isSelected`/`selectable`을 유도한다(Steps가 `currentStepId`
|
|
86
|
-
하나에서 `pending/current/complete`를 유도하는 것과 같은 원칙 — 제품이 상태를 배열로 다시
|
|
87
|
-
넘기지 않는다).
|
|
88
|
-
- `date`가 없는 셀은 순수한 채움칸이다(이번 달 1일 전 요일들처럼). 채움칸은 절대 포커스,
|
|
89
|
-
선택, 접근성 이름을 갖지 않는다.
|
|
90
|
-
|
|
91
|
-
### 낭독은 제품이 조립한다
|
|
92
|
-
|
|
93
|
-
"8월 19일 수요일, 경기 2개, 선택됨" 같은 문장은 `composeAccessibleName`이 만든다 — Steps의
|
|
94
|
-
`composeAccessibleName`과 같은 이유(어순·조사는 언어마다 다르고, `content`의 의미는 제품만
|
|
95
|
-
안다). 빈 문자열을 반환하면 resolver가 던진다.
|
|
96
|
-
|
|
97
|
-
### 선택 불가능한 날짜는 포커스를 잃지 않는다
|
|
98
|
-
|
|
99
|
-
`getCollectionNavigationTarget`(Menu/Select/Combobox)은 disabled 항목을 건너뛴다. Calendar는
|
|
100
|
-
**건너뛰지 않는다** — WAI-ARIA Date Picker Dialog 패턴처럼, 화살표 아래는 항상 정확히 한 주
|
|
101
|
-
아래로 이동해야 예측 가능하다. 비활성 날짜에 포커스가 앉는 것과 그 날짜를 **활성화**할 수
|
|
102
|
-
있는 것은 별개다: `resolveCalendarGridDescriptor`가 내주는 `selectable: false`가 그 경계를
|
|
103
|
-
표시하고, activate(Enter/Space/tap) 자체를 막는 책임은 renderer에 있다.
|
|
104
|
-
|
|
105
|
-
### 격자 밖으로 나가면 넘긴다, 감싸지 않는다
|
|
106
|
-
|
|
107
|
-
한 페이지(보이는 달)의 경계를 넘는 화살표 이동은 순환하지 않고 `{ overflow: "before" |
|
|
108
|
-
"after" }`를 돌려준다 — HJM은 인접 달의 모양을 모르기 때문이다. 제품이 이 신호를 받아
|
|
109
|
-
달을 넘기고(`focusedMonth` 변경) 해당 날짜에 포커스를 옮긴다. **월 이동은 별도 통제
|
|
110
|
-
축(`focusedMonth`/`onFocusedMonthChange`)이며, 선택(`selectedDate`)과 완전히 독립이다** —
|
|
111
|
-
달을 넘겨도 선택은 지워지지 않는다.
|
|
112
|
-
|
|
113
|
-
## HJM 기본값
|
|
114
|
-
|
|
115
|
-
- `today`는 지속되는 테두리(`border.focus`)로, `selected`는 채워진 배경
|
|
116
|
-
(`action.brand.background`)으로 표시한다 — 겹쳐도(오늘이면서 선택된 날) 둘 다 읽힌다.
|
|
117
|
-
- 비활성 날짜와 이번 달 밖 날짜는 각각 `disabledOpacity`/`outsideFocusedMonthOpacity`로
|
|
118
|
-
구분한다(같은 회색조가 아니라 서로 다른 강도).
|
|
119
|
-
- 셀 지름은 `control.minTouchTarget`(medium) 이상을 보장한다.
|
|
120
|
-
|
|
121
|
-
## 플랫폼 번역
|
|
122
|
-
|
|
123
|
-
- Web: `grid`/`row`/`gridcell` role과 roving tabindex 화살표 이동을 쓴다(같은 결과: 원하는
|
|
124
|
-
날짜에 도달). 채움칸은 `aria-hidden`.
|
|
125
|
-
- Native: 복합 grid role의 동등물이 없다 — Steps/Breadcrumb가 이미 같은 결론을 냈다. 각
|
|
126
|
-
날짜는 독립적으로 포커스·탭 가능한 요소이고, 월 이동은 두 플랫폼 모두 있는 이전/다음
|
|
127
|
-
버튼으로만 이뤄진다(화살표 스와이프 같은 제스처 기반 이동은 이 계약에 없다).
|
|
128
|
-
- 두 플랫폼 모두 "오늘·선택·비활성"을 같은 세 가지 비-색 신호(테두리/배경/투명도)로
|
|
129
|
-
전달한다. 픽셀 parity가 아니라 같은 action/result/announcement라는 `beta →
|
|
130
|
-
stable(adaptive)` gate의 기준을 그대로 따랐다 — Calendar 자체는 `adaptive`가 아니라
|
|
131
|
-
`shared`로 분류하지만(오버레이 선택이 없으므로), 접근성 경로가 플랫폼마다 다르다는 점은
|
|
132
|
-
같다.
|
|
133
|
-
|
|
134
|
-
## 공개한 축 / 배제한 축
|
|
135
|
-
|
|
136
|
-
| 축 | 상태 |
|
|
137
|
-
| --- | --- |
|
|
138
|
-
| `today` / `selected` / `disabled` / `outsideFocusedMonth` | 공개 |
|
|
139
|
-
| 월 이동(`focusedMonth`, 선택과 독립) | 공개 |
|
|
140
|
-
| 방향키 격자 이동(일/주 단위, Home/End) | 공개 — Web roving tabindex |
|
|
141
|
-
| range 선택(시작~끝 날짜 구간) | **배제** — 측정된 요구가 없다. Yajalal FA 조회, 경기 일정
|
|
142
|
-
어디에도 기간 선택 UI가 없다. 필요해지면 `CalendarSelection`을 확장하지 않고 별도
|
|
143
|
-
`CalendarRangeSelection` 타입을 새로 여는 쪽을 권한다 — 단일 선택 소비자의 타입을
|
|
144
|
-
좁히지 않기 위해서다. |
|
|
145
|
-
| 요일 시작(일요일/월요일) 로직 | **배제** — 제품이 `weekdayLabels`와 `cells` 순서로 이미
|
|
146
|
-
결정해서 넘긴다. HJM이 로케일별 첫 요일을 판단하지 않는다. |
|
|
147
|
-
| PageUp/PageDown 월·년 이동 단축키 | **배제** — 화살표 overflow 신호 + 명시적 이전/다음
|
|
148
|
-
버튼이 이미 월 이동을 커버한다. 측정된 단축키 요구가 나오면 추가한다. |
|
|
149
|
-
| 다중 월 동시 표시(antd `Calendar`의 연간 뷰 등) | **배제** — 한 번에 한 달만 다룬다. |
|
|
150
|
-
|
|
151
|
-
## 검증 화면
|
|
152
|
-
|
|
153
|
-
아직 없다. 위 「Yajalal 실사용처 재확인」에서 밝혔듯 현재 살아있는 vertical slice 후보가
|
|
154
|
-
없다 — `planned → beta` 승격은 실제 제품 화면이 나온 뒤 리드가 진행한다.
|
|
1
|
+
# Calendar
|
|
2
|
+
|
|
3
|
+
2026-09-16 · React / React Native beta. 날짜별 기록을 탐색하는 작동 예제를 제공한다.
|
|
4
|
+
제품의 실제 채택·기기 검증·npm 게시를 뜻하지 않는다.
|
|
5
|
+
|
|
6
|
+
## 설계와 참조
|
|
7
|
+
|
|
8
|
+
- [React Aria Calendar](https://react-aria.adobe.com/Calendar)의 선택과 현재 표시 월을
|
|
9
|
+
별도로 통제하는 구성을 비교했다. HJM은 제품이 계산한 ISO 날짜 배열을 받는다.
|
|
10
|
+
달력 체계·로케일·시간대 계산을 renderer에 추가하는 대안은 기존 문자열 계약을 깨므로 채택하지 않았다.
|
|
11
|
+
- [WAI 날짜 선택 예제](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/datepicker-dialog/)의
|
|
12
|
+
격자·roving focus·요일 탐색을 참고했다. HJM은 inline Calendar와 DatePicker의 overlay를 구분한다.
|
|
13
|
+
- 토스 UI의 읽기 쉬운 제목, 평평한 콘텐츠, 하나의 명확한 선택 강조를 HJM 토큰으로 표현한다.
|
|
14
|
+
외부 라이브러리의 코드·자산·폰트를 복사하지 않는다.
|
|
15
|
+
|
|
16
|
+
과거 Yajalal 조사에서는 월 격자가 날짜 레일로 바뀌어 살아 있는 Calendar 소비 화면이 없었다.
|
|
17
|
+
이번 구현의 근거는 React/RN 컴포넌트를 채우라는 명시 요청이며, 검증 화면은 Showcase의
|
|
18
|
+
`Patterns/Calendar` 기록 탐색이다. 이를 Yajalal 채택으로 기록하지 않는다.
|
|
19
|
+
|
|
20
|
+
## 경계
|
|
21
|
+
|
|
22
|
+
Calendar는 항상 표시되는 한 달의 격자다. DatePicker는 날짜 필드와 Web dialog / Native Sheet에
|
|
23
|
+
같은 Calendar를 담는다. 날짜 선택·접근성·큰 글자 수정이 양쪽에 동시에 반영되도록 렌더링을
|
|
24
|
+
공유한다. DatePicker의 선택 후 닫기·trigger copy·clear·읽기 전용은 바깥 필드가 맡는다.
|
|
25
|
+
|
|
26
|
+
- `grid.cells`: 제품이 만든 7열 row-major 배열. 길이는 양의 7의 배수다.
|
|
27
|
+
- `date`: ISO `YYYY-MM-DD` stable key. 빈 셀은 `{}`이며 focus나 접근성 이름을 갖지 않는다.
|
|
28
|
+
- `weekdayLabels`, `todayDate`, `monthLabel`: 제품이 정한 로케일·시계의 결과를 받는다.
|
|
29
|
+
- `selectedDate` / `defaultSelectedDate` / `onSelectionChange`: 단일 선택. 월 이동과 독립이다.
|
|
30
|
+
- `focusedMonth` / `defaultFocusedMonth` / `onFocusedMonthChange`: 제품의 표시 월 요청 계약.
|
|
31
|
+
실제 `grid`와 `monthLabel` 갱신은 제품이 수행한다. handler가 없으면 월 버튼은 비활성이다.
|
|
32
|
+
- `composeAccessibleName`: 날짜·오늘·비활성·제품 콘텐츠를 현지화한다. 선택 상태는 별도
|
|
33
|
+
`aria-selected` / native accessibilityState로도 제공한다.
|
|
34
|
+
- `renderCellContent`: 점·짧은 개수 등 장식 콘텐츠. 독립된 버튼/입력을 넣지 않는다.
|
|
35
|
+
내용 의미는 `composeAccessibleName`에 포함한다. 중복 낭독 방지를 위해 slot은 접근성에서 숨긴다.
|
|
36
|
+
|
|
37
|
+
## 공개 API
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { Calendar } from "@hjmds/react/calendar";
|
|
41
|
+
// Native: @hjmds/react-native/calendar
|
|
42
|
+
|
|
43
|
+
<Calendar
|
|
44
|
+
descriptor={{ grid, monthLabel, focusedMonth: month, onFocusedMonthChange: setMonth,
|
|
45
|
+
selectedDate: date, onSelectionChange: setDate }}
|
|
46
|
+
previousMonth={{ month: previousMonthKey, label: "이전 달" }}
|
|
47
|
+
nextMonth={{ month: nextMonthKey, label: "다음 달" }}
|
|
48
|
+
composeAccessibleName={formatCellName}
|
|
49
|
+
/>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
두 renderer는 `size="medium" | "large"`, `renderCellContent`,
|
|
53
|
+
`ref.current.focusDate(date)`를 지원한다. focus 요청 날짜가 다음 렌더에서 도착하면 그때 적용한다.
|
|
54
|
+
Web의 `autoFocus`는 기본 false이며 DatePicker가 열린 격자에만 true로 설정한다.
|
|
55
|
+
|
|
56
|
+
### Web 경계 이동
|
|
57
|
+
|
|
58
|
+
방향키는 좌우 하루/상하 한 주, RTL 좌우 반전, Home/End는 현재 행의 첫/마지막 날짜다.
|
|
59
|
+
비활성 날짜에도 초점은 이동하지만 클릭·Enter/Space로 선택할 수 없다. 월 페이지 밖으로 이동하면
|
|
60
|
+
`onNavigateBeyondGrid({ date, intent, overflow }, focusDate)`를 호출한다.
|
|
61
|
+
|
|
62
|
+
날짜 계산을 소유하는 제품이 다음 날짜/월을 계산하고 `setMonth(nextMonth); focusDate(nextDate)`를
|
|
63
|
+
호출한다. 이 handler가 없으면 격자 경계에 머무른다. 연속 키보드 월 탐색이 필요한 제품은 반드시
|
|
64
|
+
연결한다. DatePicker도 같은 callback을 받는다. Native는 명시적 이전/다음 버튼으로 월을 이동한다.
|
|
65
|
+
|
|
66
|
+
## 시각·접근성
|
|
67
|
+
|
|
68
|
+
- 오늘은 border, 선택은 fill, 비활성은 opacity와 접근성 상태로 구분한다.
|
|
69
|
+
- 7열의 최소 touch target을 유지한다. 폭이 부족하면 격자만 가로 스크롤하고 본문은 넘치지 않는다.
|
|
70
|
+
- 셀 높이를 고정하지 않아 2배 글자나 짧은 제품 콘텐츠가 잘리지 않는다.
|
|
71
|
+
- Web: grid / row / columnheader / gridcell, 하나의 날짜 tab stop, visible focus.
|
|
72
|
+
- Native: 각 날짜는 독립 접근 가능한 button. 실제 플랫폼과 RN Web의 focus 경로를 구분한다.
|
|
73
|
+
- 단일 날짜·한 페이지 계약이다. range, 연간 view, 비그레고리력 계산 엔진은 이 컴포넌트에 없다.
|
|
74
|
+
제품이 준비한 날짜 배열의 의미를 보존하며 HJM은 날짜 형식·중복·격자 모양을 검증한다.
|
|
75
|
+
|
|
76
|
+
## 검증
|
|
77
|
+
|
|
78
|
+
Web 테스트는 비활성 날짜 focus/활성화 분리, 하루/주/Home/End, 경계에서 새 페이지의 지정 날짜로
|
|
79
|
+
focus 이동, controlled 선택 보존, 320px·2배 글자·RTL 최소 target과 본문 overflow를 검사한다.
|
|
80
|
+
Native 테스트는 개별 날짜의 접근성·비활성·선택, 명시적 월 이동, 크기와 스크롤 경계를 검사한다.
|
|
81
|
+
DatePicker의 기존 선택·닫기·방향키 회귀와 controlled-open 읽기 전용/disabled 활성화 방지도 검사한다.
|
|
82
|
+
전체 패키지·Showcase 검증 및 브라우저 확인은 `react-native-completion.md` 실행 기록에 남긴다.
|
package/docs/carousel.md
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
사용자가 한 번에 하나만 보이는 카드 묶음을 순서대로 넘겨 본다 — 야잘알 홈의 "내 구단
|
|
10
10
|
경기 스트립"(가로 스크롤 페이저)이 실사용처다. Ant Design `Carousel`과 같은 사용자
|
|
11
|
-
문제를 풀지만, HJM은 `direct` crosswalk를 따르면서도 antd의 기본
|
|
12
|
-
|
|
11
|
+
문제를 풀지만, HJM은 `direct` crosswalk를 따르면서도 antd의 기본 무한
|
|
12
|
+
순환을 제공하지 않는다. 자동재생은 두 라이브러리 모두 기본 false다. 2026-09-16
|
|
13
|
+
[공식 API](https://ant.design/components/carousel/) 대조로 기존 문서의 기본값 오류를 수정했다.
|
|
13
14
|
|
|
14
15
|
## 일반화한 계약
|
|
15
16
|
|
|
@@ -128,3 +129,27 @@ mount policy 참고) — 새로 만든 개념이 아니라 이미 검증된 패
|
|
|
128
129
|
|
|
129
130
|
아직 없음. `planned → beta` 승격은 실제 제품 vertical slice 이후 리드가 진행한다(로드맵
|
|
130
131
|
maturity gate). 유력 후보: 야잘알 홈의 내 구단 경기 스트립.
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
## React / React Native renderer (2026-09-16)
|
|
135
|
+
|
|
136
|
+
두 패키지의 `./carousel` 경로에서 `Carousel`을 제공한다. 공통 props는 label, slides,
|
|
137
|
+
renderSlide, composeAccessibleName, labels(previous/next/pause/resume/navigation),
|
|
138
|
+
currentKey 또는 defaultCurrentKey, onCurrentKeyChange와 선택적 autoplay다.
|
|
139
|
+
Web은 ref/HTML attributes, Native는 View style을 받는다. 빈 목록이나 존재하지 않는
|
|
140
|
+
currentKey는 계약 오류다. 동적으로 현재 항목을 제거하는 제품은 새 key도 함께 정한다.
|
|
141
|
+
|
|
142
|
+
[WAI APG](https://www.w3.org/WAI/ARIA/apg/patterns/carousel/)에 따라 Web 재생 제어를
|
|
143
|
+
카드보다 먼저 배치하고 focus 진입 후 자동 재개하지 않는다. hover/숨겨진 페이지에서는
|
|
144
|
+
일시 정지하고 마지막 카드에서 종료한다. 명시적 재시작은 첫 카드부터 시작한다.
|
|
145
|
+
이전/다음/각 위치 버튼은 Tab 순서에 남고, 카드 내부 입력의 화살표 키는 가로채지 않는다.
|
|
146
|
+
|
|
147
|
+
Native는 현재 위치의 별도 adjustable 제어를 둬 카드 안의 버튼을 접근성 그룹에
|
|
148
|
+
흡수하지 않는다. 수평 의도가 분명한 swipe만 한 장 이동으로 처리하며 수직 scroll은
|
|
149
|
+
제품에 남긴다. VoiceOver/TalkBack이 켜지거나 OS 확인 전에는 자동재생하지 않는다.
|
|
150
|
+
앱이 background인 동안 멈추고, 터치·수동 이동은 명시적 재생 전까지 멈춘다.
|
|
151
|
+
Native 위치 제어는 숫자 버튼으로 표현해 현재 위치를 눈으로도 읽게 한다.
|
|
152
|
+
|
|
153
|
+
자동재생은 선택 기능이며 기본 화면 예제는 수동이다. Web browser 및 Native renderer
|
|
154
|
+
테스트는 숨김·key 유지·경계·controlled 요청·RTL·자동재생 중지와 swipe/접근성 action을
|
|
155
|
+
검증한다. Native 기기 제스처·스크린 리더 실측은 별도이며 이 증거로 stable 승격하지 않는다.
|
package/docs/cascader.md
CHANGED
|
@@ -91,3 +91,20 @@ alias 판정과 같은 자리가 된다.
|
|
|
91
91
|
폭이 넓은 실제 계층 데이터가 나와, "렌더러 선택"이라던 전제가 깨진다.
|
|
92
92
|
3. `changeOnSelect`형 중간 노드 커밋이 `commitAt` 하나로 표현하기엔 부족한 추가 규칙
|
|
93
93
|
(예: 중간 노드 커밋 시 하위 요약값 표시)이 실제 화면에서 요구된다.
|
|
94
|
+
|
|
95
|
+
## 2026-09-18 후속: 축을 추가하지 않고 조합으로 닫았다
|
|
96
|
+
|
|
97
|
+
이 판정은 `valueMode`/`commitAt` 두 축을 **TreeSelect 컴포넌트**에 추가하는 것을 전제로
|
|
98
|
+
썼다. 그 사이 TreeSelect 자체가 별도 컴포넌트가 아니라 조합(Popover 표면 + Tree
|
|
99
|
+
collection + tri-state 판정 모듈)으로 확정됐고, Tree renderer가 들어오면서 두 축이
|
|
100
|
+
가리키던 동작이 이미 존재한다.
|
|
101
|
+
|
|
102
|
+
- `valueMode: "path"` — Tree의 resolve 결과가 `parentId` 사슬을 들고 있으므로 경로는
|
|
103
|
+
파생이다. 저장 규칙은 여전히 제품 소유이고 계약이 값 모양을 새로 규정하지 않는다.
|
|
104
|
+
- `commitAt: "any"` — 중간 노드를 그냥 고르면 된다. Tree의 단일 선택이 리프를 강제하지
|
|
105
|
+
않으므로 막아 둔 자리를 여는 새 축이 필요 없다.
|
|
106
|
+
|
|
107
|
+
그래서 catalog의 Cascader 행은 `prerequisite`에서 `composed`(Popover·Tree)로 옮겼고,
|
|
108
|
+
작동 예제는 Showcase `Patterns/Tree`의 Cascader 화면이다. "만들지 않는다"는 판정 자체는
|
|
109
|
+
그대로다 — 바뀐 것은 흡수하는 쪽이 컴포넌트가 아니라 조합이라는 점뿐이다.
|
|
110
|
+
|
package/docs/chart.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Chart — 렌더러를 만들지 않는다
|
|
2
|
+
|
|
3
|
+
**결정.** 막대·선·원 차트 렌더러를 HJM에 두지 않는다. 대신 **색·축·격자·범례 토큰**만
|
|
4
|
+
`dataviz` 계약으로 고정하고, 그리는 일은 제품이 고른 라이브러리(Recharts·victory-native·
|
|
5
|
+
d3 등)에 맡긴다.
|
|
6
|
+
|
|
7
|
+
**왜.** 차트를 하나 그리기 시작하면 축 눈금, 툴팁, 애니메이션, 반응형 재계산, 키보드
|
|
8
|
+
탐색, 데이터 테이블 대체 표현까지 전부 따라온다. 그 전부를 이 패키지가 두 표면에서
|
|
9
|
+
유지하는 비용이 제품이 라이브러리 하나를 붙이는 비용보다 크다. 게다가 반쯤 만든 차트는
|
|
10
|
+
제품이 **우리 차트와 자기 차트 두 벌**을 갖게 만든다.
|
|
11
|
+
|
|
12
|
+
**실제로 어긋난 것은 색이었다.** Spint 열지도와 Yajalal 통계가 각자 색을 골랐고, 같은
|
|
13
|
+
포트폴리오의 두 화면이 다른 파랑을 썼다. 그건 렌더러가 없어서가 아니라 **팔레트가 없어서**
|
|
14
|
+
생긴 문제다. 그래서 그 자리만 계약으로 막는다.
|
|
15
|
+
|
|
16
|
+
## 계약이 정하는 것
|
|
17
|
+
|
|
18
|
+
- `datavizSeriesPalette` — light/dark 각 8색의 계열 팔레트. 상태색(info/success/warning/
|
|
19
|
+
attention)은 **쓰지 않는다**: 뜻을 가진 색이라 "3번 계열"에 쓰면 사용자가 경고로 읽는다.
|
|
20
|
+
- `resolveDatavizSeriesColor(theme, index)` — 8을 넘으면 **순환**한다. 색을 자동 생성하지
|
|
21
|
+
않는다. 생성색은 대비를 보장할 수 없고, 9번째 계열이 필요한 화면은 대개 표가 맞다.
|
|
22
|
+
- `datavizChromeTokens` — 축선·축라벨·격자·범례·툴팁은 본문 토큰(`border`·`textMuted`·
|
|
23
|
+
`textBody`·`surface`)을 그대로 쓴다. 차트 전용 회색을 따로 두면 같은 화면의 표·캡션과
|
|
24
|
+
어긋난다.
|
|
25
|
+
- `datavizRedundantEncodings` / `validateDatavizEncoding` — 색만으로 계열을 구분하지
|
|
26
|
+
않는다(WCAG 1.4.1). 직접 라벨·패턴·마커 모양 중 **하나 이상**을 색과 함께 쓴다. 색각
|
|
27
|
+
이상과 흑백 인쇄에서 차트가 남는 조건이다.
|
|
28
|
+
|
|
29
|
+
## 계약이 정하지 않는 것
|
|
30
|
+
|
|
31
|
+
축 스케일, 보간, 툴팁 동작, 데이터 정렬, 빈 상태 문구. 전부 제품과 라이브러리의 몫이다.
|
|
32
|
+
이 결정을 뒤집으려면 "제품 두 곳 이상이 같은 차트 종류를 필요로 하고, 라이브러리 위임으로
|
|
33
|
+
해결되지 않는 접근성 문제가 실측으로 확인됐다"가 먼저 있어야 한다.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# ClipboardButton
|
|
2
|
+
|
|
3
|
+
복사는 제품이 반복해서 다시 만들던 세 가지다: 비동기 clipboard 호출, 잠깐 보이는 "복사됨"
|
|
4
|
+
상태, 그리고 **그 상태를 읽어 주는 것**.
|
|
5
|
+
|
|
6
|
+
마지막 하나가 늘 빠졌다 — 라벨이 눈에만 바뀌면 보조기기 사용자는 아무 일도 일어나지
|
|
7
|
+
않은 것과 같다. 그래서 `role="status"`로 함께 알린다.
|
|
8
|
+
|
|
9
|
+
- **거부된 클립보드를 성공으로 속이지 않는다.** 권한이 없으면 `onCopyError`로 알리고
|
|
10
|
+
라벨은 그대로 둔다.
|
|
11
|
+
- **값이 바뀌면 "복사됨"을 지운다.** 그 표시는 이전 문자열을 가리키던 것이라, 남겨 두면
|
|
12
|
+
사실이 아닌 말을 화면에 두는 셈이다.
|
|
13
|
+
- 두 상태의 문구는 제품이 준다.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Collapsible contract
|
|
2
|
+
|
|
3
|
+
**문제.** 접었다 펴는 한 덩어리. "더 보기", 필터 패널, 접히는 본문처럼 **이웃이 없는
|
|
4
|
+
disclosure**다.
|
|
5
|
+
|
|
6
|
+
**Accordion과 다른 것은 개수가 아니라 관계다.** Accordion의 항목들은 서로를 안다 —
|
|
7
|
+
하나를 열면 다른 것이 닫히는 정책, 항목 사이 구분선, 그룹 전체의 키보드 이동이 있다.
|
|
8
|
+
Collapsible에는 그 셋이 전부 없다. `items.length === 1`인 Accordion으로 대신하면 그룹
|
|
9
|
+
chrome이 따라오고 그걸 다시 CSS로 지우게 된다. 그래서 별도로 둔다 — 상태 어휘와 모션은
|
|
10
|
+
공유한다.
|
|
11
|
+
|
|
12
|
+
**닫힌 내용은 트리에서 뺀다.** 시각적으로만 숨기면 화면 리더와 브라우저 찾기가 그대로
|
|
13
|
+
도달한다. "접혀 있다"는 표시와 실제가 어긋나므로 renderer는 unmount한다. 애니메이션을
|
|
14
|
+
위해 남겨 두고 싶다면 그건 제품이 아니라 이 계약을 바꿀 일이다.
|
|
15
|
+
|
|
16
|
+
**trigger는 `aria-expanded`와 `aria-controls`를 함께 낸다.** 상태만 알리고 무엇이 열렸는지
|
|
17
|
+
가리키지 않으면 사용자는 방금 나타난 것을 찾아야 한다.
|
|
18
|
+
|
|
19
|
+
**Web·Native 공통.** 네이티브도 `button` role에 expanded 상태를 그대로 쓴다.
|
package/docs/command-palette.md
CHANGED
|
@@ -114,3 +114,22 @@ heading을 가지므로) — CommandPalette는 다르다: `role="dialog"` 표면
|
|
|
114
114
|
## 검증 화면
|
|
115
115
|
|
|
116
116
|
아직 없음. `planned → beta` 승격은 실제 제품 vertical slice 이후 리드가 진행한다.
|
|
117
|
+
|
|
118
|
+
## Web renderer (2026-09-18)
|
|
119
|
+
|
|
120
|
+
`@hjmds/react/command-palette`의 `CommandPalette`가 이 계약을 실행한다. catalog는
|
|
121
|
+
Web `beta`, Native `unsupported`다.
|
|
122
|
+
|
|
123
|
+
- **모달 takeover다.** Dialog·Sheet·SidePanel과 같은 모달 스택·스크롤 락·배경 격리를
|
|
124
|
+
공유한다(`packages/react/src/modal.tsx`). 별도 `modal` 축은 없다.
|
|
125
|
+
- **실행은 언제나 닫는다.** `activation`은 정책이 거부할 수 없는 dismiss 사유이고,
|
|
126
|
+
`onActivateAfterDismiss`는 팔레트가 사라진 뒤에 실행된다 — 다음 표면을 여는 명령이
|
|
127
|
+
겹쳐 뜨지 않도록.
|
|
128
|
+
- **결과 목록은 Combobox 어휘 그대로다.** 검색 input이 `role="combobox"`,
|
|
129
|
+
결과가 `listbox`/`option`, 활성 행은 `aria-activedescendant`로 가리킨다.
|
|
130
|
+
방향키는 계약의 `getCollectionNavigationTarget`(disabled 건너뜀)을 쓴다.
|
|
131
|
+
- **query가 바뀌면 활성 행이 첫 결과로 되돌아간다.** Enter의 대상이 언제나 분명해야 한다.
|
|
132
|
+
- **전역 단축키는 제품 소유다.** 이 renderer는 여는 키를 정하지 않는다.
|
|
133
|
+
- 로컬 검증: `test/command-palette.browser.test.tsx` 5개(이름·초점·배경 inert, 활성 행과
|
|
134
|
+
disabled 건너뜀·재필터, 실행 시 강제 종료와 사유, 종료 후 후속 명령 순서, Escape·바깥
|
|
135
|
+
pointer 종료)와 `Patterns/CommandPalette`.
|
package/docs/confirm-popover.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ConfirmPopover —
|
|
1
|
+
# ConfirmPopover — Popover 위의 확인 조합
|
|
2
2
|
|
|
3
3
|
## 문제로 제기된 것
|
|
4
4
|
|
|
@@ -91,3 +91,13 @@ Component Explorer에서 이 문서로 연결한다.
|
|
|
91
91
|
반복되어, 그 조합 자체(콜백 규약, 포커스 초기값)를 매번 다시 구현하는 비용이
|
|
92
92
|
측정 가능하게 커진다 — 그때는 새 상태 축이 아니라 **Popover 위의 얇은
|
|
93
93
|
합성 헬퍼**(recipe 없이 콜백 배선만 감싸는 유틸리티) 형태를 먼저 검토한다.
|
|
94
|
+
|
|
95
|
+
## 2026-09-16 작동 예제
|
|
96
|
+
|
|
97
|
+
React/RN 확장 요청에 따라 `Patterns/Popover/ReversibleConfirmation`을 제공한다.
|
|
98
|
+
`@hjmds/react/popover`의 children 함수에서 `close()`와 HJM Button을 조합한다.
|
|
99
|
+
취소 버튼을 초기 초점으로 지정하고, 보관 후 사라지는 트리거 대신 보관 취소 버튼에
|
|
100
|
+
초점을 보낸다. 보관 취소는 실제 로컬 상태를 복원한다. 외부 서버 요청이나 배포 증거는 아니다.
|
|
101
|
+
|
|
102
|
+
별도 `ConfirmPopover` renderer/export는 추가하지 않는다. catalog의 composed 항목과
|
|
103
|
+
Popover/AlertDialog 선택 경계는 유지하며, 이 예제를 완성된 조합으로 추적한다.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# ContextMenu contract
|
|
2
|
+
|
|
3
|
+
**문제.** 포인터 위치에서 열리는 메뉴 — 우클릭, 그리고 터치의 길게 누르기.
|
|
4
|
+
|
|
5
|
+
**Menu와 목록은 같고 앵커가 다르다.** Menu는 트리거 버튼에 붙고 ContextMenu는 이벤트가
|
|
6
|
+
일어난 **좌표**에 붙는다. 항목 타입은 `MenuItemDescriptor` 그대로다. 두 번째 항목 어휘를
|
|
7
|
+
만들면 tone·shortcut·disabled 규칙이 두 곳에서 갈라진다.
|
|
8
|
+
|
|
9
|
+
**트리거가 없다는 점이 접근성까지 번진다.** 버튼이 없으므로 키보드 사용자에게 여는 방법을
|
|
10
|
+
따로 줘야 한다(Shift+F10, 또는 메뉴 키). 그것을 빼면 마우스 없이는 존재하지 않는 기능이
|
|
11
|
+
된다 — 이 계약이 여는 이유(`openReason`)를 값으로 갖는 이유다.
|
|
12
|
+
|
|
13
|
+
**키보드로 열 때의 앵커는 초점 요소의 상자다.** 좌표가 없다고 (0,0)에 띄우면 메뉴가 방금
|
|
14
|
+
초점이 있던 항목과 무관해 보인다. `resolveContextMenuAnchor`가 이 판단을 갖고 있고,
|
|
15
|
+
좌표도 초점 상자도 없으면 렌더링 대신 던진다.
|
|
16
|
+
|
|
17
|
+
**닫으면 열었던 자리로 초점을 돌린다.** 좌표에서 열렸다고 초점까지 떠 있으면 다음 Tab이
|
|
18
|
+
문서 처음으로 간다.
|
|
19
|
+
|
|
20
|
+
**브라우저 기본 메뉴를 제품이 소유한 표면에서만 대체한다.** 텍스트 선택·링크·이미지 위의
|
|
21
|
+
기본 메뉴를 빼앗으면 복사·새 탭 열기를 사용자에게서 뺏는 것이다.
|
|
22
|
+
|
|
23
|
+
**Web 전용.** 네이티브의 길게 누르기 메뉴는 OS가 제공하는 표면이라 같은 계약이 아니다.
|
package/docs/data-table.md
CHANGED
|
@@ -77,3 +77,25 @@
|
|
|
77
77
|
|
|
78
78
|
**검증 화면.** 아직 실제 제품 vertical slice가 없다 — catalog는 `planned`으로
|
|
79
79
|
남고, `beta` 승격은 로드맵 gate(실제 화면 검증)를 통과한 뒤 리드가 진행한다.
|
|
80
|
+
|
|
81
|
+
## Web renderer (2026-09-18)
|
|
82
|
+
|
|
83
|
+
`@hjmds/react/data-table`의 `DataTable`이 이 계약을 실행한다. catalog는 Web `beta`,
|
|
84
|
+
Native `unsupported`다.
|
|
85
|
+
|
|
86
|
+
- **기존 `Table`과 겹치지 않는다.** `@hjmds/react`의 `Table`(advanced-display)은 열·행을
|
|
87
|
+
그려 주는 표시용이고 선택·tri-state·async 상태·정렬 상태 순환이 없다. DataTable은 그
|
|
88
|
+
상호작용 계약을 실행하는 쪽이다. 단순 표는 계속 `Table`을 쓴다 — 같은 화면에 둘을 겹쳐
|
|
89
|
+
쓰지 않는다.
|
|
90
|
+
- **정렬 버튼은 header 안에 있다.** `th` 자체를 버튼으로 만들지 않고, 방향은
|
|
91
|
+
`aria-sort`에 그대로 흘린다. 다음 상태는 `getNextDataTableSortState`가 정하고 실제 정렬은
|
|
92
|
+
제품이 한다.
|
|
93
|
+
- **선택은 공용 collection 모델 그대로다.** multiple은 checkbox, single은 radio 의미를
|
|
94
|
+
쓰고, 머리글의 tri-state는 `resolveDataTableSelectAllState`가 파생한다. disabled 행은
|
|
95
|
+
분모·분자 양쪽에서 빠진다.
|
|
96
|
+
- **셀 하나에 focusable 컨트롤은 최대 하나다.** roving tabindex grid 탐색을 도입하지 않고
|
|
97
|
+
기본 tab 순서를 쓴다.
|
|
98
|
+
- **페이지네이션은 표 아래에 조합한다.** `footer` slot은 제품이 채우고 표가 소유하지 않는다.
|
|
99
|
+
- 로컬 검증: `test/data-table.browser.test.tsx` 5개(header 안 정렬 버튼과 aria-sort 3단계,
|
|
100
|
+
tri-state 선택과 disabled 제외, 단일 선택 radio 의미, 셀당 컨트롤 하나와 기본 tab 순서,
|
|
101
|
+
async 상태 발표와 footer 조합)와 `Patterns/CommandPalette`의 표 화면.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# DateRange contract
|
|
2
|
+
|
|
3
|
+
**문제.** 날짜 **구간** 선택 — 통계 기간, 예약, 검색 필터.
|
|
4
|
+
|
|
5
|
+
**Calendar에 `mode` 축을 넣지 않은 이유.** 단일 선택의 값은 `string | null`이고 구간의
|
|
6
|
+
값은 `{ start, end }`다. 한 컴포넌트가 두 값 모양을 가지면 모든 소비자가 매번 좁히기를
|
|
7
|
+
해야 하고, "range 모드인데 `selectedDate`를 준" 조합이 타입에 남는다. 격자는 그대로
|
|
8
|
+
재사용하고 **구간 판정만** 이 모듈이 갖는다.
|
|
9
|
+
|
|
10
|
+
**다음 클릭이 무엇을 하는가.**
|
|
11
|
+
|
|
12
|
+
| 현재 | 클릭 | 결과 |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| 비어 있음 | 아무 날 | 시작 |
|
|
15
|
+
| 고르는 중(시작만) | 시작 이후 | 끝 확정 |
|
|
16
|
+
| 고르는 중(시작만) | 시작 이전 | 두 값을 **바꿔 담는다** |
|
|
17
|
+
| 완성됨 | 아무 날 | 새로 시작 |
|
|
18
|
+
|
|
19
|
+
완성된 구간을 다시 눌렀을 때 가까운 끝을 옮기는 동작은 넣지 않았다 — "어느 쪽 끝인가"를
|
|
20
|
+
추측해야 하고, 추측이 틀리면 사용자가 왜 그렇게 됐는지 알 수 없다. 다시 시작하는 쪽이
|
|
21
|
+
예측 가능하다. 시작보다 앞선 날짜를 거절하지 않고 바꿔 담는 것도 같은 이유다.
|
|
22
|
+
|
|
23
|
+
**"고르는 중"은 별도 플래그가 아니다.** `start`가 있고 `end`가 없는 상태가 그것이다.
|
|
24
|
+
플래그를 두면 플래그와 값이 어긋난 상태가 표현 가능해진다.
|
|
25
|
+
|
|
26
|
+
**미리보기는 같은 함수를 쓴다.** `resolveDateRangeCellState(value, date, hoveredDate)`가
|
|
27
|
+
확정된 구간과 hover 미리보기를 함께 처리한다 — renderer가 두 벌의 칠하기 규칙을 만들지
|
|
28
|
+
않게 한다.
|
|
29
|
+
|
|
30
|
+
**Native.** renderer는 아직 없다(catalog `planned`). 구간 선택은 제스처·스크롤과 함께
|
|
31
|
+
검증해야 해서 Web 먼저 낸다.
|
package/docs/density.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# 전역 density 축
|
|
2
|
+
|
|
3
|
+
**문제.** 같은 화면의 목록·메뉴·표가 각각 다른 밀도로 그려졌다. `ListRow density`,
|
|
4
|
+
`Menu density`, `DataTable density`가 서로를 모르는 값이라 제품이 세 군데를 따로 맞췄다.
|
|
5
|
+
|
|
6
|
+
**해결.** Provider에 `density` 축을 하나 둔다(`"comfortable" | "compact"`, 기본
|
|
7
|
+
`comfortable`). 그 아래의 목록·메뉴·표가 **기본값으로** 이 값을 읽는다.
|
|
8
|
+
|
|
9
|
+
**컴포넌트의 명시적 prop이 언제나 이긴다.** 전역값은 기본이지 강제가 아니다. 한 화면
|
|
10
|
+
안에서 "표만 빽빽하게"가 필요한 경우가 실제로 있고, 그때 전역 축을 끄러 Provider까지
|
|
11
|
+
올라가야 한다면 축이 없느니만 못하다.
|
|
12
|
+
|
|
13
|
+
**어휘를 하나로 통일하지 않았다.** 목록의 `relaxed`와 표의 `regular`는 같은 말이 아니다.
|
|
14
|
+
억지로 합치면 둘 중 하나가 거짓말을 한다. 대신 `resolveDensityDefault(density, {
|
|
15
|
+
comfortable, compact })`가 전역 축을 각 컴포넌트의 어휘로 옮긴다 — 매핑이 한 곳에 있다.
|
|
16
|
+
|
|
17
|
+
**OS 신호가 없다.** 밀도는 "같은 화면에 얼마나 담을 것인가"라는 제품의 입장이지 사용자
|
|
18
|
+
설정이 아니다. 그래서 `systemDensity` 같은 짝을 두지 않았다. 반대로 큰 글자는 사용자
|
|
19
|
+
설정이므로, `textScale`이 큰 상태에서 `compact`를 켜는 것은 제품이 스스로 막아야 한다 —
|
|
20
|
+
계약이 대신 꺼 주면 제품이 그 충돌을 영영 모른다.
|
|
21
|
+
|
|
22
|
+
**Provider 밖에서는 각 recipe의 기본값이 그대로 선다.** 전역 축이 "감싸지 않은 렌더러"를
|
|
23
|
+
다르게 만들면 축 자체가 숨은 의존이 된다.
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# HJM Design System Expansion Roadmap
|
|
2
2
|
|
|
3
|
+
## 2026-09-16 React·React Native 완성
|
|
4
|
+
|
|
5
|
+
사용자가 Flutter를 제외하고 토스 UI를 중심 모티브로 컴포넌트를 채우도록 요청했습니다.
|
|
6
|
+
[전체 누락 목록·참조·완성 기준](react-native-completion.md)을 따라 renderer와 실제 조합
|
|
7
|
+
화면을 함께 만듭니다. 기존 소수 수정으로 전체 목표를 완료 처리하지 않습니다.
|
|
8
|
+
|
|
9
|
+
## 2026-09-15 실제 제품 조사에서 추가한 작업
|
|
10
|
+
|
|
11
|
+
[8개 앱 업데이트 목록](product-audit-2026-09-15.md)에 공통 결함·기존 컴포넌트 채택·제품 소유
|
|
12
|
+
기능을 구분했다. Toast/Notice compact 레이아웃과 Showcase 스타일 충돌부터 수정하고,
|
|
13
|
+
입력 설명/focus·Dialog 완료 신호·상태 조합을 다음 순서로 등록한다. 넓은 간격·어색한 버튼에
|
|
14
|
+
대한 사용자 피드백과 실제 소스/브라우저 결과가 근거이며 catalog maturity는 바꾸지 않는다.
|
|
15
|
+
|
|
3
16
|
## 무엇을 흡수하는가
|
|
4
17
|
|
|
5
18
|
HJM은 외부 라이브러리의 외형이나 public prop 이름을 복사하지 않습니다. 각 시스템에서
|
|
@@ -229,7 +242,7 @@ surface별 `planned → beta` gate는 public renderer export와 package CI가
|
|
|
229
242
|
남아 있지만 렌더되지 않고 월 범위 쿼리의 내부 계산일 뿐이며, 화면은 월 헤더 + 7일
|
|
230
243
|
날짜 레일이다. 앱 전체에 값 하나를 고르는 압축 트리거 UI도 없다.
|
|
231
244
|
|
|
232
|
-
이후 DatePicker에는 first-party Web·Native renderer와 canonical 환경 증거가 추가되어
|
|
245
|
+
이후 DatePicker와 Calendar에는 first-party Web·Native renderer와 canonical 환경 증거가 추가되어
|
|
233
246
|
`beta`로 승격됐다. 다만 위 실측은 여전히 유효하다. **제품 adoption evidence는 없고**,
|
|
234
247
|
Yajalal의 날짜 레일을 DatePicker 채택으로 세지 않는다. 따라서 `stable` 승격 근거는 없다.
|
|
235
248
|
|
|
@@ -238,8 +251,8 @@ Yajalal의 날짜 레일을 DatePicker 채택으로 세지 않는다. 따라서
|
|
|
238
251
|
|
|
239
252
|
### Batch 3 — 복합 데이터와 파일
|
|
240
253
|
|
|
241
|
-
- beta: Timeline, UploadItem, DatePicker, FilePicker
|
|
242
|
-
-
|
|
254
|
+
- beta: Timeline, UploadItem, DatePicker, Calendar, FilePicker
|
|
255
|
+
- composed: TimePicker (Web/RN 시·분 선택·확정 예제 제공, 독립 renderer는 없음)
|
|
243
256
|
- web: DataTable, Tree, ColorPicker, CommandPalette, SidePanel, Popover
|
|
244
257
|
|
|
245
258
|
FilePicker는 파일 선택 의도만 소유합니다. 업로드 요청·재시도·서버 상태는 제품이 소유하고,
|