@jk-core/components 1.1.16 → 1.1.17

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.
Files changed (83) hide show
  1. package/README.md +73 -73
  2. package/dist/index.js +2 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.umd.cjs +1 -1
  5. package/dist/index.umd.cjs.map +1 -1
  6. package/package.json +99 -99
  7. package/src/Calendar/Calendar.module.scss +213 -213
  8. package/src/Calendar/RangeCalendar.tsx +125 -125
  9. package/src/Calendar/ScrollCalendar.module.scss +214 -214
  10. package/src/Calendar/ScrollCalendar.tsx +124 -124
  11. package/src/Calendar/SingleCalendar.tsx +121 -121
  12. package/src/Calendar/components/DateLabel/DateLabel.module.scss +89 -89
  13. package/src/Calendar/components/DateLabel/index.tsx +91 -91
  14. package/src/Calendar/components/DayTile/DayTile.module.scss +117 -117
  15. package/src/Calendar/components/DayTile/index.tsx +108 -108
  16. package/src/Calendar/components/MonthTile/MonthTile.module.scss +59 -59
  17. package/src/Calendar/components/MonthTile/index.tsx +50 -50
  18. package/src/Calendar/components/ViewSelector/ViewSelector.module.scss +48 -48
  19. package/src/Calendar/components/ViewSelector/index.tsx +49 -49
  20. package/src/Calendar/components/YearTile/YearTile.module.scss +85 -85
  21. package/src/Calendar/components/YearTile/index.tsx +65 -65
  22. package/src/Calendar/hooks/useCalendarNav.ts +83 -83
  23. package/src/Calendar/hooks/useDateSelect.ts +54 -54
  24. package/src/Calendar/index.scss +189 -189
  25. package/src/Calendar/index.tsx +66 -66
  26. package/src/Calendar/type.ts +3 -3
  27. package/src/Calendar/utils/getWeeksInMonth.ts +45 -45
  28. package/src/Calendar/utils/isInRange.ts +8 -8
  29. package/src/Calendar/utils/isSameDay.ts +21 -21
  30. package/src/assets/arrow.svg +11 -11
  31. package/src/assets/close.svg +15 -15
  32. package/src/assets/drop-arrow.svg +3 -3
  33. package/src/common/Accordion/Accordion.module.scss +53 -53
  34. package/src/common/Accordion/arrow-down.svg +3 -3
  35. package/src/common/Accordion/arrow-up.svg +3 -3
  36. package/src/common/Accordion/index.tsx +54 -54
  37. package/src/common/Breadcrumbs/Breadcrumbs.module.scss +46 -46
  38. package/src/common/Breadcrumbs/home.svg +5 -5
  39. package/src/common/Breadcrumbs/index.tsx +82 -82
  40. package/src/common/Button/Button.module.scss +127 -127
  41. package/src/common/Button/index.tsx +60 -60
  42. package/src/common/Card/Card.module.scss +28 -28
  43. package/src/common/Card/index.tsx +19 -19
  44. package/src/common/Divider/Divider.module.scss +101 -101
  45. package/src/common/Divider/index.tsx +24 -24
  46. package/src/common/DropDown/DropDown.module.scss +135 -135
  47. package/src/common/DropDown/List.tsx +156 -156
  48. package/src/common/DropDown/arrow-down.svg +3 -3
  49. package/src/common/DropDown/index.tsx +108 -108
  50. package/src/common/DropDown/search.svg +4 -4
  51. package/src/common/Pagination/Pagination.module.scss +210 -210
  52. package/src/common/Pagination/arrow-left.svg +11 -11
  53. package/src/common/Pagination/arrow-right.svg +11 -11
  54. package/src/common/Pagination/index.tsx +156 -156
  55. package/src/common/SegmentButton/SegmentButton.module.scss +45 -45
  56. package/src/common/SegmentButton/index.tsx +79 -79
  57. package/src/common/Skeleton/Skeleton.module.scss +80 -80
  58. package/src/common/Skeleton/index.tsx +47 -47
  59. package/src/common/SwitchButton/SwitchButton.module.scss +65 -65
  60. package/src/common/SwitchButton/index.tsx +57 -56
  61. package/src/common/Table/Table.module.scss +70 -70
  62. package/src/common/Table/index.tsx +128 -128
  63. package/src/index.scss +1 -1
  64. package/src/index.tsx +21 -21
  65. package/src/styles/color.scss +346 -346
  66. package/src/styles/font-face.scss +18 -18
  67. package/src/styles/font.scss +49 -49
  68. package/src/styles/mediaQuery.scss +22 -22
  69. package/src/styles/scrollbar.scss +71 -71
  70. package/src/svg.d.ts +6 -6
  71. package/src/utils/styles/mediaQuery.scss +22 -22
  72. package/src/utils/ts/allowDecimal.ts +4 -4
  73. package/src/utils/ts/autoHypen.ts +33 -33
  74. package/src/utils/ts/calculateMax.ts +24 -24
  75. package/src/utils/ts/checkIsMobilePlatform.ts +15 -15
  76. package/src/utils/ts/formatFileSize.ts +16 -16
  77. package/src/utils/ts/formatMoney.ts +16 -16
  78. package/src/utils/ts/gradientRatio.ts +61 -61
  79. package/src/utils/ts/kiloToMega.ts +30 -30
  80. package/src/utils/ts/maskingPhone.ts +8 -8
  81. package/src/utils/ts/toQueryString.ts +7 -7
  82. package/src/utils/ts/valueAsNumber.ts +15 -15
  83. package/src/vite-env.d.ts +2 -2
