@jk-core/components 1.0.1 → 1.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.
Files changed (87) hide show
  1. package/dist/index.js +1287 -730
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.umd.cjs +5 -5
  4. package/dist/index.umd.cjs.map +1 -1
  5. package/dist/src/Calendar/components/DateLabel/index.d.ts +1 -1
  6. package/dist/src/Calendar/components/DayTile/index.d.ts +1 -1
  7. package/dist/src/Calendar/components/ViewSelector/index.d.ts +1 -1
  8. package/dist/src/common/Accordion/index.d.ts +12 -0
  9. package/dist/src/common/Breadcrumbs/index.d.ts +40 -0
  10. package/dist/src/common/Button/index.d.ts +27 -0
  11. package/dist/src/common/Card/index.d.ts +6 -0
  12. package/dist/src/common/DropDown/List.d.ts +10 -0
  13. package/dist/src/common/DropDown/index.d.ts +29 -0
  14. package/dist/src/common/Pagination/index.d.ts +16 -0
  15. package/dist/src/common/SegmentButton/index.d.ts +26 -0
  16. package/dist/src/common/Skeleton/index.d.ts +28 -0
  17. package/dist/src/common/SwitchButton/index.d.ts +19 -0
  18. package/dist/src/index.d.ts +10 -1
  19. package/dist/src/utils/ts/allowDecimal.d.ts +1 -0
  20. package/dist/src/utils/ts/autoHypen.d.ts +1 -0
  21. package/dist/src/utils/ts/calculateMax.d.ts +9 -0
  22. package/dist/src/utils/ts/checkIsMobilePlatform.d.ts +2 -0
  23. package/dist/src/utils/ts/formatFileSize.d.ts +4 -0
  24. package/dist/src/utils/ts/formatMoney.d.ts +2 -0
  25. package/dist/src/utils/ts/gradientRatio.d.ts +19 -0
  26. package/dist/src/utils/ts/kiloToMega.d.ts +6 -0
  27. package/dist/src/utils/ts/maskingPhone.d.ts +2 -0
  28. package/dist/src/utils/ts/toQueryString.d.ts +5 -0
  29. package/dist/src/utils/ts/valueAsNumber.d.ts +12 -0
  30. package/package.json +33 -25
  31. package/src/Calendar/RangeCalendar.tsx +5 -5
  32. package/src/Calendar/ScrollCalendar.tsx +3 -3
  33. package/src/Calendar/SingleCalendar.tsx +15 -15
  34. package/src/Calendar/components/DateLabel/index.tsx +19 -19
  35. package/src/Calendar/components/DayTile/index.tsx +13 -5
  36. package/src/Calendar/components/MonthTile/index.tsx +2 -2
  37. package/src/Calendar/components/ViewSelector/index.tsx +7 -7
  38. package/src/Calendar/components/YearTile/YearTile.module.scss +0 -1
  39. package/src/Calendar/components/YearTile/index.tsx +1 -1
  40. package/src/Calendar/hooks/useCalendarNav.ts +4 -1
  41. package/src/Calendar/index.tsx +3 -3
  42. package/src/Calendar/utils/isInRange.ts +1 -1
  43. package/src/common/Accordion/Accordion.module.scss +52 -0
  44. package/src/common/Accordion/arrow-down.svg +3 -0
  45. package/src/common/Accordion/arrow-up.svg +3 -0
  46. package/src/common/Accordion/index.tsx +55 -0
  47. package/src/common/Breadcrumbs/Breadcrumbs.module.scss +45 -0
  48. package/src/common/Breadcrumbs/home.svg +5 -0
  49. package/src/common/Breadcrumbs/index.tsx +82 -0
  50. package/src/common/Button/Button.module.scss +130 -0
  51. package/src/common/Button/index.tsx +60 -0
  52. package/src/common/Card/Card.module.scss +27 -0
  53. package/src/common/Card/index.tsx +19 -0
  54. package/src/common/DropDown/DropDown.module.scss +135 -0
  55. package/src/common/DropDown/List.tsx +157 -0
  56. package/src/common/DropDown/arrow-down.svg +3 -0
  57. package/src/common/DropDown/index.tsx +104 -0
  58. package/src/common/DropDown/search.svg +4 -0
  59. package/src/common/Pagination/Pagination.module.scss +177 -0
  60. package/src/common/Pagination/arrow-left.svg +12 -0
  61. package/src/common/Pagination/arrow-right.svg +12 -0
  62. package/src/common/Pagination/index.tsx +141 -0
  63. package/src/common/SegmentButton/SegmentButton.module.scss +44 -0
  64. package/src/common/SegmentButton/index.tsx +66 -0
  65. package/src/common/Skeleton/Skeleton.module.scss +80 -0
  66. package/src/common/Skeleton/index.tsx +48 -0
  67. package/src/common/SwitchButton/SwitchButton.module.scss +65 -0
  68. package/src/common/SwitchButton/index.tsx +56 -0
  69. package/src/index.scss +1 -0
  70. package/src/index.tsx +17 -1
  71. package/src/styles/color.scss +94 -0
  72. package/src/styles/font-face.scss +18 -0
  73. package/src/styles/font.scss +49 -0
  74. package/src/styles/scrollbar.scss +71 -0
  75. package/src/svg.d.ts +4 -2
  76. package/src/utils/styles/mediaQuery.scss +22 -0
  77. package/src/utils/ts/allowDecimal.ts +5 -0
  78. package/src/utils/ts/autoHypen.ts +33 -0
  79. package/src/utils/ts/calculateMax.ts +24 -0
  80. package/src/utils/ts/checkIsMobilePlatform.ts +15 -0
  81. package/src/utils/ts/formatFileSize.ts +16 -0
  82. package/src/utils/ts/formatMoney.ts +16 -0
  83. package/src/utils/ts/gradientRatio.ts +61 -0
  84. package/src/utils/ts/kiloToMega.ts +30 -0
  85. package/src/utils/ts/maskingPhone.ts +9 -0
  86. package/src/utils/ts/toQueryString.ts +7 -0
  87. package/src/utils/ts/valueAsNumber.ts +16 -0
