cashdoc-cms-design-system 1.0.1 → 1.0.3
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/README.md +124 -0
- package/dist/components/Pagination/Pagination.d.ts +106 -0
- package/dist/components/Pagination/index.d.ts +2 -0
- package/dist/components/Table/Table.d.ts +151 -0
- package/dist/components/Table/index.d.ts +2 -0
- package/dist/components/TimePicker/TimePicker.d.ts +110 -0
- package/dist/components/TimePicker/index.d.ts +2 -0
- package/dist/components/ToolTip/ToolTip.d.ts +110 -0
- package/dist/components/ToolTip/index.d.ts +2 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/index.es.js +5193 -4165
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +47 -22
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1 +1,125 @@
|
|
|
1
|
+
# Cashdoc CMS Design System
|
|
1
2
|
|
|
3
|
+
Cashdoc CMS용 디자인 시스템 컴포넌트 라이브러리
|
|
4
|
+
|
|
5
|
+
## 문서
|
|
6
|
+
Storybook과 JSDoc, 둘 중 더 편한 쪽을 보면 됩니다.
|
|
7
|
+
### Storybook
|
|
8
|
+
[Storybook](https://cashdoc-cms-design-system.vercel.app/?path=/docs/components-button--docs)
|
|
9
|
+
|
|
10
|
+
### JSDoc(TSDoc)
|
|
11
|
+
<img width="1038" height="363" alt="스크린샷 2025-12-24 오후 3 48 42" src="https://github.com/user-attachments/assets/1178fa60-30cb-436b-944c-495417179ac3" />
|
|
12
|
+
|
|
13
|
+
<br />
|
|
14
|
+
|
|
15
|
+
## 기술 스택
|
|
16
|
+
|
|
17
|
+
- **React 18** - UI 라이브러리
|
|
18
|
+
- **TypeScript** - 타입 안전성
|
|
19
|
+
- **TailwindCSS** - 스타일링
|
|
20
|
+
- **Radix UI** - 접근성 프리미티브
|
|
21
|
+
- **Framer Motion** - 애니메이션
|
|
22
|
+
- **Vite** - 빌드 도구
|
|
23
|
+
- **Storybook** - 컴포넌트 문서화
|
|
24
|
+
|
|
25
|
+
<br />
|
|
26
|
+
|
|
27
|
+
## 설치
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm install cashdoc-cms-design-system
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
<br />
|
|
34
|
+
|
|
35
|
+
## 사용법
|
|
36
|
+
|
|
37
|
+
프로젝트 최상단에서 cashdoc-cms-design-system의 `style.css` 파일을 import 후 사용하면 됩니다.
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import "cashdoc-cms-design-system/dist/style.css";
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { Button, Modal, DatePicker } from "cashdoc-cms-design-system";
|
|
45
|
+
|
|
46
|
+
function App() {
|
|
47
|
+
return <Button variant="default">버튼</Button>;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
<br />
|
|
52
|
+
|
|
53
|
+
## 컴포넌트 리스트
|
|
54
|
+
|
|
55
|
+
### Form
|
|
56
|
+
|
|
57
|
+
- `Button` - 기본 버튼
|
|
58
|
+
- `Checkbox` - 체크박스
|
|
59
|
+
- `RadioButton` - 라디오 버튼
|
|
60
|
+
- `Switch` - 토글 스위치
|
|
61
|
+
- `TextInput` - 텍스트 입력 필드
|
|
62
|
+
- `TagInput` - 태그 입력 필드
|
|
63
|
+
|
|
64
|
+
### Data Input
|
|
65
|
+
|
|
66
|
+
- `DatePicker` - 날짜 선택
|
|
67
|
+
- `DateRangePicker` - 기간 선택
|
|
68
|
+
- `Dropdown` - 드롭다운 메뉴
|
|
69
|
+
- `Select` - 선택 입력
|
|
70
|
+
- `Combobox` - 검색 가능한 선택
|
|
71
|
+
|
|
72
|
+
### Feedback
|
|
73
|
+
|
|
74
|
+
- `Modal` - 모달 다이얼로그
|
|
75
|
+
- `ConfirmModal` - 확인 모달
|
|
76
|
+
- `DeleteModal` - 삭제 확인 모달
|
|
77
|
+
- `ErrorModal` - 에러 알림
|
|
78
|
+
- `SuccessModal` - 성공 알림
|
|
79
|
+
- `WarningModal` - 경고 알림
|
|
80
|
+
- `Toast` - 토스트 알림
|
|
81
|
+
- `LoadingCircle` - 로딩 인디케이터
|
|
82
|
+
|
|
83
|
+
### Navigation
|
|
84
|
+
|
|
85
|
+
- `SideNavigation` - 사이드바 네비게이션
|
|
86
|
+
- `Popover` - 팝오버 메뉴
|
|
87
|
+
|
|
88
|
+
### Display
|
|
89
|
+
|
|
90
|
+
- `Text` - 타이포그래피
|
|
91
|
+
- `Icons` - 아이콘 세트
|
|
92
|
+
|
|
93
|
+
#
|
|
94
|
+
<br />
|
|
95
|
+
# 개발
|
|
96
|
+
|
|
97
|
+
### 로컬 개발 서버
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pnpm dev
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Storybook 실행
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pnpm storybook
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 빌드
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pnpm build
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 타입 체크
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pnpm type-check
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 린트
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pnpm lint
|
|
125
|
+
```
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface PaginationProps {
|
|
4
|
+
currentPage: number;
|
|
5
|
+
totalPages: number;
|
|
6
|
+
onPageChange: (page: number) => void;
|
|
7
|
+
siblingCount?: number;
|
|
8
|
+
showPrevNext?: boolean;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 사용자가 여러 페이지로 나뉜 콘텐츠를 탐색할 수 있게 하는 페이지네이션 컴포넌트입니다.
|
|
14
|
+
*
|
|
15
|
+
* {@link Pagination}은 이전/다음 버튼과 페이지 번호를 제공하며,
|
|
16
|
+
* 많은 페이지가 있을 때 중간 페이지를 생략(ellipsis)하여 UI를 깔끔하게 유지합니다.
|
|
17
|
+
*
|
|
18
|
+
* ## When (언제 사용해야 하는가)
|
|
19
|
+
*
|
|
20
|
+
* **사용해야 하는 경우:**
|
|
21
|
+
* - **긴 목록**: 많은 항목을 여러 페이지로 나누어 표시할 때
|
|
22
|
+
* - **검색 결과**: 검색 결과가 여러 페이지에 걸쳐 있을 때
|
|
23
|
+
* - **데이터 테이블**: 대량의 데이터를 페이지 단위로 보여줄 때
|
|
24
|
+
* - **명확한 페이지 구분**: 사용자가 특정 페이지로 직접 이동할 필요가 있을 때
|
|
25
|
+
*
|
|
26
|
+
* **사용하지 말아야 하는 경우:**
|
|
27
|
+
* - **무한 스크롤**: 지속적으로 콘텐츠가 로드되는 피드 형태가 더 적절한 경우
|
|
28
|
+
* - **적은 항목**: 한 페이지에 모두 표시할 수 있는 소량의 데이터
|
|
29
|
+
* - **단계별 프로세스**: 순차적인 단계를 나타낼 때는 Stepper 컴포넌트 사용
|
|
30
|
+
*
|
|
31
|
+
* ## Layout behavior
|
|
32
|
+
*
|
|
33
|
+
* - **Responsive**: 모바일에서는 siblingCount를 줄여 더 적은 페이지 번호를 표시하는 것을 권장
|
|
34
|
+
* - **Centered**: 일반적으로 페이지 하단 중앙에 배치
|
|
35
|
+
* - **Ellipsis**: 많은 페이지가 있을 때 자동으로 중간 페이지를 생략 (...으로 표시)
|
|
36
|
+
*
|
|
37
|
+
* ## Usage guidelines
|
|
38
|
+
*
|
|
39
|
+
* ### ✅ Do (권장 사항)
|
|
40
|
+
*
|
|
41
|
+
* - **적절한 페이지당 항목 수**: 10-50개 사이가 적절 (콘텐츠 유형에 따라 조정)
|
|
42
|
+
* - **현재 페이지 명확히 표시**: 활성 페이지는 시각적으로 구분되어야 함
|
|
43
|
+
* - **충분한 클릭 영역**: 모바일에서도 쉽게 탭할 수 있도록 버튼 크기 유지
|
|
44
|
+
* - **첫/마지막 페이지 비활성화**: 이동할 수 없는 경우 버튼을 비활성화하여 명확히 표시
|
|
45
|
+
*
|
|
46
|
+
* ### 🚫 Don't (주의/금지 사항)
|
|
47
|
+
*
|
|
48
|
+
* - **너무 많은 페이지 번호**: siblingCount를 너무 크게 설정하지 마세요
|
|
49
|
+
* - **불명확한 상태**: 로딩 중이거나 에러 상태일 때도 적절한 피드백 제공 필요
|
|
50
|
+
*
|
|
51
|
+
* ## Accessibility
|
|
52
|
+
*
|
|
53
|
+
* - **ARIA Labels**: 각 버튼에 명확한 레이블 제공 (aria-label, aria-current)
|
|
54
|
+
* - **Keyboard Navigation**: Tab 키로 버튼 간 이동, Enter/Space로 페이지 변경
|
|
55
|
+
* - **Screen Reader**: 현재 페이지와 전체 페이지 수를 읽어주도록 설정
|
|
56
|
+
* - **Disabled State**: 비활성화된 버튼은 포커스할 수 없도록 처리
|
|
57
|
+
*
|
|
58
|
+
* ## Example
|
|
59
|
+
*
|
|
60
|
+
* {@tool snippet}
|
|
61
|
+
* 기본적인 페이지네이션 사용:
|
|
62
|
+
*
|
|
63
|
+
* ```tsx
|
|
64
|
+
* const [currentPage, setCurrentPage] = useState(1);
|
|
65
|
+
* const totalPages = 10;
|
|
66
|
+
*
|
|
67
|
+
* <Pagination
|
|
68
|
+
* currentPage={currentPage}
|
|
69
|
+
* totalPages={totalPages}
|
|
70
|
+
* onPageChange={setCurrentPage}
|
|
71
|
+
* />
|
|
72
|
+
* ```
|
|
73
|
+
* {@end-tool}
|
|
74
|
+
*
|
|
75
|
+
* {@tool snippet}
|
|
76
|
+
* 많은 페이지와 커스텀 siblingCount:
|
|
77
|
+
*
|
|
78
|
+
* ```tsx
|
|
79
|
+
* <Pagination
|
|
80
|
+
* currentPage={25}
|
|
81
|
+
* totalPages={100}
|
|
82
|
+
* siblingCount={2}
|
|
83
|
+
* onPageChange={handlePageChange}
|
|
84
|
+
* />
|
|
85
|
+
* // 결과: 1 ... 23 24 25 26 27 ... 100
|
|
86
|
+
* ```
|
|
87
|
+
* {@end-tool}
|
|
88
|
+
*
|
|
89
|
+
* {@tool snippet}
|
|
90
|
+
* 비활성화 상태:
|
|
91
|
+
*
|
|
92
|
+
* ```tsx
|
|
93
|
+
* <Pagination
|
|
94
|
+
* currentPage={5}
|
|
95
|
+
* totalPages={10}
|
|
96
|
+
* onPageChange={handlePageChange}
|
|
97
|
+
* disabled={isLoading}
|
|
98
|
+
* />
|
|
99
|
+
* ```
|
|
100
|
+
* {@end-tool}
|
|
101
|
+
*
|
|
102
|
+
* See also:
|
|
103
|
+
*
|
|
104
|
+
* - {@link Button}, 단일 버튼 컴포넌트
|
|
105
|
+
*/
|
|
106
|
+
export declare const Pagination: React.ForwardRefExoticComponent<PaginationProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { VariantProps } from 'class-variance-authority';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 데이터를 행과 열로 구조화하여 표시하는 테이블 컴포넌트입니다.
|
|
6
|
+
*
|
|
7
|
+
* {@link Table}은 Compound Component 패턴을 사용하여 구성되며,
|
|
8
|
+
* TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption
|
|
9
|
+
* 등의 하위 컴포넌트를 조합하여 유연한 테이블 레이아웃을 만들 수 있습니다.
|
|
10
|
+
*
|
|
11
|
+
* ## When (언제 사용해야 하는가)
|
|
12
|
+
*
|
|
13
|
+
* **사용해야 하는 경우:**
|
|
14
|
+
* - **데이터 목록**: 사용자 목록, 주문 내역, 제품 리스트 등 구조화된 데이터를 표시할 때
|
|
15
|
+
* - **비교**: 여러 항목의 속성을 나란히 비교해야 할 때
|
|
16
|
+
* - **정렬 및 필터**: 데이터를 정렬하거나 필터링해서 보여줄 때
|
|
17
|
+
* - **대량 데이터**: 많은 양의 정보를 조직적으로 표시해야 할 때
|
|
18
|
+
*
|
|
19
|
+
* **사용하지 말아야 하는 경우:**
|
|
20
|
+
* - **레이아웃 목적**: 단순히 레이아웃을 만들기 위해서는 CSS Grid나 Flexbox를 사용하세요.
|
|
21
|
+
* - **소량 데이터**: 2-3개의 간단한 정보는 리스트나 카드 형태가 더 적합할 수 있습니다.
|
|
22
|
+
* - **복잡한 인터랙션**: 각 행마다 많은 버튼이나 입력이 필요하다면 카드 레이아웃을 고려하세요.
|
|
23
|
+
*
|
|
24
|
+
* ## Layout behavior
|
|
25
|
+
*
|
|
26
|
+
* - **Responsive**: 기본적으로 스크롤 가능한 컨테이너로 래핑되어 모바일에서도 사용 가능합니다.
|
|
27
|
+
* - **Full Width**: 부모 컨테이너의 전체 너비를 차지합니다.
|
|
28
|
+
* - **Auto Height**: 내용에 따라 높이가 자동으로 조정됩니다.
|
|
29
|
+
*
|
|
30
|
+
* ## Usage guidelines
|
|
31
|
+
*
|
|
32
|
+
* ### ✅ Do (권장 사항)
|
|
33
|
+
*
|
|
34
|
+
* - **명확한 헤더**: TableHead를 사용하여 각 열의 의미를 명확히 표시하세요.
|
|
35
|
+
* - **일관된 정렬**: 숫자는 오른쪽, 텍스트는 왼쪽 정렬하는 것이 일반적입니다.
|
|
36
|
+
* - **적절한 variant**: 데이터가 많을 때는 `striped`나 `hoverable`을 사용하여 가독성을 높이세요.
|
|
37
|
+
* - **Caption 활용**: 테이블의 목적을 설명하는 caption을 추가하면 접근성이 향상됩니다.
|
|
38
|
+
*
|
|
39
|
+
* ### 🚫 Don't (주의/금지 사항)
|
|
40
|
+
*
|
|
41
|
+
* - **과도한 열**: 너무 많은 열은 가독성을 해칩니다. 중요한 정보만 표시하세요.
|
|
42
|
+
* - **중첩 테이블**: 테이블 안에 테이블을 넣지 마세요. 복잡도가 급격히 증가합니다.
|
|
43
|
+
* - **빈 셀 남용**: 빈 셀이 많으면 데이터 구조를 다시 검토하세요.
|
|
44
|
+
*
|
|
45
|
+
* ## Accessibility
|
|
46
|
+
*
|
|
47
|
+
* - **Semantic HTML**: 적절한 `<table>`, `<thead>`, `<tbody>`, `<th>`, `<td>` 태그를 사용합니다.
|
|
48
|
+
* - **Scope Attribute**: TableHead는 `scope="col"`을 자동으로 가집니다.
|
|
49
|
+
* - **ARIA Attributes**: 정렬, 선택 등의 상태는 `aria-sort`, `aria-selected`로 표현됩니다.
|
|
50
|
+
* - **Caption**: 테이블의 목적을 설명하는 `<caption>`을 사용할 수 있습니다.
|
|
51
|
+
*
|
|
52
|
+
* ## Example
|
|
53
|
+
*
|
|
54
|
+
* {@tool snippet}
|
|
55
|
+
* 기본 테이블 사용:
|
|
56
|
+
*
|
|
57
|
+
* ```tsx
|
|
58
|
+
* <Table>
|
|
59
|
+
* <TableCaption>사용자 목록</TableCaption>
|
|
60
|
+
* <TableHeader>
|
|
61
|
+
* <TableRow>
|
|
62
|
+
* <TableHead>이름</TableHead>
|
|
63
|
+
* <TableHead>이메일</TableHead>
|
|
64
|
+
* <TableHead align="right">역할</TableHead>
|
|
65
|
+
* </TableRow>
|
|
66
|
+
* </TableHeader>
|
|
67
|
+
* <TableBody>
|
|
68
|
+
* <TableRow>
|
|
69
|
+
* <TableCell>홍길동</TableCell>
|
|
70
|
+
* <TableCell>hong@example.com</TableCell>
|
|
71
|
+
* <TableCell align="right">관리자</TableCell>
|
|
72
|
+
* </TableRow>
|
|
73
|
+
* </TableBody>
|
|
74
|
+
* </Table>
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* Striped 테이블:
|
|
78
|
+
*
|
|
79
|
+
* ```tsx
|
|
80
|
+
* <Table striped hoverable>
|
|
81
|
+
* <TableHeader>
|
|
82
|
+
* <TableRow>
|
|
83
|
+
* <TableHead>제품명</TableHead>
|
|
84
|
+
* <TableHead align="right">가격</TableHead>
|
|
85
|
+
* </TableRow>
|
|
86
|
+
* </TableHeader>
|
|
87
|
+
* <TableBody>
|
|
88
|
+
* {products.map((product) => (
|
|
89
|
+
* <TableRow key={product.id}>
|
|
90
|
+
* <TableCell>{product.name}</TableCell>
|
|
91
|
+
* <TableCell align="right">{product.price}</TableCell>
|
|
92
|
+
* </TableRow>
|
|
93
|
+
* ))}
|
|
94
|
+
* </TableBody>
|
|
95
|
+
* </Table>
|
|
96
|
+
* ```
|
|
97
|
+
* {@end-tool}
|
|
98
|
+
*/
|
|
99
|
+
declare const tableVariants: (props?: ({
|
|
100
|
+
bordered?: boolean | null | undefined;
|
|
101
|
+
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
102
|
+
export interface TableProps extends React.TableHTMLAttributes<HTMLTableElement>, VariantProps<typeof tableVariants> {
|
|
103
|
+
/** zebra stripe 패턴 적용 여부 */
|
|
104
|
+
striped?: boolean;
|
|
105
|
+
/** row hover 효과 적용 여부 */
|
|
106
|
+
hoverable?: boolean;
|
|
107
|
+
/** 테두리 표시 여부 */
|
|
108
|
+
bordered?: boolean;
|
|
109
|
+
/** 좁은 padding 적용 여부 */
|
|
110
|
+
compact?: boolean;
|
|
111
|
+
}
|
|
112
|
+
export declare const Table: React.ForwardRefExoticComponent<TableProps & React.RefAttributes<HTMLTableElement>>;
|
|
113
|
+
export interface TableHeaderProps extends React.HTMLAttributes<HTMLTableSectionElement> {
|
|
114
|
+
}
|
|
115
|
+
export declare const TableHeader: React.ForwardRefExoticComponent<TableHeaderProps & React.RefAttributes<HTMLTableSectionElement>>;
|
|
116
|
+
export interface TableBodyProps extends React.HTMLAttributes<HTMLTableSectionElement> {
|
|
117
|
+
}
|
|
118
|
+
export declare const TableBody: React.ForwardRefExoticComponent<TableBodyProps & React.RefAttributes<HTMLTableSectionElement>>;
|
|
119
|
+
export interface TableFooterProps extends React.HTMLAttributes<HTMLTableSectionElement> {
|
|
120
|
+
}
|
|
121
|
+
export declare const TableFooter: React.ForwardRefExoticComponent<TableFooterProps & React.RefAttributes<HTMLTableSectionElement>>;
|
|
122
|
+
declare const tableRowVariants: (props?: ({
|
|
123
|
+
hoverable?: boolean | null | undefined;
|
|
124
|
+
selected?: boolean | null | undefined;
|
|
125
|
+
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
126
|
+
export interface TableRowProps extends React.HTMLAttributes<HTMLTableRowElement>, VariantProps<typeof tableRowVariants> {
|
|
127
|
+
/** 선택된 행 표시 여부 */
|
|
128
|
+
selected?: boolean;
|
|
129
|
+
}
|
|
130
|
+
export declare const TableRow: React.ForwardRefExoticComponent<TableRowProps & React.RefAttributes<HTMLTableRowElement>>;
|
|
131
|
+
export interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
|
|
132
|
+
/** 정렬 가능 여부 */
|
|
133
|
+
sortable?: boolean;
|
|
134
|
+
/** 정렬 방향 */
|
|
135
|
+
sortDirection?: "asc" | "desc" | null;
|
|
136
|
+
/** 정렬 클릭 핸들러 */
|
|
137
|
+
onSort?: () => void;
|
|
138
|
+
}
|
|
139
|
+
export declare const TableHead: React.ForwardRefExoticComponent<TableHeadProps & React.RefAttributes<HTMLTableCellElement>>;
|
|
140
|
+
declare const tableCellVariants: (props?: ({
|
|
141
|
+
align?: "center" | "right" | "left" | null | undefined;
|
|
142
|
+
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
143
|
+
export interface TableCellProps extends React.TdHTMLAttributes<HTMLTableCellElement>, VariantProps<typeof tableCellVariants> {
|
|
144
|
+
/** 텍스트 정렬 방식 */
|
|
145
|
+
align?: "left" | "center" | "right";
|
|
146
|
+
}
|
|
147
|
+
export declare const TableCell: React.ForwardRefExoticComponent<TableCellProps & React.RefAttributes<HTMLTableCellElement>>;
|
|
148
|
+
export interface TableCaptionProps extends React.HTMLAttributes<HTMLTableCaptionElement> {
|
|
149
|
+
}
|
|
150
|
+
export declare const TableCaption: React.ForwardRefExoticComponent<TableCaptionProps & React.RefAttributes<HTMLTableCaptionElement>>;
|
|
151
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption, } from './Table';
|
|
2
|
+
export type { TableProps, TableHeaderProps, TableBodyProps, TableFooterProps, TableRowProps, TableHeadProps, TableCellProps, TableCaptionProps, } from './Table';
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface TimePickerProps {
|
|
4
|
+
value?: string;
|
|
5
|
+
onChange?: (time: string) => void;
|
|
6
|
+
label?: string;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
format?: "24h" | "12h";
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
error?: boolean;
|
|
11
|
+
errorMessage?: string;
|
|
12
|
+
helperText?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
minuteStep?: number;
|
|
15
|
+
showIcon?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 사용자가 시간을 선택할 수 있게 하는 컴포넌트입니다.
|
|
19
|
+
*
|
|
20
|
+
* {@link TimePicker}는 직관적인 스크롤 인터페이스를 통해 시간과 분을 선택할 수 있으며,
|
|
21
|
+
* 24시간 형식과 12시간(AM/PM) 형식을 모두 지원합니다.
|
|
22
|
+
*
|
|
23
|
+
* `@radix-ui/react-popover`를 기반으로 구현되었으며, 사용자가 잘못된 시간을 입력하는 것을 방지합니다.
|
|
24
|
+
*
|
|
25
|
+
* ## When (언제 사용해야 하는가)
|
|
26
|
+
*
|
|
27
|
+
* **사용해야 하는 경우:**
|
|
28
|
+
* - **특정 시간 입력**: 예약 시간, 알림 시간, 업무 시작/종료 시간 등을 선택해야 할 때
|
|
29
|
+
* - **정확한 시간 선택**: 사용자가 직접 타이핑하여 발생할 수 있는 형식 오류를 방지하고 싶을 때
|
|
30
|
+
* - **제한된 간격 선택**: 5분, 10분, 15분 단위로만 선택이 필요한 경우 (minuteStep 사용)
|
|
31
|
+
*
|
|
32
|
+
* **사용하지 말아야 하는 경우:**
|
|
33
|
+
* - **대략적인 시간**: '오전', '오후', '저녁' 등 상대적인 시간 선택이 필요한 경우 `Dropdown`이나 `RadioButton`이 더 적절할 수 있습니다.
|
|
34
|
+
* - **초 단위 정밀도**: 현재는 시간과 분까지만 지원하므로, 초 단위가 필요한 경우 다른 컴포넌트를 고려하세요.
|
|
35
|
+
*
|
|
36
|
+
* ## Layout behavior
|
|
37
|
+
*
|
|
38
|
+
* - **Popover 기반**: 클릭 시 입력창 아래에 시간 선택 팝오버가 나타나며, 화면 공간을 효율적으로 사용합니다.
|
|
39
|
+
* - **스크롤 선택**: 시간과 분을 각각 스크롤하여 선택할 수 있어 직관적입니다.
|
|
40
|
+
* - **Full Width**: `className`을 통해 부모 요소의 너비에 맞게 조절할 수 있습니다.
|
|
41
|
+
*
|
|
42
|
+
* ## Usage guidelines
|
|
43
|
+
*
|
|
44
|
+
* ### ✅ Do (권장 사항)
|
|
45
|
+
*
|
|
46
|
+
* - **적절한 Format**: 사용자가 익숙한 형식(한국: 24시간, 미국: 12시간)을 선택하세요.
|
|
47
|
+
* - **Minute Step 활용**: 일정 간격으로만 선택이 필요한 경우 `minuteStep`을 활용하여 UX를 개선하세요.
|
|
48
|
+
* - **레이블 및 헬퍼 텍스트**: 입력 항목의 용도를 명확히 하고, 필요한 경우 보충 설명을 제공하세요.
|
|
49
|
+
*
|
|
50
|
+
* ### 🚫 Don't (주의/금지 사항)
|
|
51
|
+
*
|
|
52
|
+
* - **Read-only 입력**: 사용자가 직접 타이핑하여 입력하는 기능은 지원하지 않으므로, 반드시 UI를 통해 선택하도록 안내하세요.
|
|
53
|
+
* - **과도한 Step**: `minuteStep`이 너무 크면 정확한 시간을 선택하기 어려울 수 있습니다.
|
|
54
|
+
*
|
|
55
|
+
* ## Accessibility
|
|
56
|
+
*
|
|
57
|
+
* - **Keyboard Navigation**: 팝오버는 키보드로 열고 닫을 수 있으며, `Esc` 키로 취소할 수 있습니다.
|
|
58
|
+
* - **Focus Management**: 시간 선택기가 열려 있는 동안 포커스가 적절히 관리됩니다.
|
|
59
|
+
* - **Aria Labels**: 각 버튼에는 스크린 리더를 위한 적절한 레이블이 포함되어 있습니다.
|
|
60
|
+
*
|
|
61
|
+
* ## Example
|
|
62
|
+
*
|
|
63
|
+
* {@tool snippet}
|
|
64
|
+
* 기본적인 시간 선택 예시 (24시간 형식):
|
|
65
|
+
*
|
|
66
|
+
* ```tsx
|
|
67
|
+
* const [time, setTime] = useState("");
|
|
68
|
+
*
|
|
69
|
+
* <TimePicker
|
|
70
|
+
* label="출근 시간"
|
|
71
|
+
* value={time}
|
|
72
|
+
* onChange={setTime}
|
|
73
|
+
* placeholder="시간을 선택하세요"
|
|
74
|
+
* />
|
|
75
|
+
* ```
|
|
76
|
+
* {@end-tool}
|
|
77
|
+
*
|
|
78
|
+
* {@tool snippet}
|
|
79
|
+
* 12시간 형식 및 15분 단위 선택:
|
|
80
|
+
*
|
|
81
|
+
* ```tsx
|
|
82
|
+
* <TimePicker
|
|
83
|
+
* label="회의 시간"
|
|
84
|
+
* format="12h"
|
|
85
|
+
* minuteStep={15}
|
|
86
|
+
* value="2:30 PM"
|
|
87
|
+
* onChange={setTime}
|
|
88
|
+
* />
|
|
89
|
+
* ```
|
|
90
|
+
* {@end-tool}
|
|
91
|
+
*
|
|
92
|
+
* {@tool snippet}
|
|
93
|
+
* 에러 상태:
|
|
94
|
+
*
|
|
95
|
+
* ```tsx
|
|
96
|
+
* <TimePicker
|
|
97
|
+
* label="마감 시간"
|
|
98
|
+
* error={true}
|
|
99
|
+
* errorMessage="업무 시간(9:00-18:00) 내에서 선택해주세요"
|
|
100
|
+
* />
|
|
101
|
+
* ```
|
|
102
|
+
* {@end-tool}
|
|
103
|
+
*
|
|
104
|
+
* See also:
|
|
105
|
+
*
|
|
106
|
+
* - {@link DatePicker}, 날짜를 선택해야 하는 경우
|
|
107
|
+
* - {@link DateRangePicker}, 날짜 범위를 선택해야 하는 경우
|
|
108
|
+
* - {@link TextInput}, 단순한 텍스트 입력이 필요한 경우
|
|
109
|
+
*/
|
|
110
|
+
export declare const TimePicker: React.ForwardRefExoticComponent<TimePickerProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
3
|
+
/**
|
|
4
|
+
* 마우스 호버나 포커스 시 간단한 힌트나 설명을 제공하는 툴팁 컴포넌트입니다.
|
|
5
|
+
*
|
|
6
|
+
* {@link ToolTip}은 UI 요소에 대한 추가 정보를 간결하게 제공하여
|
|
7
|
+
* 사용자가 인터페이스를 더 쉽게 이해할 수 있도록 돕습니다.
|
|
8
|
+
*
|
|
9
|
+
* Radix UI의 Tooltip 컴포넌트를 기반으로 구현되어 접근성 기능이
|
|
10
|
+
* 자동으로 처리됩니다.
|
|
11
|
+
*
|
|
12
|
+
* ## When (언제 사용해야 하는가)
|
|
13
|
+
*
|
|
14
|
+
* **사용해야 하는 경우:**
|
|
15
|
+
* - **아이콘 설명**: 텍스트 레이블이 없는 아이콘 버튼의 기능을 설명할 때
|
|
16
|
+
* - **축약된 텍스트**: 말줄임표(...)로 잘린 텍스트의 전체 내용을 보여줄 때
|
|
17
|
+
* - **간단한 힌트**: 입력 필드나 기능에 대한 짧은 도움말을 제공할 때
|
|
18
|
+
* - **추가 정보**: UI 요소의 상태나 제약사항을 간단히 알려줄 때
|
|
19
|
+
*
|
|
20
|
+
* **사용하지 말아야 하는 경우:**
|
|
21
|
+
* - **필수 정보**: 사용자가 반드시 알아야 하는 정보는 본문에 직접 표시하세요.
|
|
22
|
+
* - **복잡한 내용**: 여러 줄의 긴 텍스트나 인터랙티브 요소는 Popover나 Modal을 사용하세요.
|
|
23
|
+
* - **모바일 전용 UI**: 터치 기기에서는 호버가 없으므로 중요한 정보를 툴팁에만 의존하지 마세요.
|
|
24
|
+
*
|
|
25
|
+
* ## Layout behavior
|
|
26
|
+
*
|
|
27
|
+
* - **Positioning**: 트리거 요소를 기준으로 상하좌우 원하는 위치에 배치됩니다. (기본: top)
|
|
28
|
+
* - **Auto-adjustment**: 화면 경계를 벗어나지 않도록 자동으로 위치를 조정합니다.
|
|
29
|
+
* - **Floating**: 다른 UI 요소들 위에 떠 있으며, 레이아웃에 영향을 주지 않습니다.
|
|
30
|
+
* - **Arrow**: 트리거와의 연결을 시각적으로 나타내는 화살표를 선택적으로 표시할 수 있습니다.
|
|
31
|
+
*
|
|
32
|
+
* ## Usage guidelines
|
|
33
|
+
*
|
|
34
|
+
* ### ✅ Do (권장 사항)
|
|
35
|
+
*
|
|
36
|
+
* - **간결한 텍스트**: 한두 줄 이내의 짧은 텍스트로 핵심만 전달하세요.
|
|
37
|
+
* - **적절한 지연**: `delayDuration`을 조절하여 우발적인 호버로 인한 깜빡임을 방지하세요. (기본: 200ms)
|
|
38
|
+
* - **명확한 트리거**: 툴팁이 붙는 요소가 호버 가능함을 사용자가 인지할 수 있도록 하세요.
|
|
39
|
+
*
|
|
40
|
+
* ### 🚫 Don't (주의/금지 사항)
|
|
41
|
+
*
|
|
42
|
+
* - **중요 정보 숨기기**: 필수적인 정보를 툴팁에만 넣지 마세요.
|
|
43
|
+
* - **상호작용 요소**: 툴팁 내부에 클릭 가능한 버튼이나 링크를 넣지 마세요. (Popover 사용 권장)
|
|
44
|
+
* - **과도한 사용**: 모든 요소에 툴팁을 붙이면 오히려 사용성이 떨어집니다.
|
|
45
|
+
*
|
|
46
|
+
* ## Accessibility
|
|
47
|
+
*
|
|
48
|
+
* - **Keyboard Support**: 포커스 가능한 요소에 툴팁이 붙으면 포커스 시 자동으로 표시됩니다.
|
|
49
|
+
* - **ESC Key**: `Esc` 키를 누르면 툴팁이 닫힙니다.
|
|
50
|
+
* - **Screen Readers**: `aria-describedby` 속성이 자동으로 부여되어 스크린 리더가 내용을 읽습니다.
|
|
51
|
+
* - **Touch Devices**: 터치 기기에서는 탭으로 툴팁을 활성화할 수 있습니다.
|
|
52
|
+
*
|
|
53
|
+
* ## Example
|
|
54
|
+
*
|
|
55
|
+
* {@tool snippet}
|
|
56
|
+
* 아이콘 버튼에 툴팁 추가:
|
|
57
|
+
*
|
|
58
|
+
* ```tsx
|
|
59
|
+
* <ToolTip content="설정">
|
|
60
|
+
* <Button variant="ghost" size="icon">
|
|
61
|
+
* <SettingsIcon />
|
|
62
|
+
* </Button>
|
|
63
|
+
* </ToolTip>
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* 위치와 지연 시간 커스터마이징:
|
|
67
|
+
*
|
|
68
|
+
* ```tsx
|
|
69
|
+
* <ToolTip
|
|
70
|
+
* content="이 기능은 프리미엄 사용자만 이용할 수 있습니다"
|
|
71
|
+
* side="bottom"
|
|
72
|
+
* delayDuration={300}
|
|
73
|
+
* >
|
|
74
|
+
* <Button disabled>프리미엄 기능</Button>
|
|
75
|
+
* </ToolTip>
|
|
76
|
+
* ```
|
|
77
|
+
* {@end-tool}
|
|
78
|
+
*
|
|
79
|
+
* See also:
|
|
80
|
+
*
|
|
81
|
+
* - {@link Popover}, 클릭으로 열고 상호작용이 필요한 경우
|
|
82
|
+
* - {@link Modal}, 중요한 정보나 복잡한 작업이 필요한 경우
|
|
83
|
+
*/
|
|
84
|
+
export interface ToolTipProps extends Omit<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>, "content"> {
|
|
85
|
+
/** 툴팁을 트리거하는 요소 */
|
|
86
|
+
children: React.ReactNode;
|
|
87
|
+
/** 툴팁에 표시할 내용 */
|
|
88
|
+
content: React.ReactNode;
|
|
89
|
+
/** 툴팁이 나타날 위치 (기본: "top") */
|
|
90
|
+
side?: "top" | "right" | "bottom" | "left";
|
|
91
|
+
/** 트리거로부터의 거리 (픽셀) (기본: 4) */
|
|
92
|
+
sideOffset?: number;
|
|
93
|
+
/** 트리거와의 정렬 방식 (기본: "center") */
|
|
94
|
+
align?: "start" | "center" | "end";
|
|
95
|
+
/** 마우스 호버 후 툴팁이 나타나기까지의 지연 시간 (밀리초) (기본: 200) */
|
|
96
|
+
delayDuration?: number;
|
|
97
|
+
/** 다른 툴팁에서 빠르게 진입 시 지연 건너뛰기 시간 (밀리초) (기본: 300) */
|
|
98
|
+
skipDelayDuration?: number;
|
|
99
|
+
/** 툴팁 위로 마우스를 이동할 수 있는지 여부 (기본: true) */
|
|
100
|
+
disableHoverableContent?: boolean;
|
|
101
|
+
/** 화살표 표시 여부 (기본: true) */
|
|
102
|
+
showArrow?: boolean;
|
|
103
|
+
/** Controlled 모드: 툴팁 열림 상태 */
|
|
104
|
+
open?: boolean;
|
|
105
|
+
/** Uncontrolled 모드: 초기 열림 상태 */
|
|
106
|
+
defaultOpen?: boolean;
|
|
107
|
+
/** 툴팁 열림 상태 변경 시 콜백 */
|
|
108
|
+
onOpenChange?: (open: boolean) => void;
|
|
109
|
+
}
|
|
110
|
+
export declare const ToolTip: React.ForwardRefExoticComponent<ToolTipProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -7,9 +7,13 @@ export * from './Text';
|
|
|
7
7
|
export * from './TextInput';
|
|
8
8
|
export * from './DatePicker';
|
|
9
9
|
export * from './DateRangePicker';
|
|
10
|
+
export * from './TimePicker';
|
|
10
11
|
export * from './Switch';
|
|
11
12
|
export * from './RadioButton';
|
|
12
13
|
export * from './SideNavigation';
|
|
14
|
+
export * from './Pagination';
|
|
13
15
|
export * from './Checkbox';
|
|
14
16
|
export * from './Modal';
|
|
15
17
|
export * from './Toast';
|
|
18
|
+
export * from './ToolTip';
|
|
19
|
+
export * from './Table';
|