@@ -1,12 +1,12 @@
1
- <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
2
-
3
- <!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
4
- <svg width="30px" height="30px" viewBox="0 0 24 24" fill="#000000" xmlns="http://www.w3.org/2000/svg">
5
-
6
- <g id="SVGRepo_bgCarrier"/>
7
-
8
- <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
9
-
10
- <g id="SVGRepo_iconCarrier"> <path d="M7.82054 20.7313C8.21107 21.1218 8.84423 21.1218 9.23476 20.7313L15.8792 14.0868C17.0505 12.9155 17.0508 11.0167 15.88 9.84497L9.3097 3.26958C8.91918 2.87905 8.28601 2.87905 7.89549 3.26958C7.50497 3.6601 7.50497 4.29327 7.89549 4.68379L14.4675 11.2558C14.8581 11.6464 14.8581 12.2795 14.4675 12.67L7.82054 19.317C7.43002 19.7076 7.43002 20.3407 7.82054 20.7313Z"/> </g>
11
-
1
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
2
+
3
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
4
+ <svg width="30px" height="30px" viewBox="0 0 24 24" fill="#000000" xmlns="http://www.w3.org/2000/svg">
5
+
6
+ <g id="SVGRepo_bgCarrier"/>
7
+
8
+ <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
9
+
10
+ <g id="SVGRepo_iconCarrier"> <path d="M7.82054 20.7313C8.21107 21.1218 8.84423 21.1218 9.23476 20.7313L15.8792 14.0868C17.0505 12.9155 17.0508 11.0167 15.88 9.84497L9.3097 3.26958C8.91918 2.87905 8.28601 2.87905 7.89549 3.26958C7.50497 3.6601 7.50497 4.29327 7.89549 4.68379L14.4675 11.2558C14.8581 11.6464 14.8581 12.2795 14.4675 12.67L7.82054 19.317C7.43002 19.7076 7.43002 20.3407 7.82054 20.7313Z"/> </g>
11
+
12
12
  </svg>