@@ -0,0 +1,27 @@
1
+ interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {
2
+ /** 버튼 내용 */
3
+ text: string;
4
+ /** 버튼 등급 (Primary, Secondary, Tertiary, Cancel) */
5
+ grade?: 'tertiary' | 'secondary' | 'primary' | 'cancel';
6
+ /** 버튼 비활성화 여부 */
7
+ disabled?: boolean;
8
+ /** 버튼 클래스 */
9
+ className?: string;
10
+ /** 로딩 중 여부, 버튼 내부 스피너 표시 */
11
+ isLoading?: boolean;
12
+ /** 버튼 클릭 이벤트 핸들러 */
13
+ onClick?: () => void;
14
+ }
15
+ /**
16
+ * 공용 버튼 컴포넌트입니다.
17
+ *
18
+ * 다양한 등급(grade)과 상태(로딩, 비활성화 등)를 지원하며, 텍스트와 클릭 이벤트를 전달할 수 있습니다.
19
+ *
20
+ * - `grade` prop을 통해 버튼의 스타일(Primary, Secondary, Tertiary, Cancel)을 지정할 수 있습니다.
21
+ * - `isLoading`이 true일 때 로딩 애니메이션이 표시됩니다.
22
+ * - `disabled`가 true이면 버튼이 비활성화됩니다.
23
+ * - `text` prop으로 버튼에 표시할 텍스트를 지정합니다.
24
+ * - 추가적으로 button의 모든 기본 속성(React.)을 지원합니다.
25
+ */
26
+ export declare function Button({ text, grade, disabled, onClick, className, isLoading, ...props }: Props): import("react/jsx-runtime").JSX.Element;
27
+ export {};
@@ -0,0 +1,6 @@
1
+ interface CardProps {
2
+ title: string | React.ReactNode;
3
+ children?: React.ReactNode;
4
+ }
5
+ export default function Card({ title, children }: CardProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,10 @@
1
+ interface ListProps {
2
+ selectedItem?: string;
3
+ list: string[];
4
+ filteredList: string[];
5
+ parent: React.RefObject<HTMLDivElement | null>;
6
+ onSelect: (item: string, index: number) => void;
7
+ setFilteredList?: React.Dispatch<React.SetStateAction<string[]>>;
8
+ }
9
+ export default function List({ parent, selectedItem, list, filteredList, onSelect, setFilteredList }: ListProps): import("react/jsx-runtime").JSX.Element;
10
+ export {};
@@ -0,0 +1,29 @@
1
+ interface DropDownProps {
2
+ /**
3
+ * 드롭다운 목록
4
+ */
5
+ list: string[];
6
+ /**
7
+ * 선택된 아이템을 처리하는 함수
8
+ */
9
+ onSelect: (item: string, index: number) => void;
10
+ /**
11
+ * 필터링 기능 활성화 여부
12
+ */
13
+ filter?: boolean;
14
+ /**
15
+ * 드롭다운의 플레이스홀더 텍스트
16
+ */
17
+ placeholder?: string;
18
+ /**
19
+ * 드롭다운의 스타일
20
+ */
21
+ style?: React.CSSProperties;
22
+ }
23
+ /**
24
+ * 드롭다운 컴포넌트
25
+ * 사용자가 선택할 수 있는 목록을 제공하며, 선택된 아이템을 표시합니다.
26
+ * 필터링 기능을 활성화할 수 있으며, 플레이스홀더 텍스트를 설정할 수 있습니다.
27
+ */
28
+ export default function DropDown({ list, onSelect, filter, placeholder, style }: DropDownProps): import("react/jsx-runtime").JSX.Element;
29
+ export {};
@@ -0,0 +1,16 @@
1
+ interface PaginationProps {
2
+ /**
3
+ * 총 페이지 수
4
+ */
5
+ totalPage: number;
6
+ /**
7
+ * 현재 페이지
8
+ */
9
+ currentPage: number;
10
+ /**
11
+ * 페이지 클릭 핸들러
12
+ */
13
+ onPageClick: (page: number) => void;
14
+ }
15
+ export default function Pagination({ totalPage, currentPage, onPageClick }: PaginationProps): import("react/jsx-runtime").JSX.Element;
16
+ export {};
@@ -0,0 +1,26 @@
1
+ interface SegmentButtonProps {
2
+ /**
3
+ * 세그먼트 버튼의 너비를 지정합니다. 기본값은 'auto'입니다.
4
+ * 만약 stretch가 true로 설정되면, 너비는 'auto'로 설정됩니다.
5
+ */
6
+ width?: string;
7
+ /**
8
+ * 세그먼트 버튼이 전체 너비를 차지하도록 설정합니다.
9
+ * true로 설정하면 버튼이 부모 요소의 너비를 채웁니다.
10
+ */
11
+ stretch?: boolean;
12
+ /**
13
+ * 세그먼트 버튼의 옵션을 설정합니다.
14
+ * 각 옵션은 텍스트와 클릭 이벤트 핸들러를 포함합니다.
15
+ */
16
+ option: Array<{
17
+ text: string;
18
+ onClick: () => void;
19
+ }>;
20
+ }
21
+ /**
22
+ * 세그먼트 버튼 컴포넌트는 여러 개의 버튼을 세그먼트 형태로 표시합니다.
23
+ * 각 버튼은 클릭 시 지정된 함수를 실행합니다.
24
+ */
25
+ export default function SegmentButton({ width, stretch, option }: SegmentButtonProps): import("react/jsx-runtime").JSX.Element;
26
+ export {};
@@ -0,0 +1,28 @@
1
+ interface SkeletonProps {
2
+ /**
3
+ * 스켈레톤의 모양을 지정합니다. 'circle', 'rectangle', 'round' 중 하나를 선택할 수 있습니다.
4
+ * 기본값은 'rectangle'입니다.
5
+ */
6
+ shape?: 'circle' | 'rectangle' | 'round';
7
+ /**
8
+ * 스켈레톤의 너비를 지정합니다. 기본값은 '100%'입니다.
9
+ * 'auto'로 설정하면 콘텐츠에 맞게 자동으로 조정됩니다.
10
+ */
11
+ width?: string | number;
12
+ /**
13
+ * 스켈레톤의 높이를 지정합니다. 기본값은 '20px'입니다.
14
+ * 'auto'로 설정하면 콘텐츠에 맞게 자동으로 조정됩니다.
15
+ */
16
+ height?: string | number;
17
+ /**
18
+ * 스켈레톤 애니메이션 효과를 지정합니다. 'glow' 또는 'blink' 중 하나를 선택할 수 있습니다.
19
+ * 기본값은 'glow'입니다.
20
+ */
21
+ animation?: 'glow' | 'blink';
22
+ }
23
+ /**
24
+ * 스켈레톤 컴포넌트는 로딩 상태를 나타내기 위해 사용됩니다.
25
+ * 다양한 모양과 크기를 지원하며, 애니메이션 효과를 적용할 수 있습니다.
26
+ */
27
+ export default function Skeleton({ shape, width, height, animation }: SkeletonProps): import("react/jsx-runtime").JSX.Element;
28
+ export {};
@@ -0,0 +1,19 @@
1
+ interface SwitchButtonProps {
2
+ /**
3
+ * 스위치 버튼의 상태를 나타냅니다.
4
+ * true이면 스위치가 켜진 상태, false이면 꺼진 상태입니다.
5
+ * 기본값은 false로 설정되어 있습니다.
6
+ */
7
+ checked?: boolean;
8
+ /**
9
+ * 스위치 버튼의 상태가 변경될 때 호출되는 함수입니다.
10
+ */
11
+ onChange?: (value: boolean) => void;
12
+ }
13
+ /**
14
+ * SwitchButton 컴포넌트
15
+ * 스위치 버튼을 표시하며, 클릭 시 상태가 변경됩니다.
16
+ * checked 속성으로 초기 상태를 설정할 수 있으며, onChange 함수를 통해 상태 변경을 처리할 수 있습니다.
17
+ */
18
+ export default function SwitchButton({ checked, onChange }: SwitchButtonProps): import("react/jsx-runtime").JSX.Element;
19
+ export {};
@@ -1,4 +1,13 @@
1
+ import { default as Breadcrumbs, BreadcrumbsItem } from './common/Breadcrumbs';
2
+ import { Button } from './common/Button';
3
+ import { default as Pagination } from './common/Pagination';
4
+ import { default as Skeleton } from './common/Skeleton';
5
+ import { default as SwitchButton } from './common/SwitchButton';
1
6
  import { default as Calendar } from './Calendar';
2
7
  import { CalendarRange, CalendarView } from './Calendar/type';
3
- export { Calendar };
8
+ import { default as Accordion } from './common/Accordion';
9
+ import { default as Card } from './common/Card';
10
+ import { default as DropDown } from './common/DropDown';
11
+ import { default as SegmentButton } from './common/SegmentButton';
12
+ export { Calendar, Accordion, Breadcrumbs, Button, Pagination, Skeleton, SwitchButton, Card, DropDown, SegmentButton, BreadcrumbsItem, };
4
13
  export type { CalendarView, CalendarRange };
@@ -0,0 +1 @@
1
+ export declare const allowDecimal: (val: number) => boolean;
@@ -0,0 +1 @@
1
+ export default function AutoHypen(input: string): string;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * unit으로 나누어떨어지면서,
3
+ * a가 양수라면, a보다 크되 가장 작은 정수.
4
+ * a가 음수라면, a보다 작되 가장 큰 정수를 반환.
5
+ * 절댓값이 threshold에 미치지못한다면, 그대로 반환.
6
+ * @param {*} value
7
+ * @param {*} unit
8
+ */
9
+ export declare const calculateMax: (value?: number, threshold?: number, unit?: number) => number | undefined;
@@ -0,0 +1,2 @@
1
+ declare const checkIsMobile: () => boolean;
2
+ export default checkIsMobile;
@@ -0,0 +1,4 @@
1
+ export declare const formatFileSize: (bytes: number, unit?: "KB" | "MB" | "GB" | "TB") => {
2
+ size: number;
3
+ unit: string;
4
+ };
@@ -0,0 +1,2 @@
1
+ declare const formatMoney: (value?: number) => "- 원" | (string | number)[];
2
+ export default formatMoney;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 특정 퍼센트에서 시작 색상과 끝 색상 사이의 그라디언트 색상을 계산합니다.
3
+ *
4
+ * @param {Object} params - 그라디언트 계산을 위한 매개변수입니다.
5
+ * @param {number} params.currPercent - 그라디언트 색상을 계산할 현재 퍼센트입니다.
6
+ * @param {number} params.startPercent - 그라디언트의 시작 퍼센트입니다.
7
+ * @param {string} params.startColor - 그라디언트의 시작 색상입니다. 16진수 형식(앞에 # 없이)으로 입력합니다.
8
+ * @param {number} params.endPercent - 그라디언트의 끝 퍼센트입니다.
9
+ * @param {string} params.endColor - 그라디언트의 끝 색상입니다. 16진수 형식(앞에 # 없이)으로 입력합니다.
10
+ * @returns {string} 계산된 그라디언트 색상입니다. 16진수 형식(앞에 # 없이)으로 반환됩니다.
11
+ */
12
+ declare const gradientRatio: ({ currPercent, startPercent, startColor, endPercent, endColor, }: {
13
+ currPercent: number;
14
+ startPercent: number;
15
+ startColor: string;
16
+ endPercent: number;
17
+ endColor: string;
18
+ }) => string;
19
+ export default gradientRatio;
@@ -0,0 +1,6 @@
1
+ declare const KiloToMega: (amount: number | null | undefined, unit?: string, scale?: number, isKilo?: boolean) => {
2
+ generation: number;
3
+ unit: string;
4
+ total: string;
5
+ };
6
+ export default KiloToMega;
@@ -0,0 +1,2 @@
1
+ declare const maskingPhone: (phone?: string) => string;
2
+ export default maskingPhone;
@@ -0,0 +1,5 @@
1
+ declare const makeQueryString: ({ path, data }: {
2
+ path: string;
3
+ data: object;
4
+ }) => string;
5
+ export default makeQueryString;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * useForm의 register option중
3
+ * valueAsNumber: true인 경우에
4
+ * input 필드가 비어있는 경우 (즉, ''인 경우)
5
+ * 필드값이 NaN으로 할당되어 이를 대체하기위한 함수
6
+ *
7
+ * e.g.) ...register('calibrationFactor', valueAsNumber()),
8
+ * e.g.) ...register('vltMp', { required: true, ...valueAsNumber() }),
9
+ */
10
+ export declare const valueAsNumber: () => {
11
+ setValueAs: (v: string) => undefined | string | number;
12
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jk-core/components",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.umd.cjs",
6
6
  "types": "./dist/index.d.ts",
@@ -44,8 +44,9 @@
44
44
  "build": "yarn clean && vite build",
45
45
  "clean": "rm -rf dist",
46
46
  "lint": "yarn lint:eslint && yarn lint:stylelint",
47
- "lint:eslint": "eslint ./src/ --ext .tsx,.ts",
48
- "lint:stylelint": "stylelint \"./src/**/*.scss\" --config .stylelintrc.json"
47
+ "lint:fix": "yarn lint:eslint --fix && yarn lint:stylelint --fix",
48
+ "lint:eslint": "eslint src",
49
+ "lint:stylelint": "stylelint \"./src/**/*.scss\" --config ../../.stylelintrc.json"
49
50
  },
50
51
  "dependencies": {
51
52
  "@jk-core/hooks": "^1.0.0",
@@ -54,34 +55,41 @@
54
55
  "react-dom": "^19.0.0"
55
56
  },
56
57
  "devDependencies": {
57
- "@eslint/js": "^9.21.0",
58
- "@types/node": "^22.14.0",
59
- "@types/react": "^19.0.10",
60
- "@types/react-dom": "^19.0.4",
61
- "@vitejs/plugin-react": "^4.3.4",
62
- "eslint": "^8.57.0",
58
+ "@eslint/js": "^9.30.1",
59
+ "@stylistic/eslint-plugin": "^5.1.0",
60
+ "@types/eslint-plugin-jsx-a11y": "^6.10.0",
61
+ "@types/node": "^24.0.10",
62
+ "@types/react": "^19.1.8",
63
+ "@types/react-dom": "^19.1.6",
64
+ "@vitejs/plugin-react": "^4.6.0",
65
+ "code-inspector-plugin": "^0.20.15",
66
+ "eslint": "^9.30.1",
63
67
  "eslint-config-airbnb": "^19.0.4",
64
68
  "eslint-config-airbnb-typescript": "^18.0.0",
65
- "eslint-import-resolver-typescript": "^3.6.1",
66
- "eslint-plugin-import": "^2.29.1",
67
- "eslint-plugin-jsx-a11y": "^6.8.0",
68
- "eslint-plugin-react": "^7.34.2",
69
- "eslint-plugin-react-hooks": "^4.6.2",
70
- "eslint-plugin-react-refresh": "^0.4.6",
71
- "globals": "^15.15.0",
69
+ "eslint-import-resolver-typescript": "^4.4.4",
70
+ "eslint-plugin-import": "^2.32.0",
71
+ "eslint-plugin-jsx-a11y": "^6.10.2",
72
+ "eslint-plugin-react": "^7.37.5",
73
+ "eslint-plugin-react-hooks": "^5.2.0",
74
+ "eslint-plugin-react-refresh": "^0.4.20",
75
+ "globals": "^16.3.0",
72
76
  "optionator": "^0.9.4",
73
- "postcss": "^8.4.38",
77
+ "postcss": "^8.5.6",
78
+ "postcss-scss": "^4.0.9",
74
79
  "sass": "^1.80.0",
75
80
  "sass-loader": "^16.0.2",
76
- "stylelint": "^16.6.1",
77
- "stylelint-config-standard": "^36.0.0",
78
- "stylelint-config-standard-scss": "^13.1.0",
79
- "stylelint-scss": "^6.8.0",
80
- "stylelint-selector-bem-pattern": "^4.0.0",
81
- "typescript": "~5.7.2",
82
- "vite": "^6.2.0",
83
- "vite-plugin-checker": "^0.9.1",
81
+ "stylelint": "^16.21.1",
82
+ "stylelint-config-standard": "^38.0.0",
83
+ "stylelint-config-standard-scss": "^15.0.1",
84
+ "stylelint-scss": "^6.12.1",
85
+ "stylelint-selector-bem-pattern": "^4.0.1",
86
+ "tslib": "^2.8.1",
87
+ "typescript": "^5.8.3",
88
+ "typescript-eslint": "^8.36.0",
89
+ "vite": "^7.0.2",
90
+ "vite-plugin-checker": "^0.10.0",
84
91
  "vite-plugin-css-injected-by-js": "^3.5.2",
92
+ "vite-plugin-css-modules": "^0.0.1",
85
93
  "vite-plugin-dts": "^4.5.3",
86
94
  "vite-plugin-sass-dts": "^1.3.31",
87
95
  "vite-plugin-svgr": "^4.3.0",
@@ -1,12 +1,12 @@
1
- /* eslint-disable react/no-array-index-key */
2
- import { cn } from '@jk-core/utils';
1
+
3
2
  import { useEffect, useState } from 'react';
4
- import CloseIcon from '../assets/close.svg';
5
- import DayTile from './components/DayTile';
3
+ import { cn } from '@jk-core/utils';
4
+ import CloseIcon from '@/assets/close.svg';
6
5
  import styles from './Calendar.module.scss';
7
- import { CalendarRange } from './type';
8
6
  import DateLabel from './components/DateLabel';
7
+ import DayTile from './components/DayTile';
9
8
  import useCalendarNav from './hooks/useCalendarNav';
9
+ import { CalendarRange } from './type';
10
10
  import getWeeksInMonth from './utils/getWeeksInMonth';
11
11
 
12
12
  interface CalendarProps {
@@ -1,11 +1,11 @@
1
- import { cn } from '@jk-core/utils';
2
1
  import { useEffect, useRef, useState } from 'react';
3
2
  import { useIntersectionObserver } from '@jk-core/hooks';
4
- import DayTile from './components/DayTile';
3
+ import { cn } from '@jk-core/utils';
5
4
  import styles from './Calendar.module.scss';
6
5
  import DateLabel from './components/DateLabel';
7
- import getWeeksInMonth from './utils/getWeeksInMonth';
6
+ import DayTile from './components/DayTile';
8
7
  import { CalendarRange } from './type';
8
+ import getWeeksInMonth from './utils/getWeeksInMonth';
9
9
 
10
10
  interface CalendarProps {
11
11
  className?: string;
@@ -1,18 +1,18 @@
1
- /* eslint-disable react/no-array-index-key */
1
+
2
2
  import { useEffect, useState } from 'react';
3
3
  import { cn } from '@jk-core/utils';
4
- import CloseIcon from '../assets/close.svg';
5
- import { CalendarView } from './type';
6
- import getWeeksInMonth from './utils/getWeeksInMonth';
4
+ import CloseIcon from '@/assets/close.svg';
5
+ import styles from './Calendar.module.scss';
6
+ import DateLabel from './components/DateLabel';
7
7
  import DayTile from './components/DayTile';
8
8
  import MonthTile from './components/MonthTile';
9
+ import ViewSelector from './components/ViewSelector';
9
10
  import YearTile from './components/YearTile';
10
11
  import useCalendarNav from './hooks/useCalendarNav';
11
12
  import useDateSelect from './hooks/useDateSelect';
12
- import styles from './Calendar.module.scss';
13
- import ViewSelector from './components/ViewSelector';
13
+ import { CalendarView } from './type';
14
+ import getWeeksInMonth from './utils/getWeeksInMonth';
14
15
  import './index.scss';
15
- import DateLabel from './components/DateLabel';
16
16
 
17
17
  interface CalendarProps {
18
18
  className?: string;
@@ -86,14 +86,14 @@ export default function SingleCalendar({
86
86
  />
87
87
 
88
88
  {(method === 'day' && selectMode === 'day') && (
89
- <DayTile
90
- selectedDate={[selectedDate, null]}
91
- weeksInMonth={weeksInMonth}
92
- handleDayClick={onDayClick}
93
- tileContent={() => (tileContent ? tileContent(selectedDate, method) : null)}
94
- max={max}
95
- min={min}
96
- />
89
+ <DayTile
90
+ selectedDate={[selectedDate, null]}
91
+ weeksInMonth={weeksInMonth}
92
+ handleDayClick={onDayClick}
93
+ tileContent={() => (tileContent ? tileContent(selectedDate, method) : null)}
94
+ max={max}
95
+ min={min}
96
+ />
97
97
  )}
98
98
 
99
99
  {((method === 'month' || selectMode === 'month') && selectMode !== 'year') && (
@@ -1,7 +1,7 @@
1
1
  import { cn } from '@jk-core/utils';
2
- import { CalendarView } from 'Calendar/type';
3
- import ArrowIcon from 'assets/arrow.svg';
4
- import DropIcon from 'assets/drop-arrow.svg';
2
+ import ArrowIcon from '@/assets/arrow.svg';
3
+ import DropIcon from '@/assets/drop-arrow.svg';
4
+ import { CalendarView } from '@/Calendar/type';
5
5
  import styles from './DateLabel.module.scss';
6
6
 
7
7
  interface DateLabelProps {
@@ -26,14 +26,14 @@ export default function DateLabel({
26
26
  })}
27
27
  >
28
28
  {hideArrow !== 'all' && (
29
- <button
30
- className={styles.nav__button}
31
- type="button"
32
- onClick={() => onArrowClick('prev')}
33
- disabled={disabled('prev') || hideArrow === 'prev'}
34
- >
35
- {hideArrow !== 'prev' && <ArrowIcon />}
36
- </button>
29
+ <button
30
+ className={styles.nav__button}
31
+ type="button"
32
+ onClick={() => onArrowClick('prev')}
33
+ disabled={disabled('prev') || hideArrow === 'prev'}
34
+ >
35
+ {hideArrow !== 'prev' && <ArrowIcon />}
36
+ </button>
37
37
  )}
38
38
  {range ? (
39
39
  <div className={cn({
@@ -77,14 +77,14 @@ export default function DateLabel({
77
77
  </div>
78
78
  )}
79
79
  {hideArrow !== 'all' && (
80
- <button
81
- className={styles.nav__button}
82
- type="button"
83
- onClick={() => onArrowClick('next')}
84
- disabled={disabled('next') || hideArrow === 'next'}
85
- >
86
- {hideArrow !== 'next' && <ArrowIcon style={{ transform: 'rotate(180deg)' }} />}
87
- </button>
80
+ <button
81
+ className={styles.nav__button}
82
+ type="button"
83
+ onClick={() => onArrowClick('next')}
84
+ disabled={disabled('next') || hideArrow === 'next'}
85
+ >
86
+ {hideArrow !== 'next' && <ArrowIcon style={{ transform: 'rotate(180deg)' }} />}
87
+ </button>
88
88
  )}
89
89
  </div>
90
90
  );
@@ -1,8 +1,8 @@
1
- /* eslint-disable react/no-array-index-key */
2
- import isSameDay from 'Calendar/utils/isSameDay';
1
+
3
2
  import { cn } from '@jk-core/utils';
4
- import { CalendarRange } from 'Calendar/type';
5
- import isInRange from 'Calendar/utils/isInRange';
3
+ import { CalendarRange } from '@/Calendar/type';
4
+ import isInRange from '@/Calendar/utils/isInRange';
5
+ import isSameDay from '@/Calendar/utils/isSameDay';
6
6
  import styles from './DayTile.module.scss';
7
7
 
8
8
  const WEEKS = ['일', '월', '화', '수', '목', '금', '토'];
@@ -30,6 +30,14 @@ export default function DayTile({
30
30
  max, min, hideBefore = false, hideAfter = false, style, scroll,
31
31
  hoverDate, setHoverDate = () => { },
32
32
  }: Props) {
33
+ const handleDisabled = (date: Date): boolean => {
34
+ const compareDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
35
+ const compareMin = min ? new Date(min.getFullYear(), min.getMonth(), min.getDate()) : null;
36
+ const compareMax = max ? new Date(max.getFullYear(), max.getMonth(), max.getDate()) : null;
37
+
38
+ return !!((compareMin && compareDate < compareMin) || (compareMax && compareDate > compareMax));
39
+ };
40
+
33
41
  return (
34
42
  <div className={styles['day-tile']} style={style}>
35
43
  <div className={styles['day-tile__weeks']}>
@@ -57,7 +65,7 @@ export default function DayTile({
57
65
  [styles['day-tile__day--range']]: isInRange(day.date, selectedDate) || (!!selectedDate?.[0] && (isInRange(day.date, [selectedDate[0], hoverDate]))),
58
66
  })}
59
67
  type="button"
60
- disabled={(min && day.date < min) || (max && day.date > max)}
68
+ disabled={handleDisabled(day.date)}
61
69
  key={idx}
62
70
  onMouseOver={() => {
63
71
  if (!selectedDate?.[1] && !((min && day.date < min) || (max && day.date > max))) {
@@ -1,6 +1,6 @@
1
1
  import { cn } from '@jk-core/utils';
2
- import isSameDay from '../../utils/isSameDay';
3
- import { CalendarView } from '../../type';
2
+ import { CalendarView } from '@/Calendar/type';
3
+ import isSameDay from '@/Calendar/utils/isSameDay';
4
4
  import styles from './MonthTile.module.scss';
5
5
 
6
6
  const MONTHS = Array.from({ length: 12 }, (_, i) => i);
@@ -1,5 +1,5 @@
1
1
  import { cn } from '@jk-core/utils';
2
- import { CalendarView } from 'Calendar/type';
2
+ import { CalendarView } from '@/Calendar/type';
3
3
  import styles from './ViewSelector.module.scss';
4
4
 
5
5
  interface ViewSelectorProps {
@@ -10,12 +10,12 @@ export default function ViewSelector({ method, selectView }:ViewSelectorProps) {
10
10
  return (
11
11
  <div className={styles.view}>
12
12
  <div className={
13
- cn({
14
- [styles.view__block]: true,
15
- [styles['view__block--second']]: method === 'month',
16
- [styles['view__block--last']]: method === 'year',
17
- })
18
- }
13
+ cn({
14
+ [styles.view__block]: true,
15
+ [styles['view__block--second']]: method === 'month',
16
+ [styles['view__block--last']]: method === 'year',
17
+ })
18
+ }
19
19
  />
20
20
  <button
21
21
  className={cn({
@@ -1,7 +1,6 @@
1
1
  @use "../../../styles/mediaQuery.scss" as media;
2
2
 
3
3
  .year-tile {
4
- position: relative;
5
4
  min-height: 310px;
6
5
  padding: 5px;
7
6
  position: relative;
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useRef } from 'react';
2
2
  import { cn } from '@jk-core/utils';
3
- import { CalendarView } from '../../type';
3
+ import { CalendarView } from '@/Calendar/type';
4
4
  import styles from './YearTile.module.scss';
5
5
 
6
6
  interface Props {
@@ -17,7 +17,10 @@ const useCalendarNav = ({
17
17
  if (method === 'day') {
18
18
  if (direction === 'prev') {
19
19
  const prevMonth = new Date(viewDate.getFullYear(), viewDate.getMonth() - 1, 1);
20
- return prevMonth < min;
20
+ const isPrevMonthBeforeMin = prevMonth.getFullYear() < min.getFullYear()
21
+ || (prevMonth.getFullYear() === min.getFullYear()
22
+ && prevMonth.getMonth() < min.getMonth());
23
+ return isPrevMonthBeforeMin;
21
24
  }
22
25
  const nextMonth = new Date(viewDate.getFullYear(), viewDate.getMonth() + 1, 1);
23
26
  return nextMonth > max;
@@ -1,8 +1,8 @@
1
- /* eslint-disable react/no-array-index-key */
2
- import { CalendarRange, CalendarView } from './type';
1
+
3
2
  import RangeCalendar from './RangeCalendar';
4
- import SingleCalendar from './SingleCalendar';
5
3
  import ScrollCalendar from './ScrollCalendar';
4
+ import SingleCalendar from './SingleCalendar';
5
+ import { CalendarRange, CalendarView } from './type';
6
6
 
7
7
  import './index.scss';
8
8
 
@@ -1,4 +1,4 @@
1
- import { CalendarRange } from '../type';
1
+ import { CalendarRange } from '@/Calendar/type';
2
2
 
3
3
  const isInRange = (day: Date, range?:CalendarRange) => {
4
4
  if (!range?.[0] || !range?.[1]) return false;