@hjmds/design-contracts 2.3.0 → 2.4.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/behaviors.d.ts +6 -0
- package/dist/behaviors.d.ts.map +1 -1
- package/dist/behaviors.js.map +1 -1
- package/dist/calendar.d.ts +59 -0
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js +54 -0
- package/dist/calendar.js.map +1 -1
- package/dist/catalog.d.ts +17 -1
- package/dist/catalog.d.ts.map +1 -1
- package/dist/component-recipes.d.ts +1 -1
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +5 -3
- package/dist/component-recipes.js.map +1 -1
- package/dist/data-table.d.ts +6 -1
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +6 -1
- package/dist/data-table.js.map +1 -1
- package/dist/semantic-colors.d.ts +6 -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/shortcut-rail.d.ts +7 -1
- package/dist/shortcut-rail.d.ts.map +1 -1
- package/dist/shortcut-rail.js +7 -1
- package/dist/shortcut-rail.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/consumer-policy.md +2 -2
- package/docs/generated/component-maturity.md +1 -1
- package/docs/generated/renderer-evidence.json +3 -3
- package/docs/generated/renderer-evidence.md +1 -1
- package/docs/generated/showcase-manifest.json +4 -2
- package/docs/reference-controls.md +1 -1
- package/docs/usage/README.md +5 -5
- package/docs/usage/SELECTION.md +1 -1
- package/docs/usage/STANDARD.md +1 -1
- package/docs/usage/components/calendar.md +15 -0
- package/docs/usage/components/card.md +2 -2
- package/docs/usage/components/data-table.md +11 -7
- package/docs/usage/components/segmented-control.md +1 -1
- package/docs/usage/components/select.md +20 -2
- package/docs/usage/compositions/shortcut-rail.md +3 -2
- package/package.json +1 -1
|
@@ -35,6 +35,21 @@ import는 `@hjmds/design-contracts/components/calendar`이며 Web/Native가 같
|
|
|
35
35
|
월·오늘·시간대·업무별 선택 제한과 콘텐츠는 제품이 정하고 함수는 시계를 읽지 않는다.
|
|
36
36
|
실행 예제는 양 renderer가 공유하는 `showcase/shared/calendar-example.ts`다(2026-10-19 중복 월 계산 제거).
|
|
37
37
|
|
|
38
|
+
## 접어서 한 주만 보기 (`fold`)
|
|
39
|
+
|
|
40
|
+
2026-11-04 오늘야구 경기 화면에서 날짜 카드 줄을 걷어내고, 평소엔 한 주만 보이다가 그 자리에서 한 달로 펼치는
|
|
41
|
+
달력을 쓰자는 사용자 결정으로 추가했다. 양 renderer 공통이다.
|
|
42
|
+
|
|
43
|
+
- `fold={{ expanded, onExpandedChange, label }}`. 접힌 상태는 선택한 날짜가 있는 주, 없으면 오늘이 있는 주, 그것도 없으면 첫 주를 보인다(`resolveCalendarVisibleRows`).
|
|
44
|
+
- 격자 아래 손잡이(접혔을 때 아래 화살표, 펼쳤을 때 위 화살표, `calendarRecipe.fold.handle`)를 누르면 펼치고 접는다. 짧은 막대는 열린다는 뜻이 드러나지 않아 같은 날 화살표로 바꿨다. 손잡이 접근성 이름 `label`은 제품이 상태에 맞게 지역화한다(「달력 펼치기」/「달력 접기」).
|
|
45
|
+
- 접힌 상태에서 주 단위 이동·스와이프는 제품이 날짜를 바꿔 처리한다. 월 이동 버튼은 주지 않으면 그리지 않는다.
|
|
46
|
+
- 날짜별 점(경기 있음 등)은 기존 `renderCellContent`로 붙인다.
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<Calendar fold={{ expanded, onExpandedChange: setExpanded, label: expanded ? t("calendar.collapse") : t("calendar.expand") }}
|
|
50
|
+
descriptor={{ grid, monthLabel, selectedDate, onSelectionChange: setDate }} composeAccessibleName={name} />
|
|
51
|
+
```
|
|
52
|
+
|
|
38
53
|
## 최소 사용 예
|
|
39
54
|
|
|
40
55
|
```tsx
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# Card
|
|
2
2
|
|
|
3
|
-
2026-10-25 번뚝 글자 크기 리뷰 반영(개발 소스·미게시): 카드 제목은 `bodyLarge` 16/24, `medium` 굵기를 사용한다. 반복 카드가 페이지 제목과 경쟁하지 않도록 양 renderer에 동일하게 적용한다.
|
|
4
|
-
|
|
5
3
|
- 단계: 컴포넌트
|
|
6
4
|
- 상태: 배포
|
|
7
5
|
- 지원: Web · Native
|
|
@@ -10,6 +8,8 @@
|
|
|
10
8
|
- 근거: recipe `cardRecipe`(`src/card.ts`), 바탕 `surfaceRecipe`(`src/base-recipes.ts`). 별도 계약 문서는 없다
|
|
11
9
|
- 스토리북: `배포/컴포넌트/데이터 표시/카드` · `배포/컴포넌트/데이터 표시/인용 카드`
|
|
12
10
|
|
|
11
|
+
2026-10-25 번뚝 글자 크기 리뷰 반영(개발 소스·미게시): 카드 제목은 `bodyLarge` 16/24, `medium` 굵기를 사용한다. 반복 카드가 페이지 제목과 경쟁하지 않도록 양 renderer에 동일하게 적용한다.
|
|
12
|
+
|
|
13
13
|
## 언제 쓰나
|
|
14
14
|
|
|
15
15
|
인용문과 출처를 묶으면 미게시 동반 API [`QuoteCard`](#인용문과-출처quotecard-미게시)를 사용한다. 일반 카드에 인용 표시·출처 여백을 다시 조립하지 않는다.
|
|
@@ -52,7 +52,7 @@ import { DataTable } from "@hjmds/react/data-table";
|
|
|
52
52
|
<DataTable
|
|
53
53
|
columns={[
|
|
54
54
|
{ id: "name", header: t("orders.col.name"), sortable: true },
|
|
55
|
-
{ id: "total", header: t("orders.col.total")
|
|
55
|
+
{ id: "total", header: t("orders.col.total") },
|
|
56
56
|
]}
|
|
57
57
|
rows={orders.map((order) => ({ id: order.id }))}
|
|
58
58
|
renderCell={(rowId, columnId) => cellFor(rowId, columnId)}
|
|
@@ -83,8 +83,8 @@ import {
|
|
|
83
83
|
|
|
84
84
|
const columns = [
|
|
85
85
|
{ id: "name", header: t("record.col.player"), pinned: "start" as const },
|
|
86
|
-
{ id: "avg", header: t("record.col.avg"),
|
|
87
|
-
{ id: "era", header: t("record.col.era"),
|
|
86
|
+
{ id: "avg", header: t("record.col.avg"), sortable: true, sortDescFirst: true },
|
|
87
|
+
{ id: "era", header: t("record.col.era"), sortable: true },
|
|
88
88
|
];
|
|
89
89
|
const [sort, setSort] = useState<DataTableSortState<string>>({ columnId: "avg", direction: "descending" });
|
|
90
90
|
// 표시 문자열("0.340", "—")에서 정렬 키를 만들고, 빈 값은 방향과 상관없이 마지막이다.
|
|
@@ -117,7 +117,7 @@ const sorted = sortDataTableRows(players, sort, (player, columnId) => toDataTabl
|
|
|
117
117
|
| `selection` | `{ mode: "none" }` · `{ mode: "single", selectedKey: Key \| null, onSelectionChange: (key: Key \| null) => void, disallowEmptySelection? }` · `{ mode: "multiple", selectedKeys: ReadonlySet<Key>, onSelectionChange: (keys: ReadonlySet<Key>) => void }` | 없음(선택 열 없음) | Web만. 제어(`selectedKey(s)`) 대신 `defaultSelectedKey(s)`를 주면 비제어로 내부 상태가 유지된다 |
|
|
118
118
|
| `asyncState` | `{ status: "idle" }` · `{ status: "loading" \| "loadingMore" \| "empty" \| "error", message: string }` | `{ status: "idle" }` | `error`는 `role="alert"`, 나머지는 `role="status"`로 표 위에 나온다 |
|
|
119
119
|
| `density` | `regular` · `compact` | Provider 밀도 | 지정하지 않으면 Provider 밀도를 따른다(`comfortable` → `regular`) |
|
|
120
|
-
| 열 `align` | `start` · `center` · `end` | `
|
|
120
|
+
| 열 `align` | `start` · `center` · `end` | `center` | 머리 칸과 본문 칸 모두에 적용. 2026-11-04부터 모든 열 가운데가 기본이며 새 표에서 지정하지 않는다(호환용으로 남김). 웹 `Table`도 같은 기본값 |
|
|
121
121
|
| 열 `sortable` | `boolean` | `false` | — |
|
|
122
122
|
| 열 `width` | 양수 px | — | 힌트 |
|
|
123
123
|
| 열 `sortDescFirst` | `boolean` | `false` | 첫 누름이 내림차순(높은 값 먼저). 낮을수록 좋은 열(평균자책점·순위)은 끈다. 미게시(1.20.0 이후) |
|
|
@@ -141,15 +141,15 @@ const sorted = sortDataTableRows(players, sort, (player, columnId) => toDataTabl
|
|
|
141
141
|
| --- | --- | --- |
|
|
142
142
|
| 크기 | 본문 폭을 채운다(표 `inline-size: 100%`). 선택 열 폭 `control.minTouchTarget` 44, 정렬 버튼·체크 상자 터치 영역 44 | `packages/react/src/styles.css` `.hjm-data-table*`, `src/data-table.ts` |
|
|
143
143
|
| 간격 | 루트 세로 간격 `spacing.sm` 12. 셀 여백(`dataTableRecipe.density`): `regular` 세로 `spacing.sm` 12 · 가로 `spacing.md` 16, `compact` 세로 `spacing.xs` 8 · 가로 `spacing.sm` 12. `footer` 간격 `spacing.sm` 12 | `packages/react/src/styles.css` |
|
|
144
|
-
| 순서·정렬 | 위에서 [상태 메시지] → 표 → [footer]. 선택 열은 맨 앞.
|
|
144
|
+
| 순서·정렬 | 위에서 [상태 메시지] → 표 → [footer]. 선택 열은 맨 앞. 머리 칸과 본문 칸은 글자·숫자 모두 가운데 정렬이다(`dataTableColumnDefaults.align`). 셀은 위 정렬. `footer`는 한 줄 flex(`space-between`, 넘치면 줄바꿈)이며 요약을 앞에, [Pagination](pagination.md)을 끝에 둔다 | `packages/react/src/data-table.tsx` |
|
|
145
145
|
| 고정·스크롤 | `maxBodyHeight`를 주면 Web은 스크롤 상자(`max-block-size`, `overflow: auto`) 안에서 `thead`를 `sticky`로, Native는 머리글을 본문 밖에 두고 본문만 세로 `ScrollView`에 넣는다. 그 밖의 Web 표는 가로 스크롤 컨테이너를 그리지 않고 긴 글자를 줄바꿈한다. `pinned: "start"` 열은 `position: sticky`로 시작 쪽에 붙는다. Native는 고정 열을 제자리에 두고 나머지 열을 가로 `ScrollView`에 넣으며 스크롤 가장자리에 흐림을 둔다 | `packages/react/src/styles.css`, `react-native/src/data-table.tsx`, `splitPinnedDataTableColumns` |
|
|
146
146
|
| 좁은 폭·큰 글자 | 열 비교가 목적인 기록 표는 열을 빼지 말고 이름 열을 `pinned: "start"`로 고정해 가로 스크롤로 보존한다. 행 하나가 하나의 항목이면 [List](list.md)로 바꾼다 | [계약](../../data-table.md#정렬-helper와-native-renderer--2026-10-14) |
|
|
147
147
|
|
|
148
148
|
```text
|
|
149
149
|
┌ DataTable ───────────────────────────────────────┐
|
|
150
150
|
│ 불러오는 중…(asyncState 메시지, status/alert) │
|
|
151
|
-
│ ☐ │
|
|
152
|
-
│ ☐ │
|
|
151
|
+
│ ☐ │ 이름 ▲ │ 상태 │ 합계 │ ← 머리 행
|
|
152
|
+
│ ☐ │ … │ … │ 12,000 │
|
|
153
153
|
├────────────────────────────────────────────────────┤
|
|
154
154
|
│ 3개 선택됨 [‹ 1 2 3 ›] Pagination │ ← footer
|
|
155
155
|
└────────────────────────────────────────────────────┘
|
|
@@ -157,6 +157,10 @@ const sorted = sortDataTableRows(players, sort, (player, columnId) => toDataTabl
|
|
|
157
157
|
|
|
158
158
|
## 꼭 지킬 것
|
|
159
159
|
|
|
160
|
+
- 열 정렬은 지정하지 않는다. 모든 표는 머리글과 본문을 가운데 정렬한다. 2026-11-04 사용자 결정: 기록 표는 1~2자리 횟수가
|
|
161
|
+
대부분인데 머리글 길이(「사구」~「도루실패」)가 달라, 끝 정렬한 숫자가 머리글 아래에서 들쭉날쭉해 보였다(오늘야구 최근 10경기).
|
|
162
|
+
- 예외: 사진·배지와 이름을 함께 그리는 식별 열(선수·팀 이름 칸)만 `align: "start"`로 둔다. 가운데로 두면 이름 길이마다
|
|
163
|
+
시작 위치가 달라져 목록이 들쭉날쭉해진다(같은 날 오늘야구 박스스코어). 날짜·시즌처럼 글자만 있는 식별 열은 가운데다.
|
|
160
164
|
- 열 `id`·행 `id`는 비어 있지 않고 중복이 없어야 한다. 열은 하나 이상. 어기면 렌더 중 `TypeError`가 난다.
|
|
161
165
|
- `sortState`는 `sortable: true`인 열만 가리킨다. 아니면 `TypeError`.
|
|
162
166
|
- `labels`의 모든 문구는 i18n 키로 만든다. `selectRow`는 행 `id`를 받으므로 사람이 읽을 이름으로 바꿔 돌려준다.
|
|
@@ -76,7 +76,7 @@ import { SegmentedControl } from "@hjmds/react-native/inputs";
|
|
|
76
76
|
| `presentation` | `connected` · `pills` | `connected` | 연결된 보기 전환 / 독립된 카테고리 필터. `pills`는 1.13.0 변경 |
|
|
77
77
|
| `selectionTone` | `neutral` · `brand` | `brand` | 기본 선택 면은 `primary`와 `onPrimary`. `neutral`은 `surfaceSelected`와 `text`로 차분하게 표시한다. `text` 표현은 채움 없이 기존 표시를 유지한다 |
|
|
78
78
|
| `size` | `small` · `medium` | `medium` | 항목 최소 높이 36(+hitSlop 4) · 44 |
|
|
79
|
-
| `value` · `defaultValue` | 항목 값 | 첫 번째 활성 항목 |
|
|
79
|
+
| `value` · `defaultValue` | 항목 값 · `value`만 `null` | 첫 번째 활성 항목 | `pills` 전용으로 제어 `value={null}`이면 아무 알약도 선택되지 않는다. 선택지를 나눠 보여 주는 필터(10년 단위 시즌 연도)에서 고른 값이 다른 구간에 있을 때 쓴다. 누르면 그 값으로 `onValueChange`가 불린다. `connected`·`text`에 `null`을 주면 `TypeError`(보기 전환은 항상 현재 보기를 보여 준다). 2026-11-04 추가 |
|
|
80
80
|
| `onValueChange` | Web `(value: string) => void` · Native `(value: Value) => void` | — | 다른 항목을 고를 때. `null`은 오지 않는다 |
|
|
81
81
|
| `label` | `string` | 필수 | 화면에 보이지 않는 접근성 이름. 보이는 제목은 바깥에 둔다 |
|
|
82
82
|
| `disabled` | `boolean` | `false` | 전체 비활성(Web은 fieldset 속성) |
|
|
@@ -23,7 +23,7 @@ Web은 popover listbox, Native는 modal sheet로 목록을 연다.
|
|
|
23
23
|
| 입력하며 후보를 거름 | [Combobox](combobox.md) |
|
|
24
24
|
| 설정 화면의 즉시 저장 한 값 선택(언어·화면 테마) | [ChoiceRow](choice-row.md) |
|
|
25
25
|
| 행동 목록(편집·삭제) | [Menu](menu.md) |
|
|
26
|
-
| 여러
|
|
26
|
+
| 여러 개를 펼쳐 보이며 선택 | [CheckboxGroup](checkbox-group.md), [TransferList](transfer-list.md). 접힌 필드 하나에서 고르면 아래 [여러 개 선택](#여러-개-선택-selectionmodemultiple) |
|
|
27
27
|
| Web 폼 제출·자동완성·모바일 브라우저 picker가 중요 | `NativeSelect`(아래) |
|
|
28
28
|
|
|
29
29
|
## 공개 이름과 import
|
|
@@ -72,11 +72,29 @@ import { NativeSelect } from "@hjmds/react/forms";
|
|
|
72
72
|
options={[{ value: "kr", label: t("country.kr") }]} value={code} onValueChange={setCode} />
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
## 여러 개 선택 (`selectionMode="multiple"`)
|
|
76
|
+
|
|
77
|
+
2026-11-04 오늘야구 선수 기록 그래프에서 「지표」와 「비교 안 함」 두 필드로 나눠 고르던 것을 필드 하나에서 개수 제한 없이
|
|
78
|
+
고르자는 사용자 요청으로 추가했다. 양 renderer 공통이다.
|
|
79
|
+
|
|
80
|
+
- `selectedKeys`·`defaultSelectedKeys`(`ReadonlySet`)와 `onSelectedKeysChange`를 쓴다. `selectedKey` 계열과 함께 주면 오류다.
|
|
81
|
+
- 항목을 누르면 체크만 바뀌고 목록은 열린 채로 남는다. 닫기·바깥 누름·Escape로 닫는다.
|
|
82
|
+
- 필드에는 고른 항목의 `selectedLabel ?? label`을 **목록 순서대로** `, `로 이어 한 줄로 보인다(누른 순서 아님).
|
|
83
|
+
다른 형식이 필요하면 `formatSelectedLabels`로 지역화한다. 아무것도 고르지 않으면 `placeholder`다.
|
|
84
|
+
- 빈 선택 항목(`emptySelectionLabel`)은 나오지 않는다. 체크를 모두 끄면 빈 선택이다.
|
|
85
|
+
- Web listbox는 `aria-multiselectable`, Native 항목은 `checkbox` 역할이다. `name`이 있으면 고른 값마다 hidden input을 둔다.
|
|
86
|
+
- 개수 상한은 제품이 정한다. HJM은 제한하지 않는다.
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
<Select label="지표" placeholder="지표" dismissLabel="닫기" selectionMode="multiple"
|
|
90
|
+
selectedKeys={metrics} onSelectedKeysChange={setMetrics} items={metricItems} />
|
|
91
|
+
```
|
|
92
|
+
|
|
75
93
|
## 축과 기본값
|
|
76
94
|
|
|
77
95
|
| prop | 값 | 기본값 | 설명 |
|
|
78
96
|
| --- | --- | --- | --- |
|
|
79
|
-
| `items` / `sections` | `readonly { id, label: string, textValue: string, description?, disabled? }[]` / 섹션 `{ id, label?, accessibilityLabel?, items }` | — | 항목 필수 필드는 `id`·`label`·`textValue`. Native는 `source`도
|
|
97
|
+
| `items` / `sections` | `readonly { id, label: string, textValue: string, description?, selectedLabel?, disabled? }[]` / 섹션 `{ id, label?, accessibilityLabel?, items }` | — | 항목 필수 필드는 `id`·`label`·`textValue`. Native는 `source`도 가능. `selectedLabel`은 고른 뒤 닫힌 선택 창에만 보이는 짧은 이름(목록·접근성 값은 `label`). 목록은 「도루저지율 (CSP)」, 선택 창은 「도루저지율」처럼 반폭 창이 두 줄로 넘치지 않게 할 때 쓴다(2026-11-04) |
|
|
80
98
|
| `size` | `medium` · `large` | `medium` | 트리거 높이 44 · 52 |
|
|
81
99
|
| `density` | `compact` · `comfortable` | `comfortable` | 선택지 행 44 · 56 |
|
|
82
100
|
| `selectedKey` · `defaultSelectedKey` | 키 · `null` | 비제어 `null` | 제어하면 Web은 `onSelectionChange` 필수 |
|
|
@@ -42,9 +42,10 @@
|
|
|
42
42
|
| 바깥 틀 | 가로 목록 | 한 줄, 내용 폭 항목 | 항목 간 spacing.xs8 |
|
|
43
43
|
| 표면 | chipRecipe | 열기/삭제 뒤 하나의 pill | small36, medium44, full 모서리, idle 배경/테두리 |
|
|
44
44
|
| 열기 | button/Pressable | 이름 앞 leading 장식 가능 | 최소44 터치 영역, small 좌우 sm12/아이콘 간 xxs4, medium md16/xs8 |
|
|
45
|
-
| 삭제 | button/Pressable | 열기와 나란한 끝 쪽 | 폭44, 최소 높이44, 기본 glyph.xs14의
|
|
45
|
+
| 삭제 | button/Pressable | 열기와 나란한 끝 쪽 | 보이는 폭 xl24(`removeWidth`), 터치 폭 44는 pill 끝 바깥으로 20 확장(`removeHitSlop`), 최소 높이44, 기본 glyph.xs14의 ×. 삭제가 있으면 열기의 끝 쪽 여백은 0 |
|
|
46
46
|
|
|
47
|
-
36 표면도 각 버튼이 최소44 터치 영역을 가지며 영역을 서로 겹치지 않는다.
|
|
47
|
+
36 표면도 각 버튼이 최소44 터치 영역을 가지며 영역을 서로 겹치지 않는다. 2026-11-04 오늘야구 「퓨처스 ×」가 이름의 두 배쯤으로
|
|
48
|
+
보여, 삭제의 터치 영역을 그리는 대신 끝 바깥으로 넓혔다.
|
|
48
49
|
모든 항목이 마운트된 가로 한 줄이며 자동 회전·선택·가상화는 없다.
|
|
49
50
|
Native는 물리 LTR scroll 좌표와 RTL 행 배치를 분리한다.
|
|
50
51
|
|