@jk-core/components 0.0.64 → 0.0.65

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 CHANGED
@@ -1,3 +1,4 @@
1
+ (function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode(":root{--white: #ffffff;--black: #000000;--P-5: #eff5ff;--P-10: #d3e1fb;--P-20: #a7c4f7;--P-30: #7ca6f3;--P-40: #5089ef;--P-50: #246beb;--P-60: #1d56bc;--P-70: #16408d;--P-90: #07152f;--P-100: #000000;--S-5: #edf1f5;--S-10: #cdd7e4;--S-20: #b4c4d6;--S-30: #99b0cb;--S-40: #2a5c96;--S-50: #003675;--S-60: #002b5e;--S-70: #002036;--S-80: #00162f;--S-90: #000b17;--G-5: #f8f8f8;--G-10: #f0f0f0;--G-20: #e4e4e4;--G-30: #d8d8d8;--G-40: #c6c6c6;--G-50: #8e8e8e;--G-60: #717171;--G-70: #555555;--G-80: #2d2d2d;--G-90: #1d1d1d;--Point-5: #fdf2f3;--Point-10: #f8d6d8;--Point-20: #f5a3a8;--Point-30: #f1747c;--Point-40: #ec4651;--Point-50: #e71825;--Point-60: #b9131e;--Point-70: #8b0e16;--Point-80: #5c0a0f;--Point-90: #2e0507;--Warning-5: #fff8e9;--Warning-10: #ffeac1;--Warning-20: #ffe2a7;--Warning-30: #ffd47c;--Warning-40: #ffc550;--Warning-50: #ffb724;--Warning-60: #98690a;--Warning-70: #66490e;--Warning-80: #4d370b;--Warning-90: #332507;--Success-5: #eef7f0;--Success-10: #cee9d4;--Success-20: #b2dcbb;--Success-30: #8cca99;--Success-40: #33a14b;--Success-50: #008a1e;--Success-60: #006e18;--Success-70: #005312;--Success-80: #00370c;--Success-90: #002207;--Info-5: #e9f0ff;--Info-10: #d4e1ff;--Info-20: #a9c3ff;--Info-30: #7da4ff;--Info-40: #5286ff;--Info-50: #2768ff;--Info-60: #1f53cc;--Info-70: #173e99;--Info-80: #0c1f4d;--Info-90: #040a1a;--Red: #e40000;--Red2: #ffe4e4;--Green: #2fb400;--Green-2: #d7ffe0;--Orange: #ff8800;--Orange-5: #ffead1;--Orange-10: #ffdacc;--Orange-30: #ff8f66;--Orange-40: #ff6a33;--Orange-50: #ff4500;--Orange-60: #d53209;--Orange-70: #992900;--Orange-80: #661c00;--Orange-90: #330e00;--Modal-Shadow: #0000005a;--Modal-Background: #6666663a;--Calendar-Background: #ffffff}button{border:none;background-color:transparent;user-select:none;-webkit-user-select:none;-moz-user-select:none;cursor:pointer}.calendar{width:100%;min-width:300px;border:1px solid var(--G-30);border-radius:10px;overflow:hidden;color:var(--G-80);background-color:var(--white)}.calendar__close{display:flex;justify-content:flex-end;align-items:center;padding:5px 5px 0 0}.calendar__close svg{width:15px;height:15px;cursor:pointer}.view{position:relative;margin:0 auto;width:90%;display:flex;justify-content:space-between;align-items:center;background-color:#f3f4f8;border-radius:10px}.view__block{position:absolute;background-color:#fff;left:0;height:100%;border:2px solid var(--G-30);width:33.3%;border-radius:10px;transition:.3s}.view__block--second{left:33%}.view__block--last{left:66.6%}.view__selector{position:relative;height:40px;flex:1 0;display:flex;align-items:center;justify-content:center;color:var(--G-60);font-size:1em;font-weight:400}.view__selector--selected{color:var(--G-80);font-size:1em;font-weight:600}.nav{height:60px;display:flex;justify-content:space-between;align-items:center;padding:0 5px;border-bottom:1px solid var(--G-30);font-size:1.3em;font-weight:400}.nav__button{display:flex;align-items:center;justify-content:center;width:40px;height:40px;padding:10px;border-radius:100%}.nav__button:active{background-color:var(--G-30)}.nav__button:disabled{cursor:not-allowed;fill:var(--G-40);background-color:transparent}.nav__label{flex:1 0;display:flex;align-items:center;justify-content:space-around;font-size:1.1em;font-weight:400}.nav__label--date{display:flex;align-items:center;justify-content:center;border-radius:5px;padding:5px 10px;font-size:1.2em;font-weight:400}.nav__label--date svg{width:15px;height:15px}.nav__label--date-selected{background-color:var(--S-10)}.day-tile{min-height:310px;padding:5px;background-color:var(--white)}.day-tile__tile{display:flex;flex-direction:column;justify-content:space-between;gap:5px}.day-tile__weeks{display:flex;justify-content:space-between;font-weight:400;font-size:1em}.day-tile__weeks--date{flex:1 0;display:flex;justify-content:center;align-items:center;min-width:40px;padding:5px 0}.day-tile__week{display:flex;justify-content:space-between}.day-tile__day{display:flex;justify-content:center;align-items:center;flex-direction:column;width:100%;min-width:40px;min-height:40px;border-radius:40px;padding:5px;border:none;font-weight:400;font-size:1em}@media (min-width: 1396px){.day-tile__day:hover{background-color:var(--G-5)}}.day-tile__day:active{background-color:var(--G-10)}.day-tile__day--today{color:var(--P-50);font-weight:600}.day-tile__day--selected{background-color:var(--P-50)!important;color:var(--white)!important;font-weight:600}.day-tile__day--before{color:var(--G-40)}.day-tile__day--tile{border-radius:10px;gap:5px}.month-tile{min-height:310px;padding:5px;background-color:var(--white);display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(4,1fr);gap:5px}.month-tile__month{display:flex;flex-direction:column;justify-content:center;align-items:center;padding:5px;border-radius:10px}.month-tile__month svg{width:15px;height:15px;cursor:pointer}@media (min-width: 1396px){.month-tile__month:hover{background-color:var(--G-5)}}.month-tile__month:active{background-color:var(--G-10)}.month-tile__month--selected{background-color:var(--P-50)!important;color:var(--white)!important}.month-tile__month--today{color:var(--P-50);font-weight:600}.month-tile__month--tile{justify-content:flex-start;gap:5px}.year-tile{min-height:310px;padding:5px;background-color:var(--white);position:relative;height:310px;display:flex;flex-direction:column;align-items:center;overflow:auto;gap:10px}.year-tile::-webkit-scrollbar{display:none}.year-tile__blank{height:calc(50% - 40px);flex-shrink:0}.year-tile__blank:last-child{height:50%}.year-tile__year{min-width:50%;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:40px;border-radius:6px;flex-shrink:0;overflow:hidden;font-weight:400;font-size:1.2em}@media (min-width: 1396px){.year-tile__year:hover{background-color:var(--P-5)}}.year-tile__year:active{background-color:var(--P-10)}.year-tile__year--border{border:1px solid var(--G-30);background-color:var(--P-5)}@media (min-width: 1396px){.year-tile__year--border:hover{background-color:var(--P-10)}}.year-tile__year--year{height:40px;display:flex;align-items:center;justify-content:center}.year-tile__year--selected{color:var(--white);background-color:var(--P-50)!important}.year-tile__year--tile{display:flex;align-items:center;justify-content:center;min-height:40px;color:var(--G-80);width:100%;background-color:var(--white);border-top:var(--P-50)}")),document.head.appendChild(e)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})();
1
2
  import * as F from "react";
