moru-krds-react 0.1.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/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/accordion/index.d.ts +63 -0
- package/dist/accordion/index.d.ts.map +1 -0
- package/dist/accordion/index.js +145 -0
- package/dist/accordion/index.js.map +1 -0
- package/dist/alert/index.d.ts +44 -0
- package/dist/alert/index.d.ts.map +1 -0
- package/dist/alert/index.js +66 -0
- package/dist/alert/index.js.map +1 -0
- package/dist/avatar/index.d.ts +102 -0
- package/dist/avatar/index.d.ts.map +1 -0
- package/dist/avatar/index.js +146 -0
- package/dist/avatar/index.js.map +1 -0
- package/dist/badge/index.d.ts +49 -0
- package/dist/badge/index.d.ts.map +1 -0
- package/dist/badge/index.js +41 -0
- package/dist/badge/index.js.map +1 -0
- package/dist/breadcrumb/index.d.ts +43 -0
- package/dist/breadcrumb/index.d.ts.map +1 -0
- package/dist/breadcrumb/index.js +43 -0
- package/dist/breadcrumb/index.js.map +1 -0
- package/dist/button/index.d.ts +78 -0
- package/dist/button/index.d.ts.map +1 -0
- package/dist/button/index.js +83 -0
- package/dist/button/index.js.map +1 -0
- package/dist/checkbox/index.d.ts +97 -0
- package/dist/checkbox/index.d.ts.map +1 -0
- package/dist/checkbox/index.js +110 -0
- package/dist/checkbox/index.js.map +1 -0
- package/dist/combobox/index.d.ts +135 -0
- package/dist/combobox/index.d.ts.map +1 -0
- package/dist/combobox/index.js +416 -0
- package/dist/combobox/index.js.map +1 -0
- package/dist/context-menu/index.d.ts +94 -0
- package/dist/context-menu/index.d.ts.map +1 -0
- package/dist/context-menu/index.js +399 -0
- package/dist/context-menu/index.js.map +1 -0
- package/dist/critical-alert/index.d.ts +39 -0
- package/dist/critical-alert/index.d.ts.map +1 -0
- package/dist/critical-alert/index.js +50 -0
- package/dist/critical-alert/index.js.map +1 -0
- package/dist/data-table/index.d.ts +94 -0
- package/dist/data-table/index.d.ts.map +1 -0
- package/dist/data-table/index.js +128 -0
- package/dist/data-table/index.js.map +1 -0
- package/dist/disclosure/index.d.ts +35 -0
- package/dist/disclosure/index.d.ts.map +1 -0
- package/dist/disclosure/index.js +54 -0
- package/dist/disclosure/index.js.map +1 -0
- package/dist/drawer/index.d.ts +106 -0
- package/dist/drawer/index.d.ts.map +1 -0
- package/dist/drawer/index.js +352 -0
- package/dist/drawer/index.js.map +1 -0
- package/dist/empty-state/index.d.ts +86 -0
- package/dist/empty-state/index.d.ts.map +1 -0
- package/dist/empty-state/index.js +138 -0
- package/dist/empty-state/index.js.map +1 -0
- package/dist/env.d.js +1 -0
- package/dist/env.d.js.map +1 -0
- package/dist/field/index.d.ts +149 -0
- package/dist/field/index.d.ts.map +1 -0
- package/dist/field/index.js +121 -0
- package/dist/field/index.js.map +1 -0
- package/dist/file-upload/index.d.ts +140 -0
- package/dist/file-upload/index.d.ts.map +1 -0
- package/dist/file-upload/index.js +249 -0
- package/dist/file-upload/index.js.map +1 -0
- package/dist/footer/index.d.ts +155 -0
- package/dist/footer/index.d.ts.map +1 -0
- package/dist/footer/index.js +186 -0
- package/dist/footer/index.js.map +1 -0
- package/dist/header/index.d.ts +170 -0
- package/dist/header/index.d.ts.map +1 -0
- package/dist/header/index.js +258 -0
- package/dist/header/index.js.map +1 -0
- package/dist/identifier/index.d.ts +38 -0
- package/dist/identifier/index.d.ts.map +1 -0
- package/dist/identifier/index.js +55 -0
- package/dist/identifier/index.js.map +1 -0
- package/dist/in-page-navigation/index.d.ts +71 -0
- package/dist/in-page-navigation/index.d.ts.map +1 -0
- package/dist/in-page-navigation/index.js +137 -0
- package/dist/in-page-navigation/index.js.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/input-group/index.d.ts +66 -0
- package/dist/input-group/index.d.ts.map +1 -0
- package/dist/input-group/index.js +85 -0
- package/dist/input-group/index.js.map +1 -0
- package/dist/lib/krds.d.ts +45 -0
- package/dist/lib/krds.d.ts.map +1 -0
- package/dist/lib/krds.js +11 -0
- package/dist/lib/krds.js.map +1 -0
- package/dist/lib/link.d.ts +34 -0
- package/dist/lib/link.d.ts.map +1 -0
- package/dist/lib/link.js +8 -0
- package/dist/lib/link.js.map +1 -0
- package/dist/main-menu/index.d.ts +123 -0
- package/dist/main-menu/index.d.ts.map +1 -0
- package/dist/main-menu/index.js +613 -0
- package/dist/main-menu/index.js.map +1 -0
- package/dist/masthead/index.d.ts +25 -0
- package/dist/masthead/index.d.ts.map +1 -0
- package/dist/masthead/index.js +21 -0
- package/dist/masthead/index.js.map +1 -0
- package/dist/modal/index.d.ts +105 -0
- package/dist/modal/index.d.ts.map +1 -0
- package/dist/modal/index.js +285 -0
- package/dist/modal/index.js.map +1 -0
- package/dist/pagination/index.d.ts +58 -0
- package/dist/pagination/index.d.ts.map +1 -0
- package/dist/pagination/index.js +82 -0
- package/dist/pagination/index.js.map +1 -0
- package/dist/progress/index.d.ts +58 -0
- package/dist/progress/index.d.ts.map +1 -0
- package/dist/progress/index.js +125 -0
- package/dist/progress/index.js.map +1 -0
- package/dist/radio/index.d.ts +111 -0
- package/dist/radio/index.d.ts.map +1 -0
- package/dist/radio/index.js +136 -0
- package/dist/radio/index.js.map +1 -0
- package/dist/resize/index.d.ts +118 -0
- package/dist/resize/index.d.ts.map +1 -0
- package/dist/resize/index.js +342 -0
- package/dist/resize/index.js.map +1 -0
- package/dist/scroll-area/index.d.ts +40 -0
- package/dist/scroll-area/index.d.ts.map +1 -0
- package/dist/scroll-area/index.js +52 -0
- package/dist/scroll-area/index.js.map +1 -0
- package/dist/select/index.d.ts +67 -0
- package/dist/select/index.d.ts.map +1 -0
- package/dist/select/index.js +61 -0
- package/dist/select/index.js.map +1 -0
- package/dist/separator/index.d.ts +47 -0
- package/dist/separator/index.d.ts.map +1 -0
- package/dist/separator/index.js +61 -0
- package/dist/separator/index.js.map +1 -0
- package/dist/side-navigation/index.d.ts +78 -0
- package/dist/side-navigation/index.d.ts.map +1 -0
- package/dist/side-navigation/index.js +188 -0
- package/dist/side-navigation/index.js.map +1 -0
- package/dist/skeleton/index.d.ts +76 -0
- package/dist/skeleton/index.d.ts.map +1 -0
- package/dist/skeleton/index.js +78 -0
- package/dist/skeleton/index.js.map +1 -0
- package/dist/skip-link/index.d.ts +46 -0
- package/dist/skip-link/index.d.ts.map +1 -0
- package/dist/skip-link/index.js +37 -0
- package/dist/skip-link/index.js.map +1 -0
- package/dist/spinner/index.d.ts +58 -0
- package/dist/spinner/index.d.ts.map +1 -0
- package/dist/spinner/index.js +36 -0
- package/dist/spinner/index.js.map +1 -0
- package/dist/step-indicator/index.d.ts +41 -0
- package/dist/step-indicator/index.d.ts.map +1 -0
- package/dist/step-indicator/index.js +63 -0
- package/dist/step-indicator/index.js.map +1 -0
- package/dist/structured-list/index.d.ts +150 -0
- package/dist/structured-list/index.d.ts.map +1 -0
- package/dist/structured-list/index.js +194 -0
- package/dist/structured-list/index.js.map +1 -0
- package/dist/tab/index.d.ts +108 -0
- package/dist/tab/index.d.ts.map +1 -0
- package/dist/tab/index.js +198 -0
- package/dist/tab/index.js.map +1 -0
- package/dist/table/index.d.ts +75 -0
- package/dist/table/index.d.ts.map +1 -0
- package/dist/table/index.js +91 -0
- package/dist/table/index.js.map +1 -0
- package/dist/tag/index.d.ts +71 -0
- package/dist/tag/index.d.ts.map +1 -0
- package/dist/tag/index.js +45 -0
- package/dist/tag/index.js.map +1 -0
- package/dist/text-input/index.d.ts +69 -0
- package/dist/text-input/index.d.ts.map +1 -0
- package/dist/text-input/index.js +70 -0
- package/dist/text-input/index.js.map +1 -0
- package/dist/text-list/index.d.ts +71 -0
- package/dist/text-list/index.d.ts.map +1 -0
- package/dist/text-list/index.js +79 -0
- package/dist/text-list/index.js.map +1 -0
- package/dist/textarea/index.d.ts +29 -0
- package/dist/textarea/index.d.ts.map +1 -0
- package/dist/textarea/index.js +69 -0
- package/dist/textarea/index.js.map +1 -0
- package/dist/toast/index.d.ts +76 -0
- package/dist/toast/index.d.ts.map +1 -0
- package/dist/toast/index.js +183 -0
- package/dist/toast/index.js.map +1 -0
- package/dist/tooltip/index.d.ts +84 -0
- package/dist/tooltip/index.d.ts.map +1 -0
- package/dist/tooltip/index.js +329 -0
- package/dist/tooltip/index.js.map +1 -0
- package/package.json +57 -0
- package/src/accordion/index.tsx +251 -0
- package/src/alert/index.tsx +111 -0
- package/src/avatar/index.tsx +272 -0
- package/src/badge/index.tsx +91 -0
- package/src/breadcrumb/index.tsx +125 -0
- package/src/button/index.tsx +190 -0
- package/src/checkbox/index.tsx +247 -0
- package/src/combobox/index.tsx +700 -0
- package/src/context-menu/index.tsx +623 -0
- package/src/critical-alert/index.tsx +102 -0
- package/src/data-table/index.tsx +289 -0
- package/src/disclosure/index.tsx +87 -0
- package/src/drawer/index.tsx +516 -0
- package/src/empty-state/index.tsx +256 -0
- package/src/env.d.ts +10 -0
- package/src/field/index.tsx +307 -0
- package/src/file-upload/index.tsx +492 -0
- package/src/footer/index.tsx +393 -0
- package/src/header/index.tsx +498 -0
- package/src/identifier/index.tsx +96 -0
- package/src/in-page-navigation/index.tsx +258 -0
- package/src/index.ts +72 -0
- package/src/input-group/index.tsx +197 -0
- package/src/lib/krds.ts +75 -0
- package/src/lib/link.tsx +35 -0
- package/src/main-menu/index.tsx +912 -0
- package/src/masthead/index.tsx +50 -0
- package/src/modal/index.tsx +457 -0
- package/src/pagination/index.tsx +198 -0
- package/src/progress/index.tsx +198 -0
- package/src/radio/index.tsx +283 -0
- package/src/resize/index.tsx +541 -0
- package/src/scroll-area/index.tsx +96 -0
- package/src/select/index.tsx +136 -0
- package/src/separator/index.tsx +107 -0
- package/src/side-navigation/index.tsx +336 -0
- package/src/skeleton/index.tsx +172 -0
- package/src/skip-link/index.tsx +89 -0
- package/src/spinner/index.tsx +98 -0
- package/src/step-indicator/index.tsx +122 -0
- package/src/structured-list/index.tsx +395 -0
- package/src/tab/index.tsx +348 -0
- package/src/table/index.tsx +186 -0
- package/src/tag/index.tsx +127 -0
- package/src/text-input/index.tsx +156 -0
- package/src/text-list/index.tsx +177 -0
- package/src/textarea/index.tsx +113 -0
- package/src/toast/index.tsx +311 -0
- package/src/tooltip/index.tsx +496 -0
- package/styles/krds-tailwind.css +335 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import { Children, isValidElement, type ComponentPropsWithRef, type ReactNode } from 'react';
|
|
2
|
+
import { cx, type KrdsSize } from '../lib/krds';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 구조화 목록 — KRDS `krds-structured-list`.
|
|
6
|
+
*
|
|
7
|
+
* 「유사하거나 관련된 콘텐츠 집합」을 카드로 늘어놓는다. 대부분은 {@link StructuredCard} 한 줄로 끝나고,
|
|
8
|
+
* 순서를 바꾸거나 킷에 없는 것을 끼울 때만 조각(`StructuredItem*`)을 직접 조립한다.
|
|
9
|
+
* 행·열이 있는 자료는 카드가 아니라 `ui/table` · `ui/data-table` 이다.
|
|
10
|
+
* `meta`(부가 정보 줄)는 **전폭 배치 전용**이다 — `layout="grid"` 에서는 구분선이 걸리지 않는다.
|
|
11
|
+
* 카드 안 버튼에는 `title` 로 어느 항목의 것인지 알린다(「신청하기」가 20개인 목록을 생각하라).
|
|
12
|
+
* 목록이 비면 `StructuredList` 대신 {@link StructuredListEmpty} 를 그린다.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <StructuredListToolbar info={`전체 ${total}건`}>
|
|
16
|
+
* <SortSelect aria-label="정렬기준" options={sorts} />
|
|
17
|
+
* </StructuredListToolbar>
|
|
18
|
+
* <StructuredList layout="full">
|
|
19
|
+
* {rows.map((r) => (
|
|
20
|
+
* <StructuredCard
|
|
21
|
+
* key={r.id}
|
|
22
|
+
* href={`/board/notice/${r.id}`}
|
|
23
|
+
* title={r.title}
|
|
24
|
+
* summary={r.summary}
|
|
25
|
+
* dateLabel="등록일"
|
|
26
|
+
* dateValue={r.createdAt}
|
|
27
|
+
* badge={<Badge tone="primary">공지</Badge>}
|
|
28
|
+
* tags={r.tags}
|
|
29
|
+
* />
|
|
30
|
+
* ))}
|
|
31
|
+
* </StructuredList>
|
|
32
|
+
*
|
|
33
|
+
* 자세히: docs/krds/09-부품-노트.md#구조화목록
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 배치. `grid` 3열 격자(기본) · `full` 한 줄에 한 항목(제목이 커진다) · `compact` 전폭이되 제목은 그대로.
|
|
38
|
+
*/
|
|
39
|
+
export type StructuredListLayout = 'grid' | 'full' | 'compact';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 크기. 바꾸는 것은 **항목 안쪽 여백**과 우상단 액션 버튼 위치다.
|
|
43
|
+
* `layout="full"` 과 함께 주면 제목 크기는 배치 쪽이 이겨 여백만 줄어든다.
|
|
44
|
+
*/
|
|
45
|
+
export type StructuredListSize = Extract<KrdsSize, 'small' | 'medium' | 'large'>;
|
|
46
|
+
|
|
47
|
+
const LAYOUT_CLASS: Record<StructuredListLayout, string | undefined> = {
|
|
48
|
+
grid: undefined,
|
|
49
|
+
full: 'type-full',
|
|
50
|
+
compact: 'type2',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const SIZE_CLASS: Record<StructuredListSize, string> = {
|
|
54
|
+
small: 'sm',
|
|
55
|
+
medium: 'md',
|
|
56
|
+
large: 'lg',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type StructuredListProps = ComponentPropsWithRef<'ul'> & {
|
|
60
|
+
/** 배치. 기본 `grid`. */
|
|
61
|
+
layout?: StructuredListLayout;
|
|
62
|
+
/** 크기. 기본 `medium`. */
|
|
63
|
+
size?: StructuredListSize;
|
|
64
|
+
/** 항목마다 선택 체크박스가 있을 때 켠다 — 켜지 않으면 체크박스와 우상단 버튼이 겹친다. */
|
|
65
|
+
selectable?: boolean;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** 목록 껍데기 — 킷 `krds-structured-list`. 목록 이름이 필요하면 `aria-label` 을 넘긴다. */
|
|
69
|
+
export function StructuredList({
|
|
70
|
+
layout = 'grid',
|
|
71
|
+
size = 'medium',
|
|
72
|
+
selectable,
|
|
73
|
+
className,
|
|
74
|
+
children,
|
|
75
|
+
...rest
|
|
76
|
+
}: StructuredListProps) {
|
|
77
|
+
if (process.env.NODE_ENV !== 'production' && layout === 'grid') {
|
|
78
|
+
// `meta` 의 킷 규칙은 전폭 배치 안에만 있어 격자에서는 조용히 맨 목록이 된다.
|
|
79
|
+
// 배치를 아는 것은 목록뿐이라 여기서 자식의 `meta` 를 본다(직접 조립한 것은 못 잡는다).
|
|
80
|
+
const withMeta = Children.toArray(children).some(
|
|
81
|
+
(child) => isValidElement<{ meta?: unknown }>(child) && child.props.meta != null,
|
|
82
|
+
);
|
|
83
|
+
if (withMeta) {
|
|
84
|
+
console.warn(
|
|
85
|
+
'[krds-react StructuredList] layout="grid" 에 meta 를 준 카드가 있다 — 킷의 `.c-txt-ul` 규칙은 ' +
|
|
86
|
+
'`.type-full`·`.type2` 안에만 있어 구분선도 색도 걸리지 않은 맨 목록이 나온다. ' +
|
|
87
|
+
'layout="full" 이나 "compact" 로 두거나 meta 를 빼라.',
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<ul
|
|
94
|
+
className={cx(
|
|
95
|
+
'krds-structured-list',
|
|
96
|
+
LAYOUT_CLASS[layout],
|
|
97
|
+
SIZE_CLASS[size],
|
|
98
|
+
selectable && 'type-check',
|
|
99
|
+
className,
|
|
100
|
+
)}
|
|
101
|
+
{...rest}
|
|
102
|
+
>
|
|
103
|
+
{children}
|
|
104
|
+
</ul>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type StructuredItemProps = ComponentPropsWithRef<'li'> & {
|
|
109
|
+
/** 선택됨. 체크박스의 `checked` 와 **따로** 준다 — 킷 CSS 가 `<li>` 클래스만 본다. */
|
|
110
|
+
selected?: boolean;
|
|
111
|
+
/** 항목 선택 체크박스. `<Checkbox>` 하나를 그대로 넘긴다 — `<CheckArea>` 로 감싸지 마라. */
|
|
112
|
+
check?: ReactNode;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/** 항목 하나 — 킷 `structured-item`. 안쪽 `<div class="in">` 은 부품이 스스로 그린다. */
|
|
116
|
+
export function StructuredItem({
|
|
117
|
+
selected,
|
|
118
|
+
check,
|
|
119
|
+
className,
|
|
120
|
+
children,
|
|
121
|
+
...rest
|
|
122
|
+
}: StructuredItemProps) {
|
|
123
|
+
return (
|
|
124
|
+
<li className={cx('structured-item', selected && 'is-check', className)} {...rest}>
|
|
125
|
+
<div className="in">
|
|
126
|
+
{check != null && <div className="krds-check-area">{check}</div>}
|
|
127
|
+
{children}
|
|
128
|
+
</div>
|
|
129
|
+
</li>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** 항목 머리 — 킷 `card-top`. 상태 배지 자리다. */
|
|
134
|
+
export function StructuredItemTop({ className, children, ...rest }: ComponentPropsWithRef<'div'>) {
|
|
135
|
+
return (
|
|
136
|
+
<div className={cx('card-top', className)} {...rest}>
|
|
137
|
+
{children}
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** 항목 본문 — 킷 `card-body`. 탐색 링크와 CTA 가 들어간다(전폭 배치에서는 좌우로 벌어진다). */
|
|
143
|
+
export function StructuredItemBody({ className, children, ...rest }: ComponentPropsWithRef<'div'>) {
|
|
144
|
+
return (
|
|
145
|
+
<div className={cx('card-body', className)} {...rest}>
|
|
146
|
+
{children}
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** 상세로 가는 탐색 링크 — 킷 `c-text`. 제목·요약·기간을 통째로 감싼다. */
|
|
152
|
+
export type StructuredItemLinkProps = ComponentPropsWithRef<'a'>;
|
|
153
|
+
|
|
154
|
+
export function StructuredItemLink({
|
|
155
|
+
className,
|
|
156
|
+
children,
|
|
157
|
+
target,
|
|
158
|
+
title,
|
|
159
|
+
...rest
|
|
160
|
+
}: StructuredItemLinkProps) {
|
|
161
|
+
return (
|
|
162
|
+
<a
|
|
163
|
+
className={cx('c-text', className)}
|
|
164
|
+
target={target}
|
|
165
|
+
// 새 창으로 열리면 미리 알린다 — KRDS 접근성 요구(「실행 결과를 예측할 수 있어야 한다」).
|
|
166
|
+
title={title ?? (target === '_blank' ? '새 창 열림' : undefined)}
|
|
167
|
+
{...rest}
|
|
168
|
+
>
|
|
169
|
+
{children}
|
|
170
|
+
</a>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 제목 — 킷 `c-tit`. **한 줄로 잘린다.** 안쪽 `<span>` 감싸개는 부품이 그린다.
|
|
176
|
+
* 오른쪽 화살표는 킷이 그리므로 아이콘을 직접 넣지 마라.
|
|
177
|
+
*/
|
|
178
|
+
export function StructuredItemTitle({ className, children, ...rest }: ComponentPropsWithRef<'p'>) {
|
|
179
|
+
return (
|
|
180
|
+
<p className={cx('c-tit', className)} {...rest}>
|
|
181
|
+
<span className="span">{children}</span>
|
|
182
|
+
</p>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** 요약 — 킷 `c-txt`. 격자에서 3줄, 전폭에서 1줄로 잘린다. */
|
|
187
|
+
export function StructuredItemSummary({ className, children, ...rest }: ComponentPropsWithRef<'p'>) {
|
|
188
|
+
return (
|
|
189
|
+
<p className={cx('c-txt', className)} {...rest}>
|
|
190
|
+
{children}
|
|
191
|
+
</p>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export type StructuredItemDateProps = ComponentPropsWithRef<'p'> & {
|
|
196
|
+
/** 항목 이름. 굵게 그려진다. 예: 「신청 기간」 */
|
|
197
|
+
label: ReactNode;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/** 기간·날짜 한 줄 — 킷 `c-date`. 값이 비면 규격대로 대시(`-`)를 넣는다. */
|
|
201
|
+
export function StructuredItemDate({
|
|
202
|
+
label,
|
|
203
|
+
className,
|
|
204
|
+
children,
|
|
205
|
+
...rest
|
|
206
|
+
}: StructuredItemDateProps) {
|
|
207
|
+
const value = children == null || children === '' ? '-' : children;
|
|
208
|
+
return (
|
|
209
|
+
<p className={cx('c-date', className)} {...rest}>
|
|
210
|
+
<strong className="key">{label}</strong>
|
|
211
|
+
<span className="value">{value}</span>
|
|
212
|
+
</p>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export type StructuredItemMetaProps = Omit<ComponentPropsWithRef<'ul'>, 'children'> & {
|
|
217
|
+
/** 항목들. 사이사이에 세로 구분선이 그려진다. 빈 값에는 `-` 를 넣어 자리를 지킨다. */
|
|
218
|
+
items: ReactNode[];
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 부가 정보 줄 — 킷 `c-txt-ul`. 「부서 · 등록일 · 조회수」처럼 짧은 값을 세로선으로 잇는다.
|
|
223
|
+
* **전폭·compact 배치의 `card-body` 안에서만** 킷 규칙이 걸린다.
|
|
224
|
+
*/
|
|
225
|
+
export function StructuredItemMeta({ items, className, ...rest }: StructuredItemMetaProps) {
|
|
226
|
+
return (
|
|
227
|
+
<ul className={cx('c-txt-ul', className)} {...rest}>
|
|
228
|
+
{items.map((item, i) => (
|
|
229
|
+
<li key={i}>{item}</li>
|
|
230
|
+
))}
|
|
231
|
+
</ul>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* CTA 자리 — 킷 `c-btn`. 항목과 관련된 **가장 중요한 행동 하나**만 넣는다(가이드 규정).
|
|
237
|
+
* 어느 항목의 버튼인지 알도록 `title` 에 항목 제목을 준다.
|
|
238
|
+
*/
|
|
239
|
+
export function StructuredItemCta({ className, children, ...rest }: ComponentPropsWithRef<'div'>) {
|
|
240
|
+
return (
|
|
241
|
+
<div className={cx('c-btn', className)} {...rest}>
|
|
242
|
+
{children}
|
|
243
|
+
</div>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** 태그 줄 — 킷 `card-btm`. 위쪽 구분선은 킷이 그린다. */
|
|
248
|
+
export function StructuredItemTags({ className, children, ...rest }: ComponentPropsWithRef<'div'>) {
|
|
249
|
+
return (
|
|
250
|
+
<div className={cx('card-btm', className)} {...rest}>
|
|
251
|
+
{children}
|
|
252
|
+
</div>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** 태그 하나 — 킷 `tag`. **글자 앞의 `#` 는 킷이 붙인다** — 직접 쓰면 `##` 이 된다. */
|
|
257
|
+
export function StructuredItemTag({ className, children, ...rest }: ComponentPropsWithRef<'span'>) {
|
|
258
|
+
return (
|
|
259
|
+
<span className={cx('tag', className)} {...rest}>
|
|
260
|
+
{children}
|
|
261
|
+
</span>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* 액션 버튼 묶음 — 킷 `card-btn`. 항목 우상단에 놓이는 보조 기능(공유·찜하기) 자리다.
|
|
267
|
+
* 안에는 `krds-btn medium text` 를 쓴다(킷이 높이를 고정한다).
|
|
268
|
+
*/
|
|
269
|
+
export function StructuredItemActions({
|
|
270
|
+
className,
|
|
271
|
+
children,
|
|
272
|
+
...rest
|
|
273
|
+
}: ComponentPropsWithRef<'div'>) {
|
|
274
|
+
return (
|
|
275
|
+
<div className={cx('card-btn', className)} {...rest}>
|
|
276
|
+
{children}
|
|
277
|
+
</div>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export type StructuredCardProps = Omit<StructuredItemProps, 'title'> & {
|
|
282
|
+
/** 제목. 한 줄로 잘린다. */
|
|
283
|
+
title: ReactNode;
|
|
284
|
+
/** 상세 주소. 제목·요약·기간 전체가 이 링크가 된다. */
|
|
285
|
+
href: string;
|
|
286
|
+
/** `_blank` 면 「새 창 열림」을 `title` 로 알린다. */
|
|
287
|
+
target?: string;
|
|
288
|
+
/** 요약. 격자 3줄 · 전폭 1줄로 잘린다. */
|
|
289
|
+
summary?: ReactNode;
|
|
290
|
+
/** 기간 줄의 이름. 예: 「신청 기간」 */
|
|
291
|
+
dateLabel?: ReactNode;
|
|
292
|
+
/** 기간 줄의 값. 비면 `-` 가 들어간다. */
|
|
293
|
+
dateValue?: ReactNode;
|
|
294
|
+
/** 부가 정보(세로선 구분). **전폭·compact 목록에서만** 킷 규칙이 걸린다. */
|
|
295
|
+
meta?: ReactNode[];
|
|
296
|
+
/** 상태 배지. `<Badge>` 를 넣는다. */
|
|
297
|
+
badge?: ReactNode;
|
|
298
|
+
/** 태그. `#` 은 킷이 붙이므로 글자만 넣는다. */
|
|
299
|
+
tags?: ReactNode[];
|
|
300
|
+
/** 가장 중요한 행동 하나. `title` 에 항목 제목을 주는 것을 잊지 마라. */
|
|
301
|
+
cta?: ReactNode;
|
|
302
|
+
/** 우상단 보조 기능(공유·찜하기 등). */
|
|
303
|
+
actions?: ReactNode;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 카드 한 장을 통째로 — 조각들을 킷 샘플 순서대로 조립한다.
|
|
308
|
+
* 순서를 바꾸거나 킷에 없는 것을 끼워야 할 때만 조각을 직접 쓴다.
|
|
309
|
+
*/
|
|
310
|
+
export function StructuredCard({
|
|
311
|
+
title,
|
|
312
|
+
href,
|
|
313
|
+
target,
|
|
314
|
+
summary,
|
|
315
|
+
dateLabel,
|
|
316
|
+
dateValue,
|
|
317
|
+
meta,
|
|
318
|
+
badge,
|
|
319
|
+
tags,
|
|
320
|
+
cta,
|
|
321
|
+
actions,
|
|
322
|
+
children,
|
|
323
|
+
...rest
|
|
324
|
+
}: StructuredCardProps) {
|
|
325
|
+
return (
|
|
326
|
+
<StructuredItem {...rest}>
|
|
327
|
+
{badge != null && <StructuredItemTop>{badge}</StructuredItemTop>}
|
|
328
|
+
<StructuredItemBody>
|
|
329
|
+
<StructuredItemLink href={href} target={target}>
|
|
330
|
+
<StructuredItemTitle>{title}</StructuredItemTitle>
|
|
331
|
+
{summary != null && <StructuredItemSummary>{summary}</StructuredItemSummary>}
|
|
332
|
+
{meta != null && meta.length > 0 && <StructuredItemMeta items={meta} />}
|
|
333
|
+
{dateLabel != null && (
|
|
334
|
+
<StructuredItemDate label={dateLabel}>{dateValue}</StructuredItemDate>
|
|
335
|
+
)}
|
|
336
|
+
</StructuredItemLink>
|
|
337
|
+
{cta != null && <StructuredItemCta>{cta}</StructuredItemCta>}
|
|
338
|
+
</StructuredItemBody>
|
|
339
|
+
{tags != null && tags.length > 0 && (
|
|
340
|
+
<StructuredItemTags>
|
|
341
|
+
{tags.map((tag, i) => (
|
|
342
|
+
<StructuredItemTag key={i}>{tag}</StructuredItemTag>
|
|
343
|
+
))}
|
|
344
|
+
</StructuredItemTags>
|
|
345
|
+
)}
|
|
346
|
+
{actions != null && <StructuredItemActions>{actions}</StructuredItemActions>}
|
|
347
|
+
{children}
|
|
348
|
+
</StructuredItem>
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* 자료 없음 — 킷에 빈 목록 규칙이 없어 보충했다(표의 `TableEmpty` 와 같은 모양).
|
|
354
|
+
* 목록이 비면 `StructuredList` 를 그리지 말고 이것을 그린다.
|
|
355
|
+
* 대안 행동까지 곁들이려면 `ui/empty-state` 의 `EmptyState` 를 쓴다.
|
|
356
|
+
*/
|
|
357
|
+
export function StructuredListEmpty({
|
|
358
|
+
className,
|
|
359
|
+
children = '등록된 자료가 없습니다.',
|
|
360
|
+
...rest
|
|
361
|
+
}: ComponentPropsWithRef<'p'>) {
|
|
362
|
+
return (
|
|
363
|
+
<p className={cx('py-14 text-center text-fg-subtle', className)} {...rest}>
|
|
364
|
+
{children}
|
|
365
|
+
</p>
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export type StructuredListToolbarProps = ComponentPropsWithRef<'div'> & {
|
|
370
|
+
/** 왼쪽 — 결과 건수·전체 선택 등. `aria-live="polite"` 로 감싸 바뀐 건수를 읽어 준다. */
|
|
371
|
+
info?: ReactNode;
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* 목록 위 도구 막대 — 킷에 클래스가 없어 유틸리티로 그린 보충 부품이다.
|
|
376
|
+
*
|
|
377
|
+
* 왼쪽에 결과 건수, 오른쪽에 정렬·개수 도구를 둔다. 규격: **모든 화면에서 같은 자리에 같은 순서로.**
|
|
378
|
+
* 정렬을 바꿀 때는 목록만 갈아 끼운다 — 초점을 옮기지 마라.
|
|
379
|
+
*/
|
|
380
|
+
export function StructuredListToolbar({
|
|
381
|
+
info,
|
|
382
|
+
className,
|
|
383
|
+
children,
|
|
384
|
+
...rest
|
|
385
|
+
}: StructuredListToolbarProps) {
|
|
386
|
+
return (
|
|
387
|
+
<div
|
|
388
|
+
className={cx('flex flex-wrap items-center justify-between gap-3 pb-5', className)}
|
|
389
|
+
{...rest}
|
|
390
|
+
>
|
|
391
|
+
{info != null ? <div aria-live="polite">{info}</div> : <div />}
|
|
392
|
+
<div className="flex flex-wrap items-center gap-3">{children}</div>
|
|
393
|
+
</div>
|
|
394
|
+
);
|
|
395
|
+
}
|