@jk-core/components 1.1.1 → 1.1.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/dist/index.js +613 -554
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +5 -5
- package/dist/index.umd.cjs.map +1 -1
- package/dist/src/common/SegmentButton/index.d.ts +12 -1
- package/dist/src/common/Table/index.d.ts +46 -0
- package/dist/src/index.d.ts +2 -1
- package/package.json +1 -1
- package/src/common/Button/Button.module.scss +0 -1
- package/src/common/SegmentButton/index.tsx +21 -7
- package/src/common/Table/Table.module.scss +59 -0
- package/src/common/Table/index.tsx +119 -0
- package/src/index.tsx +2 -1
|
@@ -9,9 +9,20 @@ interface SegmentButtonProps {
|
|
|
9
9
|
* true로 설정하면 버튼이 부모 요소의 너비를 채웁니다.
|
|
10
10
|
*/
|
|
11
11
|
stretch?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* 현재 세그먼트 버튼에서 선택된 인덱스입니다.
|
|
14
|
+
* 값을 설정시, 해당 인덱스의 버튼이 선택된 상태로 표시됩니다.
|
|
15
|
+
* 기본값은 0입니다.
|
|
16
|
+
*/
|
|
17
|
+
selected?: number;
|
|
12
18
|
/**
|
|
13
19
|
* 세그먼트 버튼의 옵션을 설정합니다.
|
|
14
20
|
* 각 옵션은 텍스트와 클릭 이벤트 핸들러를 포함합니다.
|
|
21
|
+
* 예시:
|
|
22
|
+
* ```ts
|
|
23
|
+
* option: [
|
|
24
|
+
* { text: '오늘', onClick: () => setDate(new Date()) },
|
|
25
|
+
* { text: '내일', onClick: () => setDate(new Date(Date.now() + 24 * 60 * 60 * 1000)) },
|
|
15
26
|
*/
|
|
16
27
|
option: Array<{
|
|
17
28
|
text: string;
|
|
@@ -22,5 +33,5 @@ interface SegmentButtonProps {
|
|
|
22
33
|
* 세그먼트 버튼 컴포넌트는 여러 개의 버튼을 세그먼트 형태로 표시합니다.
|
|
23
34
|
* 각 버튼은 클릭 시 지정된 함수를 실행합니다.
|
|
24
35
|
*/
|
|
25
|
-
export default function SegmentButton({ width, stretch, option }: SegmentButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
export default function SegmentButton({ width, stretch, option, selected }: SegmentButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
26
37
|
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
interface TableHeaderType {
|
|
2
|
+
label: string;
|
|
3
|
+
width?: string;
|
|
4
|
+
subWidth?: string[];
|
|
5
|
+
subLabel?: string[];
|
|
6
|
+
}
|
|
7
|
+
interface TableBodyType {
|
|
8
|
+
head?: string;
|
|
9
|
+
labels: string[];
|
|
10
|
+
}
|
|
11
|
+
interface TableProps {
|
|
12
|
+
/**
|
|
13
|
+
* 테이블 헤더 설정
|
|
14
|
+
* - `label`: 헤더의 라벨 텍스트
|
|
15
|
+
* - `width`: 헤더의 너비, subWidth가 있을 경우 무시됨 (선택적)
|
|
16
|
+
* - `subWidth`: 서브 헤더의 너비 배열 (선택적)
|
|
17
|
+
* - `subLabel`: 서브 헤더의 라벨 배열 (선택적
|
|
18
|
+
*/
|
|
19
|
+
header: TableHeaderType[];
|
|
20
|
+
/**
|
|
21
|
+
* 테이블 바디 설정
|
|
22
|
+
* - `head`: 바디의 헤더 텍스트 (선택적)
|
|
23
|
+
* - `labels`: 각 행의 라벨 배열
|
|
24
|
+
*/
|
|
25
|
+
body: TableBodyType[];
|
|
26
|
+
/**
|
|
27
|
+
* 테이블의 모서리를 둥글게 처리할지 여부
|
|
28
|
+
* - `rounded`: true일 경우 모서리가 둥글게 처리됨
|
|
29
|
+
*/
|
|
30
|
+
rounded?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* 테이블의 테두리 표시 여부
|
|
33
|
+
* - `border`: true일 경우 테이블에 테두리가 표시됨
|
|
34
|
+
*/
|
|
35
|
+
border?: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 테이블 컴포넌트
|
|
39
|
+
* 헤더와 바디를 설정하여 테이블을 렌더링합니다.
|
|
40
|
+
* - `header`: 테이블의 헤더 설정
|
|
41
|
+
* - `body`: 테이블의 바디 설정
|
|
42
|
+
* - `rounded`: 테이블의 모서리를 둥글게 처리할지 여부
|
|
43
|
+
* - `border`: 테이블에 테두리를 표시할지 여부
|
|
44
|
+
*/
|
|
45
|
+
export default function Table({ header, body, rounded, border }: TableProps): import("react/jsx-runtime").JSX.Element;
|
|
46
|
+
export {};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -3,11 +3,12 @@ import { default as Button } from './common/Button';
|
|
|
3
3
|
import { default as Pagination } from './common/Pagination';
|
|
4
4
|
import { default as Skeleton } from './common/Skeleton';
|
|
5
5
|
import { default as SwitchButton } from './common/SwitchButton';
|
|
6
|
+
import { default as Table } from './common/Table';
|
|
6
7
|
import { default as Calendar } from './Calendar';
|
|
7
8
|
import { CalendarRange, CalendarView } from './Calendar/type';
|
|
8
9
|
import { default as Accordion } from './common/Accordion';
|
|
9
10
|
import { default as Card } from './common/Card';
|
|
10
11
|
import { default as DropDown } from './common/DropDown';
|
|
11
12
|
import { default as SegmentButton } from './common/SegmentButton';
|
|
12
|
-
export { Calendar, Accordion, Breadcrumbs, Button, Pagination, Skeleton, SwitchButton, Card, DropDown, SegmentButton, BreadcrumbsItem, };
|
|
13
|
+
export { Calendar, Accordion, Breadcrumbs, Button, Pagination, Skeleton, SwitchButton, Card, DropDown, SegmentButton, BreadcrumbsItem, Table, };
|
|
13
14
|
export type { CalendarView, CalendarRange };
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
1
|
+
import { useMemo, useState } from 'react';
|
|
2
2
|
import { cn } from '@jk-core/utils';
|
|
3
3
|
import styles from './SegmentButton.module.scss';
|
|
4
4
|
|
|
@@ -13,9 +13,21 @@ interface SegmentButtonProps {
|
|
|
13
13
|
* true로 설정하면 버튼이 부모 요소의 너비를 채웁니다.
|
|
14
14
|
*/
|
|
15
15
|
stretch?: boolean;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 현재 세그먼트 버튼에서 선택된 인덱스입니다.
|
|
19
|
+
* 값을 설정시, 해당 인덱스의 버튼이 선택된 상태로 표시됩니다.
|
|
20
|
+
* 기본값은 0입니다.
|
|
21
|
+
*/
|
|
22
|
+
selected?: number;
|
|
16
23
|
/**
|
|
17
24
|
* 세그먼트 버튼의 옵션을 설정합니다.
|
|
18
25
|
* 각 옵션은 텍스트와 클릭 이벤트 핸들러를 포함합니다.
|
|
26
|
+
* 예시:
|
|
27
|
+
* ```ts
|
|
28
|
+
* option: [
|
|
29
|
+
* { text: '오늘', onClick: () => setDate(new Date()) },
|
|
30
|
+
* { text: '내일', onClick: () => setDate(new Date(Date.now() + 24 * 60 * 60 * 1000)) },
|
|
19
31
|
*/
|
|
20
32
|
option:Array<{ text: string; onClick: () => void }>;
|
|
21
33
|
}
|
|
@@ -24,8 +36,10 @@ interface SegmentButtonProps {
|
|
|
24
36
|
* 세그먼트 버튼 컴포넌트는 여러 개의 버튼을 세그먼트 형태로 표시합니다.
|
|
25
37
|
* 각 버튼은 클릭 시 지정된 함수를 실행합니다.
|
|
26
38
|
*/
|
|
27
|
-
export default function SegmentButton({ width, stretch, option }: SegmentButtonProps) {
|
|
28
|
-
const [
|
|
39
|
+
export default function SegmentButton({ width, stretch, option, selected }: SegmentButtonProps) {
|
|
40
|
+
const [index, setIndex] = useState(selected ?? 0);
|
|
41
|
+
|
|
42
|
+
const selectedIndex = useMemo(() => selected ?? index, [index, selected]);
|
|
29
43
|
|
|
30
44
|
return (
|
|
31
45
|
<div
|
|
@@ -39,21 +53,21 @@ export default function SegmentButton({ width, stretch, option }: SegmentButtonP
|
|
|
39
53
|
className={styles['segment--selector']}
|
|
40
54
|
style={{
|
|
41
55
|
width: `${100 / option.length}%`,
|
|
42
|
-
left: `${
|
|
56
|
+
left: `${selectedIndex * 100 / option.length}%`,
|
|
43
57
|
}}
|
|
44
58
|
/>
|
|
45
|
-
{option.map((item,
|
|
59
|
+
{option.map((item, optionIndex) => (
|
|
46
60
|
<button
|
|
47
61
|
className={cn({
|
|
48
62
|
[styles.button]: true,
|
|
49
|
-
[styles['button--selected']]:
|
|
63
|
+
[styles['button--selected']]: optionIndex === selected,
|
|
50
64
|
})}
|
|
51
65
|
key={item.text}
|
|
52
66
|
type="button"
|
|
53
67
|
style={{ width: stretch ? 'auto' : (width || 'auto') }}
|
|
54
68
|
onClick={() => {
|
|
55
|
-
setSelected(index);
|
|
56
69
|
item.onClick();
|
|
70
|
+
setIndex(optionIndex);
|
|
57
71
|
}}
|
|
58
72
|
>
|
|
59
73
|
<div>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
.wrapper {
|
|
2
|
+
width: 100%;
|
|
3
|
+
max-height: 60dvh;
|
|
4
|
+
border: 1px solid var(--G-30);
|
|
5
|
+
overflow: auto;
|
|
6
|
+
|
|
7
|
+
&__rounded {
|
|
8
|
+
border-radius: 10px;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.table {
|
|
13
|
+
width: max-content;
|
|
14
|
+
border-collapse: separate;
|
|
15
|
+
border-spacing: 0;
|
|
16
|
+
table-layout: fixed; // 이 줄을 추가하세요!
|
|
17
|
+
|
|
18
|
+
thead {
|
|
19
|
+
position: sticky;
|
|
20
|
+
top: 0;
|
|
21
|
+
z-index: 1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
thead > tr > th:last-child,
|
|
25
|
+
tbody > tr > td:last-child {
|
|
26
|
+
border-right: none;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
th {
|
|
30
|
+
flex-shrink: 0;
|
|
31
|
+
background-color: var(--G-5);
|
|
32
|
+
padding: 10px;
|
|
33
|
+
border-right: 1px solid var(--G-30);
|
|
34
|
+
border-bottom: 1px solid var(--G-30);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
tbody > tr > th {
|
|
38
|
+
position: sticky;
|
|
39
|
+
left: 0;
|
|
40
|
+
white-space: pre-wrap;
|
|
41
|
+
max-width: 200px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
td {
|
|
45
|
+
padding: 10px;
|
|
46
|
+
background-color: var(--white);
|
|
47
|
+
word-break: break-all;
|
|
48
|
+
border-bottom: 1px solid var(--G-30);
|
|
49
|
+
border-right: 1px solid var(--G-30);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.fixed {
|
|
54
|
+
position: sticky;
|
|
55
|
+
left: 0;
|
|
56
|
+
top: 0;
|
|
57
|
+
background-color: var(--G-5);
|
|
58
|
+
z-index: 1;
|
|
59
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { cn } from '@jk-core/utils';
|
|
2
|
+
import styles from './Table.module.scss';
|
|
3
|
+
|
|
4
|
+
interface TableHeaderType {
|
|
5
|
+
label: string;
|
|
6
|
+
width?: string;
|
|
7
|
+
subWidth?: string[];
|
|
8
|
+
subLabel?:string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface TableBodyType{
|
|
12
|
+
head?: string;
|
|
13
|
+
labels: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface TableProps {
|
|
17
|
+
/**
|
|
18
|
+
* 테이블 헤더 설정
|
|
19
|
+
* - `label`: 헤더의 라벨 텍스트
|
|
20
|
+
* - `width`: 헤더의 너비, subWidth가 있을 경우 무시됨 (선택적)
|
|
21
|
+
* - `subWidth`: 서브 헤더의 너비 배열 (선택적)
|
|
22
|
+
* - `subLabel`: 서브 헤더의 라벨 배열 (선택적
|
|
23
|
+
*/
|
|
24
|
+
header: TableHeaderType[];
|
|
25
|
+
/**
|
|
26
|
+
* 테이블 바디 설정
|
|
27
|
+
* - `head`: 바디의 헤더 텍스트 (선택적)
|
|
28
|
+
* - `labels`: 각 행의 라벨 배열
|
|
29
|
+
*/
|
|
30
|
+
body: TableBodyType[];
|
|
31
|
+
/**
|
|
32
|
+
* 테이블의 모서리를 둥글게 처리할지 여부
|
|
33
|
+
* - `rounded`: true일 경우 모서리가 둥글게 처리됨
|
|
34
|
+
*/
|
|
35
|
+
rounded?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* 테이블의 테두리 표시 여부
|
|
38
|
+
* - `border`: true일 경우 테이블에 테두리가 표시됨
|
|
39
|
+
*/
|
|
40
|
+
border?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 테이블 컴포넌트
|
|
45
|
+
* 헤더와 바디를 설정하여 테이블을 렌더링합니다.
|
|
46
|
+
* - `header`: 테이블의 헤더 설정
|
|
47
|
+
* - `body`: 테이블의 바디 설정
|
|
48
|
+
* - `rounded`: 테이블의 모서리를 둥글게 처리할지 여부
|
|
49
|
+
* - `border`: 테이블에 테두리를 표시할지 여부
|
|
50
|
+
*/
|
|
51
|
+
export default function Table({ header, body, rounded, border }: TableProps) {
|
|
52
|
+
const subLabels = header.flatMap(col => col.subLabel || []);
|
|
53
|
+
const isBodyWithHead = body.some(row => row.head);
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div className={cn({
|
|
57
|
+
[styles.wrapper]: true,
|
|
58
|
+
[styles.wrapper__rounded]: !!rounded,
|
|
59
|
+
[styles.wrapper__border]: !!border,
|
|
60
|
+
})}
|
|
61
|
+
>
|
|
62
|
+
|
|
63
|
+
<table className={styles.table}>
|
|
64
|
+
<colgroup>
|
|
65
|
+
{isBodyWithHead && <col style={{ width: '100px' }} />}
|
|
66
|
+
{header.map((col, colIndex) =>
|
|
67
|
+
col.subLabel
|
|
68
|
+
? col.subLabel.map((_, subIndex) => (
|
|
69
|
+
<col
|
|
70
|
+
key={`col-${colIndex}-sub-${subIndex}`}
|
|
71
|
+
style={{ width: col?.subWidth?.[subIndex] || 'auto' }}
|
|
72
|
+
/>
|
|
73
|
+
))
|
|
74
|
+
: (
|
|
75
|
+
<col
|
|
76
|
+
key={`col-${colIndex}`}
|
|
77
|
+
style={{ width: col.width || 'auto' }}
|
|
78
|
+
/>
|
|
79
|
+
),
|
|
80
|
+
)}
|
|
81
|
+
|
|
82
|
+
</colgroup>
|
|
83
|
+
<thead>
|
|
84
|
+
<tr>
|
|
85
|
+
{body.some(row => row.head) && <th className={styles.fixed} rowSpan={2} />}
|
|
86
|
+
{header.map((col, index) => (
|
|
87
|
+
<th
|
|
88
|
+
key={index}
|
|
89
|
+
rowSpan={col.subLabel ? 1 : 2}
|
|
90
|
+
colSpan={col.subLabel ? col.subLabel.length : undefined}
|
|
91
|
+
>
|
|
92
|
+
{col.label}
|
|
93
|
+
</th>
|
|
94
|
+
))}
|
|
95
|
+
</tr>
|
|
96
|
+
{subLabels.length > 0 && (
|
|
97
|
+
<tr>
|
|
98
|
+
{subLabels.map((subLabel, index) => (
|
|
99
|
+
<th key={index}>{subLabel}</th>
|
|
100
|
+
))}
|
|
101
|
+
</tr>
|
|
102
|
+
)}
|
|
103
|
+
</thead>
|
|
104
|
+
<tbody>
|
|
105
|
+
{body.map((row, rowIndex) => (
|
|
106
|
+
<tr key={rowIndex}>
|
|
107
|
+
{isBodyWithHead && <th>{row?.head || ''}</th>}
|
|
108
|
+
{row.labels.map((col, colIndex) => (
|
|
109
|
+
<td key={colIndex}>
|
|
110
|
+
{col}
|
|
111
|
+
</td>
|
|
112
|
+
))}
|
|
113
|
+
</tr>
|
|
114
|
+
))}
|
|
115
|
+
</tbody>
|
|
116
|
+
</table>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import Button from '@/common/Button';
|
|
|
3
3
|
import Pagination from '@/common/Pagination';
|
|
4
4
|
import Skeleton from '@/common/Skeleton';
|
|
5
5
|
import SwitchButton from '@/common/SwitchButton';
|
|
6
|
+
import Table from '@/common/Table';
|
|
6
7
|
import Calendar from './Calendar';
|
|
7
8
|
import { CalendarRange, CalendarView } from './Calendar/type';
|
|
8
9
|
import Accordion from './common/Accordion';
|
|
@@ -13,7 +14,7 @@ import SegmentButton from './common/SegmentButton';
|
|
|
13
14
|
export {
|
|
14
15
|
Calendar, Accordion, Breadcrumbs, Button,
|
|
15
16
|
Pagination, Skeleton, SwitchButton,
|
|
16
|
-
Card, DropDown, SegmentButton, BreadcrumbsItem,
|
|
17
|
+
Card, DropDown, SegmentButton, BreadcrumbsItem, Table,
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
export type { CalendarView, CalendarRange };
|