2
3
  import Me, { useRef as Oe, useEffect as gr, useState as oe } from "react";
3
4
  var ie = { exports: {} }, W = {};
@@ -1,3 +1,4 @@
1
+ (function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode(":root{--white: #ffffff;--black: #000000;--P-5: #eff5ff;--P-10: #d3e1fb;--P-20: #a7c4f7;--P-30: #7ca6f3;--P-40: #5089ef;--P-50: #246beb;--P-60: #1d56bc;--P-70: #16408d;--P-90: #07152f;--P-100: #000000;--S-5: #edf1f5;--S-10: #cdd7e4;--S-20: #b4c4d6;--S-30: #99b0cb;--S-40: #2a5c96;--S-50: #003675;--S-60: #002b5e;--S-70: #002036;--S-80: #00162f;--S-90: #000b17;--G-5: #f8f8f8;--G-10: #f0f0f0;--G-20: #e4e4e4;--G-30: #d8d8d8;--G-40: #c6c6c6;--G-50: #8e8e8e;--G-60: #717171;--G-70: #555555;--G-80: #2d2d2d;--G-90: #1d1d1d;--Point-5: #fdf2f3;--Point-10: #f8d6d8;--Point-20: #f5a3a8;--Point-30: #f1747c;--Point-40: #ec4651;--Point-50: #e71825;--Point-60: #b9131e;--Point-70: #8b0e16;--Point-80: #5c0a0f;--Point-90: #2e0507;--Warning-5: #fff8e9;--Warning-10: #ffeac1;--Warning-20: #ffe2a7;--Warning-30: #ffd47c;--Warning-40: #ffc550;--Warning-50: #ffb724;--Warning-60: #98690a;--Warning-70: #66490e;--Warning-80: #4d370b;--Warning-90: #332507;--Success-5: #eef7f0;--Success-10: #cee9d4;--Success-20: #b2dcbb;--Success-30: #8cca99;--Success-40: #33a14b;--Success-50: #008a1e;--Success-60: #006e18;--Success-70: #005312;--Success-80: #00370c;--Success-90: #002207;--Info-5: #e9f0ff;--Info-10: #d4e1ff;--Info-20: #a9c3ff;--Info-30: #7da4ff;--Info-40: #5286ff;--Info-50: #2768ff;--Info-60: #1f53cc;--Info-70: #173e99;--Info-80: #0c1f4d;--Info-90: #040a1a;--Red: #e40000;--Red2: #ffe4e4;--Green: #2fb400;--Green-2: #d7ffe0;--Orange: #ff8800;--Orange-5: #ffead1;--Orange-10: #ffdacc;--Orange-30: #ff8f66;--Orange-40: #ff6a33;--Orange-50: #ff4500;--Orange-60: #d53209;--Orange-70: #992900;--Orange-80: #661c00;--Orange-90: #330e00;--Modal-Shadow: #0000005a;--Modal-Background: #6666663a;--Calendar-Background: #ffffff}button{border:none;background-color:transparent;user-select:none;-webkit-user-select:none;-moz-user-select:none;cursor:pointer}.calendar{width:100%;min-width:300px;border:1px solid var(--G-30);border-radius:10px;overflow:hidden;color:var(--G-80);background-color:var(--white)}.calendar__close{display:flex;justify-content:flex-end;align-items:center;padding:5px 5px 0 0}.calendar__close svg{width:15px;height:15px;cursor:pointer}.view{position:relative;margin:0 auto;width:90%;display:flex;justify-content:space-between;align-items:center;background-color:#f3f4f8;border-radius:10px}.view__block{position:absolute;background-color:#fff;left:0;height:100%;border:2px solid var(--G-30);width:33.3%;border-radius:10px;transition:.3s}.view__block--second{left:33%}.view__block--last{left:66.6%}.view__selector{position:relative;height:40px;flex:1 0;display:flex;align-items:center;justify-content:center;color:var(--G-60);font-size:1em;font-weight:400}.view__selector--selected{color:var(--G-80);font-size:1em;font-weight:600}.nav{height:60px;display:flex;justify-content:space-between;align-items:center;padding:0 5px;border-bottom:1px solid var(--G-30);font-size:1.3em;font-weight:400}.nav__button{display:flex;align-items:center;justify-content:center;width:40px;height:40px;padding:10px;border-radius:100%}.nav__button:active{background-color:var(--G-30)}.nav__button:disabled{cursor:not-allowed;fill:var(--G-40);background-color:transparent}.nav__label{flex:1 0;display:flex;align-items:center;justify-content:space-around;font-size:1.1em;font-weight:400}.nav__label--date{display:flex;align-items:center;justify-content:center;border-radius:5px;padding:5px 10px;font-size:1.2em;font-weight:400}.nav__label--date svg{width:15px;height:15px}.nav__label--date-selected{background-color:var(--S-10)}.day-tile{min-height:310px;padding:5px;background-color:var(--white)}.day-tile__tile{display:flex;flex-direction:column;justify-content:space-between;gap:5px}.day-tile__weeks{display:flex;justify-content:space-between;font-weight:400;font-size:1em}.day-tile__weeks--date{flex:1 0;display:flex;justify-content:center;align-items:center;min-width:40px;padding:5px 0}.day-tile__week{display:flex;justify-content:space-between}.day-tile__day{display:flex;justify-content:center;align-items:center;flex-direction:column;width:100%;min-width:40px;min-height:40px;border-radius:40px;padding:5px;border:none;font-weight:400;font-size:1em}@media (min-width: 1396px){.day-tile__day:hover{background-color:var(--G-5)}}.day-tile__day:active{background-color:var(--G-10)}.day-tile__day--today{color:var(--P-50);font-weight:600}.day-tile__day--selected{background-color:var(--P-50)!important;color:var(--white)!important;font-weight:600}.day-tile__day--before{color:var(--G-40)}.day-tile__day--tile{border-radius:10px;gap:5px}.month-tile{min-height:310px;padding:5px;background-color:var(--white);display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(4,1fr);gap:5px}.month-tile__month{display:flex;flex-direction:column;justify-content:center;align-items:center;padding:5px;border-radius:10px}.month-tile__month svg{width:15px;height:15px;cursor:pointer}@media (min-width: 1396px){.month-tile__month:hover{background-color:var(--G-5)}}.month-tile__month:active{background-color:var(--G-10)}.month-tile__month--selected{background-color:var(--P-50)!important;color:var(--white)!important}.month-tile__month--today{color:var(--P-50);font-weight:600}.month-tile__month--tile{justify-content:flex-start;gap:5px}.year-tile{min-height:310px;padding:5px;background-color:var(--white);position:relative;height:310px;display:flex;flex-direction:column;align-items:center;overflow:auto;gap:10px}.year-tile::-webkit-scrollbar{display:none}.year-tile__blank{height:calc(50% - 40px);flex-shrink:0}.year-tile__blank:last-child{height:50%}.year-tile__year{min-width:50%;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:40px;border-radius:6px;flex-shrink:0;overflow:hidden;font-weight:400;font-size:1.2em}@media (min-width: 1396px){.year-tile__year:hover{background-color:var(--P-5)}}.year-tile__year:active{background-color:var(--P-10)}.year-tile__year--border{border:1px solid var(--G-30);background-color:var(--P-5)}@media (min-width: 1396px){.year-tile__year--border:hover{background-color:var(--P-10)}}.year-tile__year--year{height:40px;display:flex;align-items:center;justify-content:center}.year-tile__year--selected{color:var(--white);background-color:var(--P-50)!important}.year-tile__year--tile{display:flex;align-items:center;justify-content:center;min-height:40px;color:var(--G-80);width:100%;background-color:var(--white);border-top:var(--P-50)}")),document.head.appendChild(e)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})();
1
2
  (function(O,k){typeof exports=="object"&&typeof module<"u"?k(exports,require("react")):typeof define=="function"&&define.amd?define(["exports","react"],k):(O=typeof globalThis<"u"?globalThis:O||self,k(O.index={},O.React))})(this,function(O,k){"use strict";function Me(n){const i=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const a in n)if(a!=="default"){const p=Object.getOwnPropertyDescriptor(n,a);Object.defineProperty(i,a,p.get?p:{enumerable:!0,get:()=>n[a]})}}return i.default=n,Object.freeze(i)}const P=Me(k);var z={exports:{}},A={};/**
2
3
  * @license React
3
4
  * react-jsx-runtime.production.min.js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jk-core/components",
3
- "version": "0.0.64",
3
+ "version": "0.0.65",
4
4
  "type": "module",
5
5
  "main": "./dist/index.umd.cjs",
6
6
  "types": "./dist/index.d.ts",
@@ -26,9 +26,9 @@
26
26
  }
27
27
  },
28
28
  "files": [
29
- "dist",
30
- "dist/style.css",
31
- "src"
29
+ "./dist",
30
+ "./`src",
31
+ "./src/Calendar/scss"
32
32
  ],
33
33
  "keywords": [
34
34
  "utils",
@@ -81,6 +81,7 @@
81
81
  "typescript": "^5.5.3",
82
82
  "vite": "^5.4.8",
83
83
  "vite-plugin-checker": "^0.8.0",
84
+ "vite-plugin-css-injected-by-js": "^3.5.2",
84
85
  "vite-plugin-dts": "^4.2.3",
85
86
  "vite-plugin-sass-dts": "^1.3.29",
86
87
  "vite-plugin-svgr": "^4.2.0",
@@ -1,49 +0,0 @@
1
- /* eslint-disable max-len */
2
- /* eslint-disable react/no-array-index-key */
3
- import isSameDay from 'Calendar/utils/isSameDay';
4
-
5
- const WEEKS = ['일', '월', '화', '수', '목', '금', '토'];
6
-
7
- interface Props {
8
- selectedDate?: Date;
9
- weeksInMonth: {
10
- thisMonth: boolean;
11
- date: Date;
12
- }[][];
13
- tileContent?: () => React.ReactNode;
14
- handleDayClick: (day: Date) => void;
15
- }
16
- export default function DayTile({
17
- selectedDate, weeksInMonth, tileContent, handleDayClick,
18
- }: Props) {
19
- return (
20
- <div className="day-tile">
21
- <div className="day-tile__weeks">
22
- {WEEKS.map((week) => (<div className="day-tile__weeks--date" key={week}>{week}</div>))}
23
- </div>
24
-
25
- <div className="day-tile__tile">
26
- {weeksInMonth.map((week, index) => (
27
- <div key={index} className="day-tile__week">
28
- {week.map((day, idx) => (
29
- <button
30
- className={`day-tile__day
31
- ${isSameDay(day.date, new Date()) && 'day-tile__day--today'}
32
- ${!!selectedDate && isSameDay(day.date, selectedDate) && 'day-tile__day--selected'}
33
- ${!day.thisMonth && 'day-tile__day--before'}
34
- ${!!(tileContent && tileContent()) && 'day-tile__day--tile'}
35
- `}
36
- type="button"
37
- key={idx}
38
- onClick={() => handleDayClick(day.date)}
39
- >
40
- {day.date.getDate()}
41
- {tileContent && tileContent()}
42
- </button>
43
- ))}
44
- </div>
45
- ))}
46
- </div>
47
- </div>
48
- );
49
- }
@@ -1,34 +0,0 @@
1
- import isSameDay from '../../utils/isSameDay';
2
- import { CalendarView } from '../../type';
3
-
4
- const MONTHS = Array.from({ length: 12 }, (_, i) => i);
5
-
6
- interface Props {
7
- selectedDate?: Date;
8
- viewDate: Date;
9
- handleMonthClick: (month: number) => void;
10
- tileContent?: (date: Date, view:CalendarView) => React.ReactNode;
11
- }
12
- export default function MonthTile({
13
- selectedDate, viewDate, handleMonthClick, tileContent = () => false,
14
- }: Props) {
15
- return (
16
- <div className="month-tile">
17
- {MONTHS.map((month) => (
18
- <button
19
- className={`month-tile__month
20
- ${!!selectedDate && isSameDay(selectedDate, new Date(viewDate.getFullYear(), month), 'month') && 'month-tile--selected'}
21
- ${isSameDay(new Date(viewDate.getFullYear(), month), new Date(), 'month') && 'month-tile__month--today'}
22
- ${!!tileContent(new Date(viewDate.getFullYear(), month - 1, 1), 'month') && 'month-tile__month--tile'}`}
23
- type="button"
24
- key={month}
25
- onClick={() => handleMonthClick(month)}
26
- >
27
- <span>{`${month + 1}월`}</span>
28
- {!!tileContent(new Date(viewDate.getFullYear(), month - 1, 1), 'month')
29
- && tileContent(new Date(viewDate.getFullYear(), month - 1, 1), 'month')}
30
- </button>
31
- ))}
32
- </div>
33
- );
34
- }
@@ -1,54 +0,0 @@
1
- import { useEffect, useRef } from 'react';
2
- import { CalendarView } from '../../type';
3
-
4
- interface Props {
5
- selectedDate?: Date;
6
- onClick: (year: number) => void;
7
- tileContent?: (date: Date, view:CalendarView) => React.ReactNode;
8
- }
9
-
10
- const YEARS = Array.from({ length: 100 }, (_, i) => i + 2000);
11
-
12
- export default function YearTile({
13
- selectedDate, onClick,
14
- tileContent = () => false,
15
- }: Props) {
16
- const wrapperRef = useRef<HTMLDivElement>(null);
17
- const selectedRef = useRef<HTMLButtonElement>(null);
18
-
19
- useEffect(() => {
20
- const selectedElement = selectedRef.current;
21
- const wrapperElement = wrapperRef.current;
22
- if (!selectedElement || !wrapperElement) return;
23
-
24
- const { clientHeight } = wrapperElement;
25
- const { offsetTop, clientHeight: selectedHeight } = selectedElement;
26
-
27
- wrapperElement.scrollTo({
28
- top: offsetTop - clientHeight / 2 + selectedHeight,
29
- });
30
- }, []);
31
-
32
- return (
33
- <div className="year-tile" ref={wrapperRef}>
34
- <div className="year-tile__blank" />
35
- {YEARS.map((year) => (
36
- <button
37
- className={`year-tile__year ${selectedDate && selectedDate.getFullYear() === year && 'year-tile__year--selected'} ${tileContent(new Date(year, 1, 1), 'year') && 'year-tile__year--border'}`}
38
- key={year}
39
- type="button"
40
- ref={!!selectedDate && selectedDate.getFullYear() === year ? selectedRef : null}
41
- onClick={() => onClick(year)}
42
- >
43
- <span className="year-tile__year--year">{year}</span>
44
- {tileContent(new Date(year, 1, 1), 'year') && (
45
- <div className="year-tile__year--tile">
46
- {tileContent(new Date(year, 1, 1), 'year')}
47
- </div>
48
- )}
49
- </button>
50
- ))}
51
- <div className="year-tile__blank" />
52
- </div>
53
- );
54
- }
@@ -1,5 +0,0 @@
1
- @mixin body {
2
- min-height: 310px;
3
- padding: 5px;
4
- background-color: var(--white);
5
- }
@@ -1,80 +0,0 @@
1
- import { CalendarView } from '../type';
2
-
3
- interface Props {
4
- method: CalendarView;
5
- selectMode: CalendarView;
6
- date: Date;
7
- setDate:(date: Date) => void;
8
- min: Date;
9
- max: Date;
10
- }
11
- const useCalendarNav = ({
12
- method, selectMode, date, setDate, min, max,
13
- }:Props) => {
14
- const disabled = (direction: 'prev' | 'next') => {
15
- if (selectMode === 'year' || method !== selectMode) return true;
16
-
17
- if (method === 'day') {
18
- if (direction === 'prev') {
19
- const prevMonth = new Date(date.getFullYear(), date.getMonth() - 1, 1);
20
- return prevMonth < min;
21
- }
22
- const nextMonth = new Date(date.getFullYear(), date.getMonth() + 1, 1);
23
- return nextMonth > max;
24
- }
25
-
26
- if (method === 'month') {
27
- if (direction === 'prev') {
28
- const prevYear = new Date(date.getFullYear() - 1, date.getMonth(), 1);
29
- return prevYear < min;
30
- }
31
- const nextYear = new Date(date.getFullYear() + 1, date.getMonth(), 1);
32
- return nextYear > max;
33
- }
34
-
35
- if (method === 'year') {
36
- if (direction === 'prev') {
37
- const prevDecade = new Date(date.getFullYear() - 10, date.getMonth(), 1);
38
- return prevDecade < min;
39
- }
40
- const nextDecade = new Date(date.getFullYear() + 10, date.getMonth(), 1);
41
- return nextDecade > max;
42
- }
43
-
44
- return false;
45
- };
46
-
47
- const onArrowClick = (direction: 'prev' | 'next') => {
48
- const offset = direction === 'prev' ? -1 : 1;
49
- const minDate = new Date(2000, 0, 1);
50
- const maxDate = new Date(2099, 11, 31);
51
-
52
- if (method === 'day') {
53
- const newDate = new Date(date.getFullYear(), date.getMonth() + offset, 1);
54
-
55
- if (newDate >= minDate && newDate <= maxDate) {
56
- setDate(newDate);
57
- }
58
- }
59
-
60
- if (method === 'month') {
61
- const newDate = new Date(date.getFullYear() + offset, date.getMonth(), 1);
62
-
63
- if (newDate >= minDate && newDate <= maxDate) {
64
- setDate(newDate);
65
- }
66
- }
67
-
68
- if (method === 'year') {
69
- const newDate = new Date(date.getFullYear() + offset * 10, date.getMonth(), 1);
70
-
71
- if (newDate >= minDate && newDate <= maxDate) {
72
- setDate(newDate);
73
- }
74
- }
75
- };
76
-
77
- return { disabled, onArrowClick };
78
- };
79
-
80
- export default useCalendarNav;
@@ -1,47 +0,0 @@
1
- import { CalendarView } from '../type';
2
-
3
- interface UseDateSelectProps {
4
- viewDate: Date;
5
- setViewDate: (date: Date) => void;
6
- method: CalendarView;
7
- setSelectMode:(mode:CalendarView) => void;
8
- onChange:(date:Date) => void;
9
-
10
- }
11
-
12
- const useDateSelect = ({
13
- viewDate, setViewDate, onChange, setSelectMode, method,
14
- }: UseDateSelectProps) => {
15
- const min = new Date(2000, 0, 1);
16
- const max = new Date(2099, 11, 31);
17
-
18
- const onDayClick = (day: Date) => {
19
- if (day < min || day > max) {
20
- return;
21
- }
22
-
23
- setViewDate(day);
24
- onChange(day);
25
- };
26
-
27
- const onMonthClick = (month:number) => {
28
- const newDate = new Date(viewDate.getFullYear(), month, 1);
29
-
30
- setViewDate(newDate);
31
-
32
- if (method !== 'month') setSelectMode(method);
33
- if (method === 'month') onChange(newDate);
34
- };
35
-
36
- const onYearClick = (year:number) => {
37
- const newDate = new Date(year, 0, 1);
38
-
39
- setViewDate(newDate);
40
- if (method !== 'year') setSelectMode(method);
41
- if (method === 'year') onChange(newDate);
42
- };
43
-
44
- return { onDayClick, onMonthClick, onYearClick };
45
- };
46
-
47
- export default useDateSelect;
@@ -1,143 +0,0 @@
1
- /* eslint-disable react/no-array-index-key */
2
- import { useState } from 'react';
3
- import CloseIcon from '../assets/close.svg';
4
- import DropIcon from '../assets/drop-arrow.svg';
5
- import { CalendarView } from './type';
6
- import getWeeksInMonth from './utils/getWeeksInMonth';
7
- import DayTile from './components/DayTile';
8
- import MonthTile from './components/MonthTile';
9
- import YearTile from './components/YearTile';
10
- import useCalendarNav from './hooks/useCalendarNav';
11
- import useDateSelect from './hooks/useDateSelect';
12
-
13
- import './scss/main.scss';
14
-
15
- interface CalendarProps {
16
- date?: Date;
17
- view?: CalendarView;
18
- tileContent?: (date: Date | undefined, view: CalendarView) => React.ReactNode;
19
- onChange?:(date:Date)=>void;
20
- min?: Date;
21
- max?: Date;
22
- onClose?:()=>void;
23
- }
24
-
25
- export default function Calendar({
26
- date: selectedDate, view, tileContent, onChange = () => { }, onClose,
27
- min = new Date(2000, 0, 1), max = new Date(2099, 11, 31),
28
- }:CalendarProps) {
29
- const [viewDate, setViewDate] = useState<Date>(selectedDate || new Date());
30
- const [method, setMethod] = useState<CalendarView>(view || 'day');
31
- const [selectMode, setSelectMode] = useState<CalendarView>('day');
32
- const weeksInMonth = getWeeksInMonth(viewDate);
33
- const { onDayClick, onMonthClick, onYearClick } = useDateSelect({
34
- viewDate,
35
- setViewDate: (data) => setViewDate(data),
36
- onChange,
37
- setSelectMode,
38
- method,
39
- });
40
- const { disabled, onArrowClick } = useCalendarNav({
41
- method, selectMode, date: viewDate, setDate: setViewDate, min, max,
42
- });
43
-
44
- return (
45
- <div className="calendar">
46
- <div className="calendar__close">
47
- {onClose && (
48
- <CloseIcon onClick={onClose} />
49
- )}
50
- </div>
51
- {/* 일/월/년 선택 버튼 */}
52
- <div className="view">
53
- <div className={`view__block ${method === 'month' && 'view__block--second'} ${method === 'year' && 'view__block--last'}`} />
54
- <button
55
- className={`view__selector ${method === 'day' && 'view__selector--selected'}`}
56
- type="button"
57
- onClick={() => { setMethod('day'); setSelectMode('day'); }}
58
- >일
59
- </button>
60
- <button
61
- className={`view__selector ${method === 'month' && 'view__selector--selected'}`}
62
- type="button"
63
- onClick={() => { setMethod('month'); setSelectMode('month'); }}
64
- >월
65
- </button>
66
- <button
67
- className={`view__selector ${method === 'year' && 'view__selector--selected'}`}
68
- type="button"
69
- onClick={() => { setMethod('year'); setSelectMode('year'); }}
70
- >년
71
- </button>
72
- </div>
73
-
74
- <div className="nav">
75
- <button
76
- className="nav__button"
77
- type="button"
78
- onClick={() => onArrowClick('prev')}
79
- disabled={disabled('prev')}
80
- >
81
- ◀︎
82
- </button>
83
- <div className="nav__label">
84
- {method === 'year' && '연도 선택'}
85
- {method !== 'year' && (
86
- <button
87
- className={`nav__label--date ${selectMode === 'year' && 'nav__label--date-selected'}`}
88
- type="button"
89
- onClick={() => setSelectMode('year')}
90
- >
91
- {`${viewDate.getFullYear()}년`}<DropIcon />
92
- </button>
93
- )}
94
- {method === 'day' && (
95
- <button
96
- className={`nav__label--date ${selectMode === 'month' && 'nav__label--date-selected'}`}
97
- type="button"
98
- onClick={() => setSelectMode('month')}
99
- >
100
- {`${viewDate.getMonth() + 1}월`}<DropIcon />
101
- </button>
102
- )}
103
- </div>
104
- <button
105
- className="nav__button"
106
- type="button"
107
- onClick={() => onArrowClick('next')}
108
- disabled={disabled('next')}
109
- >►
110
- </button>
111
- </div>
112
-
113
- {(method === 'day' && selectMode === 'day') && (
114
- <DayTile
115
- selectedDate={selectedDate}
116
- weeksInMonth={weeksInMonth}
117
- // selectRange={selectRange}
118
- // selectedRange={selectedRange}
119
- handleDayClick={onDayClick}
120
- tileContent={() => (tileContent ? tileContent(selectedDate, method) : null)}
121
- />
122
- )}
123
-
124
- {((method === 'month' || selectMode === 'month') && selectMode !== 'year') && (
125
- <MonthTile
126
- selectedDate={selectedDate}
127
- viewDate={viewDate}
128
- handleMonthClick={onMonthClick}
129
- tileContent={tileContent}
130
-
131
- />
132
- )}
133
-
134
- {(method === 'year' || selectMode === 'year') && (
135
- <YearTile
136
- selectedDate={selectedDate}
137
- onClick={onYearClick}
138
- tileContent={tileContent}
139
- />
140
- )}
141
- </div>
142
- );
143
- }
@@ -1,129 +0,0 @@
1
- .calendar {
2
- width: 100%;
3
- min-width: 300px;
4
- border: 1px solid var(--G-30);
5
- border-radius: 10px;
6
- overflow: hidden;
7
- color: var(--G-80);
8
- background-color: var(--white);
9
- }
10
-
11
- .calendar__close {
12
- display: flex;
13
- justify-content: flex-end;
14
- align-items: center;
15
- padding: 5px 5px 0 0;
16
- }
17
-
18
- .calendar__close svg {
19
- width: 15px;
20
- height: 15px;
21
- cursor: pointer;
22
- }
23
-
24
- .view {
25
- position: relative;
26
- margin: 0 auto;
27
- width: 90%;
28
- display: flex;
29
- justify-content: space-between;
30
- align-items: center;
31
- background-color: #f3f4f8;
32
- border-radius: 10px;
33
- }
34
-
35
- .view__block {
36
- position: absolute;
37
- background-color: #fff;
38
- left: 0;
39
- height: 100%;
40
- border: 2px solid var(--G-30);
41
- width: 33.3%;
42
- border-radius: 10px;
43
- transition: 0.3s;
44
- }
45
-
46
- .view__block--second {
47
- left: 33%;
48
- }
49
-
50
- .view__block--last {
51
- left: 66.6%;
52
- }
53
-
54
- .view__selector {
55
- position: relative;
56
- height: 40px;
57
- flex: 1 0;
58
- display: flex;
59
- align-items: center;
60
- justify-content: center;
61
- color: var(--G-60);
62
- font-size: 1em;
63
- font-weight: 400;
64
- }
65
-
66
- .view__selector--selected {
67
- color: var(--G-80);
68
- font-size: 1em;
69
- font-weight: 600;
70
- }
71
-
72
- .nav {
73
- height: 60px;
74
- display: flex;
75
- justify-content: space-between;
76
- align-items: center;
77
- padding: 0 5px;
78
- border-bottom: 1px solid var(--G-30);
79
- font-size: 1.3em;
80
- font-weight: 400;
81
- }
82
-
83
- .nav__button {
84
- display: flex;
85
- align-items: center;
86
- justify-content: center;
87
- width: 40px;
88
- height: 40px;
89
- padding: 10px;
90
- border-radius: 100%;
91
- }
92
-
93
- .nav__button:active {
94
- background-color: var(--G-30);
95
- }
96
-
97
- .nav__button:disabled {
98
- cursor: not-allowed;
99
- fill: var(--G-40);
100
- background-color: transparent;
101
- }
102
-
103
- .nav__label {
104
- flex: 1 0;
105
- display: flex;
106
- align-items: center;
107
- justify-content: space-around;
108
- font-size: 1.1em;
109
- font-weight: 400;
110
- }
111
-
112
- .nav__label--date {
113
- display: flex;
114
- align-items: center;
115
- justify-content: center;
116
- border-radius: 5px;
117
- padding: 5px 10px;
118
- font-size: 1.2em;
119
- font-weight: 400;
120
- }
121
-
122
- .nav__label--date svg {
123
- width: 15px;
124
- height: 15px;
125
- }
126
-
127
- .nav__label--date-selected {
128
- background-color: var(--S-10);
129
- }
@@ -1,6 +0,0 @@
1
- export type CalendarView = 'day' | 'month' | 'year';
2
-
3
- export interface CalendarRange {
4
- start: Date | null;
5
- end: Date | null;
6
- }
@@ -1,45 +0,0 @@
1
- const getWeeksInMonth = (viewDate:Date) => {
2
- const startOfMonth = new Date(viewDate.getFullYear(), viewDate.getMonth(), 1);
3
- const endOfMonth = new Date(viewDate.getFullYear(), viewDate.getMonth() + 1, 0);
4
- const weeks = [];
5
- let currentWeek = [];
6
-
7
- const startDayOfWeek = startOfMonth.getDay();
8
- if (startDayOfWeek !== 0) {
9
- const prevMonthEnd = new Date(startOfMonth);
10
- prevMonthEnd.setDate(0);
11
- for (let i = startDayOfWeek - 1; i >= 0; i -= 1) {
12
- const prevDate = new Date(prevMonthEnd);
13
- prevDate.setDate(prevMonthEnd.getDate() - i);
14
- currentWeek.push({ thisMonth: false, date: prevDate });
15
- }
16
- }
17
-
18
- const currentDate = new Date(startOfMonth);
19
-
20
- while (currentDate <= endOfMonth) {
21
- currentWeek.push({ thisMonth: true, date: new Date(currentDate) });
22
- if (currentDate.getDay() === 6) {
23
- weeks.push(currentWeek);
24
- currentWeek = [];
25
- }
26
- currentDate.setDate(currentDate.getDate() + 1);
27
- }
28
-
29
- const endDayOfWeek = endOfMonth.getDay();
30
- if (endDayOfWeek !== 6) {
31
- for (let i = 1; i <= 6 - endDayOfWeek; i += 1) {
32
- const nextDate = new Date(endOfMonth);
33
- nextDate.setDate(endOfMonth.getDate() + i);
34
- currentWeek.push({ thisMonth: false, date: nextDate });
35
- }
36
- }
37
-
38
- if (currentWeek.length > 0) {
39
- weeks.push(currentWeek);
40
- }
41
-
42
- return weeks;
43
- };
44
-
45
- export default getWeeksInMonth;
@@ -1,8 +0,0 @@
1
- import { CalendarRange } from '../type';
2
-
3
- const isInRange = (day: Date, range:CalendarRange) => {
4
- if (!range.start || !range.end) return false;
5
- return day > range.start && day < range.end;
6
- };
7
-
8
- export default isInRange;
@@ -1,21 +0,0 @@
1
- import { CalendarView } from '../type';
2
-
3
- const isSameDay = (date1: Date | null, date2: Date | null, view: CalendarView = 'day'): boolean => {
4
- if (date1 === null || date2 === null) return false;
5
-
6
- switch (view) {
7
- case 'day':
8
- return date1.getFullYear() === date2.getFullYear()
9
- && date1.getMonth() === date2.getMonth()
10
- && date1.getDate() === date2.getDate();
11
- case 'month':
12
- return date1.getFullYear() === date2.getFullYear()
13
- && date1.getMonth() === date2.getMonth();
14
- case 'year':
15
- return date1.getFullYear() === date2.getFullYear();
16
- default:
17
- return false;
18
- }
19
- };
20
-
21
- export default isSameDay;
@@ -1,16 +0,0 @@
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="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="#000000" fill="#000000" stroke-width="0.696">
5
-
6
- <g id="SVGRepo_bgCarrier" stroke-width="0"/>
7
-
8
- <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
9
-
10
- <g id="SVGRepo_iconCarrier">
11
-
12
- <path fill-rule="evenodd" clip-rule="evenodd" d="M19.207 6.207a1 1 0 0 0-1.414-1.414L12 10.586 6.207 4.793a1 1 0 0 0-1.414 1.414L10.586 12l-5.793 5.793a1 1 0 1 0 1.414 1.414L12 13.414l5.793 5.793a1 1 0 0 0 1.414-1.414L13.414 12l5.793-5.793z" />
13
-
14
- </g>
15
-
16
- </svg>
@@ -1,3 +0,0 @@
1
- <svg width="25" height="25" viewBox="0 0 20 20" fill="none" stroke="#2D2D2D" xmlns="http://www.w3.org/2000/svg">
2
- <path d="M5 7.5L10 13L15 7.5" stroke-width="1.4" stroke-linecap="round"/>
3
- </svg>
package/src/index.tsx DELETED
@@ -1,3 +0,0 @@
1
- import Calendar from './Calendar';
2
-
3
- export { Calendar };
package/src/main.tsx DELETED
@@ -1,6 +0,0 @@
1
- import { createRoot } from 'react-dom/client';
2
- import Calendar from './Calendar';
3
-
4
- createRoot(document.getElementById('root')!).render(
5
- <Calendar date={new Date()} />,
6
- );
@@ -1,91 +0,0 @@
1
- :root {
2
- --white: #ffffff;
3
- --black: #000000;
4
- --P-5: #eff5ff;
5
- --P-10: #d3e1fb;
6
- --P-20: #a7c4f7;
7
- --P-30: #7ca6f3;
8
- --P-40: #5089ef;
9
- --P-50: #246beb;
10
- --P-60: #1d56bc;
11
- --P-70: #16408d;
12
- --P-90: #07152f;
13
- --P-100: #000000;
14
- --S-5: #edf1f5;
15
- --S-10: #cdd7e4;
16
- --S-20: #b4c4d6;
17
- --S-30: #99b0cb;
18
- --S-40: #2a5c96;
19
- --S-50: #003675;
20
- --S-60: #002b5e;
21
- --S-70: #002036;
22
- --S-80: #00162f;
23
- --S-90: #000b17;
24
- --G-5: #f8f8f8;
25
- --G-10: #f0f0f0;
26
- --G-20: #e4e4e4;
27
- --G-30: #d8d8d8;
28
- --G-40: #c6c6c6;
29
- --G-50: #8e8e8e;
30
- --G-60: #717171;
31
- --G-70: #555555;
32
- --G-80: #2d2d2d;
33
- --G-90: #1d1d1d;
34
- --Point-5: #fdf2f3;
35
- --Point-10: #f8d6d8;
36
- --Point-20: #f5a3a8;
37
- --Point-30: #f1747c;
38
- --Point-40: #ec4651;
39
- --Point-50: #e71825;
40
- --Point-60: #b9131e;
41
- --Point-70: #8b0e16;
42
- --Point-80: #5c0a0f;
43
- --Point-90: #2e0507;
44
- --Warning-5: #fff8e9;
45
- --Warning-10: #ffeac1;
46
- --Warning-20: #ffe2a7;
47
- --Warning-30: #ffd47c;
48
- --Warning-40: #ffc550;
49
- --Warning-50: #ffb724;
50
- --Warning-60: #98690a;
51
- --Warning-70: #66490e;
52
- --Warning-80: #4d370b;
53
- --Warning-90: #332507;
54
- --Success-5: #eef7f0;
55
- --Success-10: #cee9d4;
56
- --Success-20: #b2dcbb;
57
- --Success-30: #8cca99;
58
- --Success-40: #33a14b;
59
- --Success-50: #008a1e;
60
- --Success-60: #006e18;
61
- --Success-70: #005312;
62
- --Success-80: #00370c;
63
- --Success-90: #002207;
64
- --Info-5: #e9f0ff;
65
- --Info-10: #d4e1ff;
66
- --Info-20: #a9c3ff;
67
- --Info-30: #7da4ff;
68
- --Info-40: #5286ff;
69
- --Info-50: #2768ff;
70
- --Info-60: #1f53cc;
71
- --Info-70: #173e99;
72
- --Info-80: #0c1f4d;
73
- --Info-90: #040a1a;
74
- --Red: #e40000;
75
- --Red2: #ffe4e4;
76
- --Green: #2fb400;
77
- --Green-2: #d7ffe0;
78
- --Orange: #ff8800;
79
- --Orange-5: #ffead1;
80
- --Orange-10: #ffdacc;
81
- --Orange-30: #ff8f66;
82
- --Orange-40: #ff6a33;
83
- --Orange-50: #ff4500;
84
- --Orange-60: #d53209;
85
- --Orange-70: #992900;
86
- --Orange-80: #661c00;
87
- --Orange-90: #330e00;
88
- --Modal-Shadow: #0000005a;
89
- --Modal-Background: #6666663a;
90
- --Calendar-Background: #ffffff;
91
- }
@@ -1,22 +0,0 @@
1
- // 스크롤바 너비 14px 추가
2
- $pc: 1396px;
3
- $tablet: 1395px;
4
- $mobile: 774px;
5
-
6
- @mixin pc {
7
- @media (min-width: $pc) {
8
- @content;
9
- }
10
- }
11
-
12
- @mixin tablet {
13
- @media (max-width: $tablet) {
14
- @content;
15
- }
16
- }
17
-
18
- @mixin mobile {
19
- @media (max-width: $mobile) {
20
- @content;
21
- }
22
- }
package/src/svg.d.ts DELETED
@@ -1,6 +0,0 @@
1
- // svg를 ReactComponent처럼 사용하기 위해 선언
2
- declare module '*.svg' {
3
- import { HTMLAttributes } from 'react';
4
-
5
- export default React.Component<HTMLAttributes<HTMLDivElement>>;
6
- }
package/src/vite-env.d.ts DELETED
@@ -1 +0,0 @@
1
- /// <reference types="vite/client" />