@@ -1,156 +1,156 @@
1
-
2
- import { useMemo, useState } from 'react';
3
- import LeftArrowIcon from './arrow-left.svg';
4
- import RightArrowIcon from './arrow-right.svg';
5
- import styles from './Pagination.module.scss';
6
-
7
- interface PaginationProps {
8
- /**
9
- * 총 페이지 수
10
- */
11
- totalPage: number;
12
- /**
13
- * 현재 페이지
14
- */
15
- currentPage: number;
16
-
17
- /**
18
- * 페이지 크기 (기본값: false)
19
- * true로 설정하면 보여지는 페이지의 개수가 5개에서 7개로 늘어납니다.
20
- */
21
- large?: boolean;
22
- /**
23
- * 페이지 클릭 핸들러
24
- */
25
- onPageClick: (page: number) => void;
26
- }
27
-
28
- export default function Pagination({ totalPage, currentPage, onPageClick, large = false }: PaginationProps) {
29
- const [animation, setAnimation] = useState<'left' | 'right' | 'doubleRight' | 'doubleLeft' | 'tripleRight' | 'tripleLeft' | ''>('');
30
- const [selectedPage, setSelectedPage] = useState(currentPage || 1);
31
- const weight = useMemo(() => (large ? 2 : 0), [large]);
32
-
33
- const pageArray = useMemo(() => {
34
- if (!totalPage) return Array.from({ length: 9 + weight }).map((_, i) => (i === 2 ? 1 : null));
35
-
36
- return Array.from({ length: 9 + weight }).map((_, i) => {
37
- const pageIndex = selectedPage + i - 2 - (weight / 2) ;
38
-
39
- if (pageIndex < 1 || pageIndex > totalPage) {
40
- return null;
41
- }
42
- return pageIndex;
43
- });
44
- }, [selectedPage, totalPage, weight]);
45
-
46
- const handlePageClick = (page: number) => {
47
- if (page > totalPage || page < 1) return;
48
- onPageClick(page);
49
- if (page === null) return;
50
- const diff = page - selectedPage;
51
-
52
- switch (diff) {
53
- case 1:
54
- setAnimation('right');
55
- break;
56
- case -1:
57
- setAnimation('left');
58
- break;
59
- case 2:
60
- setAnimation('doubleRight');
61
- break;
62
- case -2:
63
- setAnimation('doubleLeft');
64
- break;
65
- case 3:
66
- setAnimation('tripleRight');
67
- break;
68
- case -3:
69
- setAnimation('tripleLeft');
70
- break;
71
- default:
72
- setAnimation('');
73
- }
74
-
75
- setSelectedPage(page);
76
-
77
- setTimeout(() => {
78
- setAnimation('');
79
- }, 200);
80
- };
81
-
82
- return (
83
- <div className={styles.pagination}>
84
- <button
85
- className={`${styles.arrow} ${selectedPage <= 1 ? styles['arrow--disabled'] : '' }`}
86
- type="button"
87
- onClick={() => handlePageClick(selectedPage - 1)}
88
- >
89
- <LeftArrowIcon />
90
- </button>
91
- <div
92
- className={styles.slider}
93
- style={{ width: `${(8 + weight) * 40}px` }}
94
- >
95
- <div
96
- className={styles.track}
97
- />
98
- <div className={styles.portal}>
99
- {selectedPage > (3 + weight / 2) && (
100
- < >
101
- <button
102
- className={styles.page}
103
- type="button"
104
- onClick={() => handlePageClick(1)}
105
- >1
106
- </button>
107
- <span className={styles.portal__ellipsis}>•••</span>
108
- </>
109
- )}
110
- </div>
111
- <div
112
- className={styles.pages}
113
- style={{ width: `${(5 + weight) * 40}px` }}
114
- >
115
- <div className={`${styles.pageWrapper} ${animation === 'left' ? styles.left : ''} ${styles[animation]}`}>
116
- {pageArray.map((page, index) => (
117
- <button
118
- className={`${styles.page} ${selectedPage === page ? styles['page--selected'] : ''}`}
119
- key={index}
120
- type="button"
121
- disabled={page === null}
122
- onClick={() => {
123
- if (page !== null) {
124
- handlePageClick(page);
125
- }
126
- }}
127
- >
128
- <span>{page}</span>
129
- </button>
130
- ))}
131
- </div>
132
- </div>
133
- <div className={styles.portal}>
134
- {selectedPage < totalPage - (2 + weight / 2) && (
135
- <>
136
- <span className={styles.portal__ellipsis}>•••</span>
137
- <button
138
- className={styles.page}
139
- type="button"
140
- onClick={() => handlePageClick(totalPage)}
141
- >{totalPage}
142
- </button>
143
- </>
144
- )}
145
- </div>
146
- </div>
147
- <button
148
- className={`${styles.arrow} ${selectedPage >= totalPage ? styles['arrow--disabled'] : ''}`}
149
- type="button"
150
- onClick={() => handlePageClick(selectedPage + 1)}
151
- >
152
- <RightArrowIcon />
153
- </button>
154
- </div>
155
- );
156
- }
1
+
2
+ import { useMemo, useState } from 'react';
3
+ import LeftArrowIcon from './arrow-left.svg';
4
+ import RightArrowIcon from './arrow-right.svg';
5
+ import styles from './Pagination.module.scss';
6
+
7
+ interface PaginationProps {
8
+ /**
9
+ * 총 페이지 수
10
+ */
11
+ totalPage: number;
12
+ /**
13
+ * 현재 페이지
14
+ */
15
+ currentPage: number;
16
+
17
+ /**
18
+ * 페이지 크기 (기본값: false)
19
+ * true로 설정하면 보여지는 페이지의 개수가 5개에서 7개로 늘어납니다.
20
+ */
21
+ large?: boolean;
22
+ /**
23
+ * 페이지 클릭 핸들러
24
+ */
25
+ onPageClick: (page: number) => void;
26
+ }
27
+
28
+ export default function Pagination({ totalPage, currentPage, onPageClick, large = false }: PaginationProps) {
29
+ const [animation, setAnimation] = useState<'left' | 'right' | 'doubleRight' | 'doubleLeft' | 'tripleRight' | 'tripleLeft' | ''>('');
30
+ const [selectedPage, setSelectedPage] = useState(currentPage || 1);
31
+ const weight = useMemo(() => (large ? 2 : 0), [large]);
32
+
33
+ const pageArray = useMemo(() => {
34
+ if (!totalPage) return Array.from({ length: 9 + weight }).map((_, i) => (i === 2 ? 1 : null));
35
+
36
+ return Array.from({ length: 9 + weight }).map((_, i) => {
37
+ const pageIndex = selectedPage + i - 2 - (weight / 2) ;
38
+
39
+ if (pageIndex < 1 || pageIndex > totalPage) {
40
+ return null;
41
+ }
42
+ return pageIndex;
43
+ });
44
+ }, [selectedPage, totalPage, weight]);
45
+
46
+ const handlePageClick = (page: number) => {
47
+ if (page > totalPage || page < 1) return;
48
+ onPageClick(page);
49
+ if (page === null) return;
50
+ const diff = page - selectedPage;
51
+
52
+ switch (diff) {
53
+ case 1:
54
+ setAnimation('right');
55
+ break;
56
+ case -1:
57
+ setAnimation('left');
58
+ break;
59
+ case 2:
60
+ setAnimation('doubleRight');
61
+ break;
62
+ case -2:
63
+ setAnimation('doubleLeft');
64
+ break;
65
+ case 3:
66
+ setAnimation('tripleRight');
67
+ break;
68
+ case -3:
69
+ setAnimation('tripleLeft');
70
+ break;
71
+ default:
72
+ setAnimation('');
73
+ }
74
+
75
+ setSelectedPage(page);
76
+
77
+ setTimeout(() => {
78
+ setAnimation('');
79
+ }, 200);
80
+ };
81
+
82
+ return (
83
+ <div className={styles.pagination}>
84
+ <button
85
+ className={`${styles.arrow} ${selectedPage <= 1 ? styles['arrow--disabled'] : '' }`}
86
+ type="button"
87
+ onClick={() => handlePageClick(selectedPage - 1)}
88
+ >
89
+ <LeftArrowIcon />
90
+ </button>
91
+ <div
92
+ className={styles.slider}
93
+ style={{ width: `${(8 + weight) * 40}px` }}
94
+ >
95
+ <div
96
+ className={styles.track}
97
+ />
98
+ <div className={styles.portal}>
99
+ {selectedPage > (3 + weight / 2) && (
100
+ < >
101
+ <button
102
+ className={styles.page}
103
+ type="button"
104
+ onClick={() => handlePageClick(1)}
105
+ >1
106
+ </button>
107
+ <span className={styles.portal__ellipsis}>•••</span>
108
+ </>
109
+ )}
110
+ </div>
111
+ <div
112
+ className={styles.pages}
113
+ style={{ width: `${(5 + weight) * 40}px` }}
114
+ >
115
+ <div className={`${styles.pageWrapper} ${animation === 'left' ? styles.left : ''} ${styles[animation]}`}>
116
+ {pageArray.map((page, index) => (
117
+ <button
118
+ className={`${styles.page} ${selectedPage === page ? styles['page--selected'] : ''}`}
119
+ key={index}
120
+ type="button"
121
+ disabled={page === null}
122
+ onClick={() => {
123
+ if (page !== null) {
124
+ handlePageClick(page);
125
+ }
126
+ }}
127
+ >
128
+ <span>{page}</span>
129
+ </button>
130
+ ))}
131
+ </div>
132
+ </div>
133
+ <div className={styles.portal}>
134
+ {selectedPage < totalPage - (2 + weight / 2) && (
135
+ <>
136
+ <span className={styles.portal__ellipsis}>•••</span>
137
+ <button
138
+ className={styles.page}
139
+ type="button"
140
+ onClick={() => handlePageClick(totalPage)}
141
+ >{totalPage}
142
+ </button>
143
+ </>
144
+ )}
145
+ </div>
146
+ </div>
147
+ <button
148
+ className={`${styles.arrow} ${selectedPage >= totalPage ? styles['arrow--disabled'] : ''}`}
149
+ type="button"
150
+ onClick={() => handlePageClick(selectedPage + 1)}
151
+ >
152
+ <RightArrowIcon />
153
+ </button>
154
+ </div>
155
+ );
156
+ }
@@ -1,45 +1,45 @@
1
- .segment {
2
- position: relative;
3
- display: grid;
4
- width: fit-content;
5
- background-color: var(--G-10);
6
- border-radius: 8px;
7
- z-index: 0;
8
- overflow: hidden;
9
- font-size: 1em;
10
-
11
- &--selector {
12
- position: absolute;
13
- top: 0;
14
- left: 0;
15
- display: flex;
16
- align-items: center;
17
- justify-content: center;
18
- height: 100%;
19
- background-color: var(--white);
20
- border: 2px solid var(--G-10);
21
- border-radius: 8px;
22
- transition: all 0.2s;
23
- z-index: 1;
24
- }
25
- }
26
-
27
- .button {
28
- height: 100%;
29
- padding: 8px 5px;
30
- color: var(--G-40);
31
- transition: color 0.2s;
32
- font-weight: 500;
33
-
34
- & > div {
35
- position: relative;
36
- display: flex;
37
- align-items: center;
38
- justify-content: center;
39
- z-index: 10;
40
- }
41
-
42
- &--selected {
43
- color: var(--G-80);
44
- }
45
- }
1
+ .segment {
2
+ position: relative;
3
+ display: grid;
4
+ width: fit-content;
5
+ background-color: var(--G-10);
6
+ border-radius: 8px;
7
+ z-index: 0;
8
+ overflow: hidden;
9
+ font-size: 1em;
10
+
11
+ &--selector {
12
+ position: absolute;
13
+ top: 0;
14
+ left: 0;
15
+ display: flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ height: 100%;
19
+ background-color: var(--white);
20
+ border: 2px solid var(--G-10);
21
+ border-radius: 8px;
22
+ transition: all 0.2s;
23
+ z-index: 1;
24
+ }
25
+ }
26
+
27
+ .button {
28
+ height: 100%;
29
+ padding: 8px 5px;
30
+ color: var(--G-40);
31
+ transition: color 0.2s;
32
+ font-weight: 500;
33
+
34
+ & > div {
35
+ position: relative;
36
+ display: flex;
37
+ align-items: center;
38
+ justify-content: center;
39
+ z-index: 10;
40
+ }
41
+
42
+ &--selected {
43
+ color: var(--G-80);
44
+ }
45
+ }
@@ -1,80 +1,80 @@
1
- import { useMemo, useState } from 'react';
2
- import { cn } from '@jk-core/utils';
3
- import styles from './SegmentButton.module.scss';
4
-
5
- interface SegmentButtonProps {
6
- /**
7
- * 세그먼트 버튼의 너비를 지정합니다. 기본값은 'auto'입니다.
8
- * 만약 stretch가 true로 설정되면, 너비는 'auto'로 설정됩니다.
9
- */
10
- width?: string;
11
- /**
12
- * 세그먼트 버튼이 전체 너비를 차지하도록 설정합니다.
13
- * true로 설정하면 버튼이 부모 요소의 너비를 채웁니다.
14
- */
15
- stretch?: boolean;
16
-
17
- /**
18
- * 현재 세그먼트 버튼에서 선택된 인덱스입니다.
19
- * 값을 설정시, 해당 인덱스의 버튼이 선택된 상태로 표시됩니다.
20
- * 기본값은 0입니다.
21
- */
22
- selected?: number;
23
- /**
24
- * 세그먼트 버튼의 옵션을 설정합니다.
25
- * 각 옵션은 텍스트와 클릭 이벤트 핸들러를 포함합니다.
26
- * 예시:
27
- * ```ts
28
- * option: [
29
- * { text: '오늘', onClick: () => setDate(new Date()) },
30
- * { text: '내일', onClick: () => setDate(new Date(Date.now() + 24 * 60 * 60 * 1000)) },
31
- */
32
- option:Array<{ text: string; onClick: () => void }>;
33
- }
34
-
35
- /**
36
- * 세그먼트 버튼 컴포넌트는 여러 개의 버튼을 세그먼트 형태로 표시합니다.
37
- * 각 버튼은 클릭 시 지정된 함수를 실행합니다.
38
- */
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]);
43
-
44
- return (
45
- <div
46
- className={styles.segment}
47
- style={{
48
- width: stretch ? '100%' : undefined,
49
- gridTemplateColumns: `repeat(${option.length},1fr)`,
50
- }}
51
- >
52
- <div
53
- className={styles['segment--selector']}
54
- style={{
55
- width: `${100 / option.length}%`,
56
- left: `${selectedIndex * 100 / option.length}%`,
57
- }}
58
- />
59
- {option.map((item, optionIndex) => (
60
- <button
61
- className={cn({
62
- [styles.button]: true,
63
- [styles['button--selected']]: optionIndex === selectedIndex,
64
- })}
65
- key={item.text}
66
- type="button"
67
- style={{ width: stretch ? 'auto' : (width || 'auto') }}
68
- onClick={() => {
69
- item.onClick();
70
- setIndex(optionIndex);
71
- }}
72
- >
73
- <div>
74
- {item.text}
75
- </div>
76
- </button>
77
- ))}
78
- </div>
79
- );
1
+ import { useMemo, useState } from 'react';
2
+ import { cn } from '@jk-core/utils';
3
+ import styles from './SegmentButton.module.scss';
4
+
5
+ interface SegmentButtonProps {
6
+ /**
7
+ * 세그먼트 버튼의 너비를 지정합니다. 기본값은 'auto'입니다.
8
+ * 만약 stretch가 true로 설정되면, 너비는 'auto'로 설정됩니다.
9
+ */
10
+ width?: string;
11
+ /**
12
+ * 세그먼트 버튼이 전체 너비를 차지하도록 설정합니다.
13
+ * true로 설정하면 버튼이 부모 요소의 너비를 채웁니다.
14
+ */
15
+ stretch?: boolean;
16
+
17
+ /**
18
+ * 현재 세그먼트 버튼에서 선택된 인덱스입니다.
19
+ * 값을 설정시, 해당 인덱스의 버튼이 선택된 상태로 표시됩니다.
20
+ * 기본값은 0입니다.
21
+ */
22
+ selected?: number;
23
+ /**
24
+ * 세그먼트 버튼의 옵션을 설정합니다.
25
+ * 각 옵션은 텍스트와 클릭 이벤트 핸들러를 포함합니다.
26
+ * 예시:
27
+ * ```ts
28
+ * option: [
29
+ * { text: '오늘', onClick: () => setDate(new Date()) },
30
+ * { text: '내일', onClick: () => setDate(new Date(Date.now() + 24 * 60 * 60 * 1000)) },
31
+ */
32
+ option:Array<{ text: string; onClick: () => void }>;
33
+ }
34
+
35
+ /**
36
+ * 세그먼트 버튼 컴포넌트는 여러 개의 버튼을 세그먼트 형태로 표시합니다.
37
+ * 각 버튼은 클릭 시 지정된 함수를 실행합니다.
38
+ */
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]);
43
+
44
+ return (
45
+ <div
46
+ className={styles.segment}
47
+ style={{
48
+ width: stretch ? '100%' : undefined,
49
+ gridTemplateColumns: `repeat(${option.length},1fr)`,
50
+ }}
51
+ >
52
+ <div
53
+ className={styles['segment--selector']}
54
+ style={{
55
+ width: `${100 / option.length}%`,
56
+ left: `${selectedIndex * 100 / option.length}%`,
57
+ }}
58
+ />
59
+ {option.map((item, optionIndex) => (
60
+ <button
61
+ className={cn({
62
+ [styles.button]: true,
63
+ [styles['button--selected']]: optionIndex === selectedIndex,
64
+ })}
65
+ key={item.text}
66
+ type="button"
67
+ style={{ width: stretch ? 'auto' : (width || 'auto') }}
68
+ onClick={() => {
69
+ item.onClick();
70
+ setIndex(optionIndex);
71
+ }}
72
+ >
73
+ <div>
74
+ {item.text}
75
+ </div>
76
+ </button>
77
+ ))}
78
+ </div>
79
+ );
80
80
  }