datepicker-pashuk 1.0.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.
- package/README.md +161 -0
- package/dist/index.cjs.js +1163 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.esm.js +1158 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/types/.storybook/main.d.ts +3 -0
- package/dist/types/.storybook/preview.d.ts +4 -0
- package/dist/types/jest.setup.d.ts +1 -0
- package/dist/types/src/entities/buggyComponent/ui/BuggyComponent.d.ts +1 -0
- package/dist/types/src/entities/calendar/assets/NextIcon.d.ts +1 -0
- package/dist/types/src/entities/calendar/assets/PreviousIcon.d.ts +1 -0
- package/dist/types/src/entities/calendar/assets/index.d.ts +2 -0
- package/dist/types/src/entities/calendar/lib/decorators/BaseCalendarEngine.d.ts +9 -0
- package/dist/types/src/entities/calendar/lib/decorators/CalenderDecorator.d.ts +7 -0
- package/dist/types/src/entities/calendar/lib/decorators/FromMondayDecorator.d.ts +7 -0
- package/dist/types/src/entities/calendar/lib/decorators/HolidayDecorator.d.ts +7 -0
- package/dist/types/src/entities/calendar/lib/decorators/MinMaxDecorator.d.ts +12 -0
- package/dist/types/src/entities/calendar/lib/decorators/RangeCalenderDecorator.d.ts +23 -0
- package/dist/types/src/entities/calendar/lib/decorators/TodoDecorator.d.ts +13 -0
- package/dist/types/src/entities/calendar/lib/decorators/ViewByWeekDecorator.d.ts +8 -0
- package/dist/types/src/entities/calendar/lib/decorators/WeekdayDecorator.d.ts +6 -0
- package/dist/types/src/entities/calendar/lib/helpers/generateMonthArray.d.ts +4 -0
- package/dist/types/src/entities/calendar/lib/helpers/generateYearArray.d.ts +1 -0
- package/dist/types/src/entities/calendar/lib/helpers/isMinMaxValue.d.ts +1 -0
- package/dist/types/src/entities/calendar/lib/helpers/isSameDates.d.ts +1 -0
- package/dist/types/src/entities/calendar/lib/helpers/validationInput.d.ts +4 -0
- package/dist/types/src/entities/calendar/lib/tests/constants.d.ts +67 -0
- package/dist/types/src/entities/calendar/model/constants.d.ts +7 -0
- package/dist/types/src/entities/calendar/model/service.d.ts +35 -0
- package/dist/types/src/entities/calendar/model/storageService.d.ts +7 -0
- package/dist/types/src/entities/calendar/model/types.d.ts +75 -0
- package/dist/types/src/entities/calendar/ui/BaseCalendar/BaseCalendar.d.ts +2 -0
- package/dist/types/src/entities/calendar/ui/CalendarCell/CalendarCell.d.ts +2 -0
- package/dist/types/src/entities/calendar/ui/CalendarGrid/CalendarGrid.d.ts +2 -0
- package/dist/types/src/entities/calendar/ui/CalendarHeader/CalendarHeader.d.ts +2 -0
- package/dist/types/src/features/calendar/Calendar.d.ts +2 -0
- package/dist/types/src/features/calendar/model/constants.d.ts +5 -0
- package/dist/types/src/features/calendarWithInput/CalendarWithInput.d.ts +2 -0
- package/dist/types/src/features/calendarWithInput/model/types.d.ts +5 -0
- package/dist/types/src/features/calenderWithTodo/lib/helperTests.d.ts +91 -0
- package/dist/types/src/features/calenderWithTodo/lib/useCalendarPortal.d.ts +8 -0
- package/dist/types/src/features/calenderWithTodo/ui/CalenderWithTodo.d.ts +2 -0
- package/dist/types/src/features/rangeCalender/lib/useDateRangePicker.d.ts +23 -0
- package/dist/types/src/features/rangeCalender/model/types.d.ts +5 -0
- package/dist/types/src/features/rangeCalender/ui/RangeCalender.d.ts +2 -0
- package/dist/types/src/index.d.ts +5 -0
- package/dist/types/src/shared/lib/getThemed.d.ts +2 -0
- package/dist/types/src/shared/lib/useCalendarService.d.ts +6 -0
- package/dist/types/src/shared/model/constants.d.ts +3 -0
- package/dist/types/src/shared/model/helperTest.d.ts +33 -0
- package/dist/types/src/shared/ui/ErrorBoundary/types/types.d.ts +7 -0
- package/dist/types/src/shared/ui/ErrorBoundary/ui/ErrorBoundary.d.ts +9 -0
- package/dist/types/src/shared/ui/Input/lib/maskDate.d.ts +1 -0
- package/dist/types/src/shared/ui/Input/model/constants.d.ts +4 -0
- package/dist/types/src/shared/ui/Input/model/types.d.ts +9 -0
- package/dist/types/src/shared/ui/Input/ui/Input.d.ts +2 -0
- package/dist/types/src/shared/ui/Input/ui/InputWithState.d.ts +2 -0
- package/dist/types/src/shared/ui/Input/ui/assets/CalendarIcon.d.ts +1 -0
- package/dist/types/src/shared/ui/Input/ui/assets/CloseIcon.d.ts +1 -0
- package/dist/types/src/shared/ui/Input/ui/assets/index.d.ts +2 -0
- package/dist/types/src/shared/ui/Portal/model/types.d.ts +18 -0
- package/dist/types/src/shared/ui/Portal/ui/Portal.d.ts +2 -0
- package/package.json +93 -0
|
@@ -0,0 +1,1163 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var reactDom = require('react-dom');
|
|
6
|
+
|
|
7
|
+
function styleInject(css, ref) {
|
|
8
|
+
if ( ref === void 0 ) ref = {};
|
|
9
|
+
var insertAt = ref.insertAt;
|
|
10
|
+
|
|
11
|
+
if (!css || typeof document === 'undefined') { return; }
|
|
12
|
+
|
|
13
|
+
var head = document.head || document.getElementsByTagName('head')[0];
|
|
14
|
+
var style = document.createElement('style');
|
|
15
|
+
style.type = 'text/css';
|
|
16
|
+
|
|
17
|
+
if (insertAt === 'top') {
|
|
18
|
+
if (head.firstChild) {
|
|
19
|
+
head.insertBefore(style, head.firstChild);
|
|
20
|
+
} else {
|
|
21
|
+
head.appendChild(style);
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
head.appendChild(style);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (style.styleSheet) {
|
|
28
|
+
style.styleSheet.cssText = css;
|
|
29
|
+
} else {
|
|
30
|
+
style.appendChild(document.createTextNode(css));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var css_248z$9 = "@import url(\"https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&family=Open+Sans:ital,wdth,wght@0,75..100,300..800;1,75..100,300..800&display=swap\");a,article,aside,audio,body,div,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,html,img,label,legend,li,mark,menu,nav,ol,output,p,ruby,section,span,summary,time,ul,video{border:0;font-size:100%;font:inherit;margin:0;padding:0;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:\"\";content:none}table{border-collapse:collapse;border-spacing:0}body{font-family:Open Sans,sans-serif}";
|
|
35
|
+
styleInject(css_248z$9);
|
|
36
|
+
|
|
37
|
+
const NextIcon = () => {
|
|
38
|
+
return (jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("path", { d: "M4.27337 4L3.33337 4.94L6.38671 8L3.33337 11.06L4.27337 12L8.27337 8L4.27337 4Z", fill: "currentColor" }), jsxRuntime.jsx("path", { d: "M8.66668 4L7.72668 4.94L10.78 8L7.72668 11.06L8.66668 12L12.6667 8L8.66668 4Z", fill: "currentColor" })] }));
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const PreviousIcon = () => {
|
|
42
|
+
return (jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("path", { d: "M11.7266 12L12.6666 11.06L9.61329 8L12.6666 4.94L11.7266 4L7.72663 8L11.7266 12Z", fill: "currentColor" }), jsxRuntime.jsx("path", { d: "M7.33332 12L8.27332 11.06L5.21998 8L8.27331 4.94L7.33331 4L3.33332 8L7.33332 12Z", fill: "currentColor" })] }));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
var css_248z$8 = ".CalendarCell-module_cell__-bATh{align-items:center;color:#333;display:flex;justify-content:center}[data-theme=dark] .CalendarCell-module_cell__-bATh{color:#f1f1f1}.CalendarCell-module_cell__-bATh{background-color:transparent;border-radius:8px;cursor:pointer;height:32px;width:32px}.CalendarCell-module_cell__-bATh:hover{background-color:#aaa}.CalendarCell-module_cell__day-number__OwQ5i{font-size:13px;font-weight:600}.CalendarCell-module_cell__todo-indicator__RR2HA{color:#2f80ed;font-size:24px;margin-left:16px;position:absolute}.CalendarCell-module_cell--other-month__K4F-o,[data-theme=dark] .CalendarCell-module_cell--other-month__K4F-o{color:#aaa}.CalendarCell-module_cell--other-month__K4F-o:hover{background-color:#f1f1f1;color:#333}.CalendarCell-module_cell--weekend__J6sbX{background-color:transparent;color:#d32f2f}[data-theme=dark] .CalendarCell-module_cell--weekend__J6sbX{color:#d32f2f}.CalendarCell-module_cell--holiday__WuOUr{background-color:#d32f2f;color:#fff}.CalendarCell-module_cell--in-range__FUcKY{background-color:rgba(47,128,237,.102);border-radius:0;color:#2f80ed}.CalendarCell-module_cell--in-range__FUcKY:hover{background-color:#fff;color:#2f80ed}[data-theme=dark] .CalendarCell-module_cell--in-range__FUcKY:hover{background-color:#333}.CalendarCell-module_cell--selected__FK-7m{background-color:#2f80ed;border-radius:8px;color:#fff}.CalendarCell-module_cell--selected__FK-7m:hover{background-color:rgba(47,128,237,.102);color:#2f80ed}";
|
|
46
|
+
var styles$8 = {"cell":"CalendarCell-module_cell__-bATh","cell__day-number":"CalendarCell-module_cell__day-number__OwQ5i","cell__todo-indicator":"CalendarCell-module_cell__todo-indicator__RR2HA","cell--other-month":"CalendarCell-module_cell--other-month__K4F-o","cell--weekend":"CalendarCell-module_cell--weekend__J6sbX","cell--holiday":"CalendarCell-module_cell--holiday__WuOUr","cell--in-range":"CalendarCell-module_cell--in-range__FUcKY","cell--selected":"CalendarCell-module_cell--selected__FK-7m"};
|
|
47
|
+
styleInject(css_248z$8);
|
|
48
|
+
|
|
49
|
+
const CalendarCell = (props) => {
|
|
50
|
+
const { date, dayNumber, isCurrentMonth, onClick, isHoliday, isSelected, isInRange, textColor, backgroundColor, isWeekend, id, isDisabled, haveToDo, onDayClickDouble, } = props;
|
|
51
|
+
const cellClasses = [
|
|
52
|
+
styles$8.cell,
|
|
53
|
+
!isCurrentMonth && styles$8['cell--other-month'],
|
|
54
|
+
isHoliday && isCurrentMonth && styles$8['cell--holiday'],
|
|
55
|
+
isSelected && styles$8['cell--selected'],
|
|
56
|
+
isInRange && styles$8['cell--in-range'],
|
|
57
|
+
isWeekend && isCurrentMonth && styles$8['cell--weekend'],
|
|
58
|
+
]
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
.join(' ');
|
|
61
|
+
const extrasStyles = {};
|
|
62
|
+
if (isHoliday || isWeekend) {
|
|
63
|
+
if (textColor)
|
|
64
|
+
extrasStyles.color = textColor;
|
|
65
|
+
if (backgroundColor)
|
|
66
|
+
extrasStyles.backgroundColor = backgroundColor;
|
|
67
|
+
}
|
|
68
|
+
const handleOnClick = (event) => {
|
|
69
|
+
if (!isDisabled)
|
|
70
|
+
onClick?.(date, event);
|
|
71
|
+
};
|
|
72
|
+
const handleOnClickDoble = (event) => {
|
|
73
|
+
if (!isDisabled)
|
|
74
|
+
onDayClickDouble?.(date, event);
|
|
75
|
+
};
|
|
76
|
+
return (jsxRuntime.jsxs("div", { className: cellClasses, style: extrasStyles, onClick: handleOnClick, onDoubleClick: handleOnClickDoble, "aria-label": `Day ${dayNumber}`, role: "cell", children: [jsxRuntime.jsx("span", { className: styles$8['cell__day-number'], children: dayNumber }), haveToDo && jsxRuntime.jsx("span", { className: styles$8['cell__todo-indicator'], children: "\u02D9" })] }, id));
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
var css_248z$7 = ".CalendarGrid-module_grid__4Gpyr{display:grid;flex:1;grid-template-columns:repeat(7,1fr);height:32px;margin:0;padding:0;width:230px}";
|
|
80
|
+
var styles$7 = {"grid":"CalendarGrid-module_grid__4Gpyr"};
|
|
81
|
+
styleInject(css_248z$7);
|
|
82
|
+
|
|
83
|
+
const CalendarGrid = (props) => {
|
|
84
|
+
const { dateArray, onDayClick, onDayClickDouble } = props;
|
|
85
|
+
return (jsxRuntime.jsx("div", { className: styles$7.grid, role: "grid", children: dateArray.map((el) => (jsxRuntime.jsx(CalendarCell, { onClick: onDayClick, onDayClickDouble: onDayClickDouble, ...el }, el.id))) }));
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const WEEKDAY = [
|
|
89
|
+
{
|
|
90
|
+
id: '1',
|
|
91
|
+
weekday: 'Su',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: '2',
|
|
95
|
+
weekday: 'Mo',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: '3',
|
|
99
|
+
weekday: 'Tu',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: '4',
|
|
103
|
+
weekday: 'We',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: '5',
|
|
107
|
+
weekday: 'Th',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: '6',
|
|
111
|
+
weekday: 'Fr',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: '7',
|
|
115
|
+
weekday: 'Sa',
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
const MONDAY_INDEX = 1;
|
|
119
|
+
const SUNDAY_INDEX = 0;
|
|
120
|
+
const SATURDAY_INDEX = 6;
|
|
121
|
+
const WEEK_LENGHT = 7;
|
|
122
|
+
|
|
123
|
+
var css_248z$6 = ".CalendarHeader-module_week__cell__Tc-71,.CalendarHeader-module_week__neYla{align-items:center;display:flex;justify-content:center}.CalendarHeader-module_week__neYla{height:32px;margin:0;padding:0;width:230px}.CalendarHeader-module_week__cell__Tc-71{border-radius:8px;color:#333;height:32px;width:32px}[data-theme=dark] .CalendarHeader-module_week__cell__Tc-71{color:#f1f1f1}.CalendarHeader-module_week__weekday__2h6R-{font-size:13px;font-weight:600}";
|
|
124
|
+
var styles$6 = {"week":"CalendarHeader-module_week__neYla","week__cell":"CalendarHeader-module_week__cell__Tc-71","week__weekday":"CalendarHeader-module_week__weekday__2h6R-"};
|
|
125
|
+
styleInject(css_248z$6);
|
|
126
|
+
|
|
127
|
+
const CalendarHeader = (props) => {
|
|
128
|
+
const { fromMonday } = props;
|
|
129
|
+
const WEEKDAY_SORT = fromMonday
|
|
130
|
+
? [...WEEKDAY.slice(MONDAY_INDEX), WEEKDAY[SUNDAY_INDEX]]
|
|
131
|
+
: WEEKDAY;
|
|
132
|
+
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx("div", { className: styles$6.week, children: WEEKDAY_SORT.map(({ weekday, id }) => (jsxRuntime.jsx("div", { className: styles$6.week__cell, children: jsxRuntime.jsx("span", { className: styles$6.week__weekday, children: weekday }) }, id))) }) }));
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
var css_248z$5 = ".BaseCalendar-module_calendar__header__Fa7wb{align-items:center;display:flex;justify-content:space-between}.BaseCalendar-module_calendar__4X1R9{align-items:center;background-color:#fff;display:flex;flex-direction:column;justify-content:flex-start}[data-theme=dark] .BaseCalendar-module_calendar__4X1R9{background-color:#333}.BaseCalendar-module_calendar__4X1R9{border:1px solid #f1f1f1;border-radius:8px;min-height:110px;padding-bottom:8px;width:250px}.BaseCalendar-module_calendar__header__Fa7wb{background-color:#fff}[data-theme=dark] .BaseCalendar-module_calendar__header__Fa7wb{background-color:#333}.BaseCalendar-module_calendar__header__Fa7wb{margin:8px;width:250px}.BaseCalendar-module_calendar__button__ffhqu{color:#333}[data-theme=dark] .BaseCalendar-module_calendar__button__ffhqu{color:#f1f1f1}.BaseCalendar-module_calendar__button__ffhqu{background-color:transparent;border:none;margin:0 16px;padding:0;transition:color .1s ease}.BaseCalendar-module_calendar__button__ffhqu:hover{color:#aaa}[data-theme=dark] .BaseCalendar-module_calendar__button__ffhqu:hover{color:#f1f1f1}.BaseCalendar-module_calendar__button__ffhqu:disabled{cursor:not-allowed;opacity:.3}.BaseCalendar-module_calendar__month__zGLfH,.BaseCalendar-module_calendar__year__OwYFc{color:#333}[data-theme=dark] .BaseCalendar-module_calendar__month__zGLfH,[data-theme=dark] .BaseCalendar-module_calendar__year__OwYFc{color:#f1f1f1}.BaseCalendar-module_calendar__month__zGLfH,.BaseCalendar-module_calendar__year__OwYFc{font-size:14px;font-weight:700}.BaseCalendar-module_calendar__notes__RD7P8{color:#aaa;font-size:10px}.BaseCalendar-module_calendar__year__OwYFc{appearance:none;-webkit-appearance:none;-moz-appearance:none;background:transparent;border:none;cursor:pointer;margin-left:none;padding:none;transition:all .2s ease}.BaseCalendar-module_calendar__year__OwYFc:hover{transform:scale(1.05)}";
|
|
136
|
+
var styles$5 = {"calendar__header":"BaseCalendar-module_calendar__header__Fa7wb","calendar":"BaseCalendar-module_calendar__4X1R9","calendar__button":"BaseCalendar-module_calendar__button__ffhqu","calendar__month":"BaseCalendar-module_calendar__month__zGLfH","calendar__year":"BaseCalendar-module_calendar__year__OwYFc","calendar__notes":"BaseCalendar-module_calendar__notes__RD7P8"};
|
|
137
|
+
styleInject(css_248z$5);
|
|
138
|
+
|
|
139
|
+
const BaseCalendar = (props) => {
|
|
140
|
+
const { fromMonday, dateArray, month, year, onClickNext, onClickPrevious, onDayClick, canNext, canPrev, onDayClickDouble, yearsArray, onClickNextYear, } = props;
|
|
141
|
+
const handleYearChange = (e) => {
|
|
142
|
+
const newYear = parseInt(e.target.value);
|
|
143
|
+
onClickNextYear(newYear);
|
|
144
|
+
};
|
|
145
|
+
return (jsxRuntime.jsxs("article", { className: styles$5.calendar, children: [jsxRuntime.jsxs("header", { className: styles$5.calendar__header, children: [jsxRuntime.jsx("button", { className: styles$5.calendar__button, type: "button", onClick: onClickPrevious, disabled: !canPrev, children: jsxRuntime.jsx(PreviousIcon, {}) }), jsxRuntime.jsxs("div", { className: styles$5.calendar__month, children: [month, ' ', jsxRuntime.jsx("select", { className: styles$5.calendar__year, value: year, onChange: handleYearChange, children: yearsArray.map((el) => (jsxRuntime.jsx("option", { value: el, children: el }, `option-yaer-${el}`))) })] }), jsxRuntime.jsx("button", { className: styles$5.calendar__button, type: "button", onClick: onClickNext, disabled: !canNext, children: jsxRuntime.jsx(NextIcon, {}) })] }), jsxRuntime.jsx(CalendarHeader, { fromMonday: fromMonday }), jsxRuntime.jsx(CalendarGrid, { dateArray: dateArray, onDayClick: onDayClick, onDayClickDouble: onDayClickDouble }), onDayClickDouble && (jsxRuntime.jsx("span", { className: styles$5.calendar__notes, children: "Click twice on the day to add a task." }))] }));
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
var THEMED;
|
|
149
|
+
(function (THEMED) {
|
|
150
|
+
THEMED["LIGHT"] = "light";
|
|
151
|
+
THEMED["DARK"] = "dark";
|
|
152
|
+
})(THEMED || (THEMED = {}));
|
|
153
|
+
|
|
154
|
+
const getTheme = (darkThemed) => {
|
|
155
|
+
return darkThemed ? THEMED.DARK : THEMED.LIGHT;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const DATE_PARTS_LENGTH = 3;
|
|
159
|
+
const MAX_INPUT_LENGTH = 10;
|
|
160
|
+
const FIRST_INDEX = 1;
|
|
161
|
+
|
|
162
|
+
const START_DATE_INDEX_ = 0;
|
|
163
|
+
const START_MONTH_INDEX = 2;
|
|
164
|
+
const START_YEAR_INDEX = 4;
|
|
165
|
+
const MAX_DATE_LENGTH = 10;
|
|
166
|
+
|
|
167
|
+
const CELLS_LENGTH = 42;
|
|
168
|
+
const MIN_CELLS_LENGTH = 28;
|
|
169
|
+
const generateMonthArray = (currentDate, params) => {
|
|
170
|
+
const days = [];
|
|
171
|
+
const { selectedDate, rangeStart, rangeEnd, fromMonday } = params;
|
|
172
|
+
const startWeekDay = fromMonday ? FIRST_INDEX : START_DATE_INDEX_;
|
|
173
|
+
const firstDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), FIRST_INDEX - startWeekDay);
|
|
174
|
+
const startWeekday = firstDayOfMonth.getDay();
|
|
175
|
+
const startDate = new Date(firstDayOfMonth);
|
|
176
|
+
startDate.setDate(firstDayOfMonth.getDate() - startWeekday + startWeekDay);
|
|
177
|
+
const selectedTime = selectedDate?.getTime();
|
|
178
|
+
const startTime = rangeStart?.getTime();
|
|
179
|
+
const endTime = rangeEnd?.getTime();
|
|
180
|
+
for (let i = 0; i < CELLS_LENGTH; i++) {
|
|
181
|
+
const date = new Date(startDate);
|
|
182
|
+
date.setDate(startDate.getDate() + i);
|
|
183
|
+
const isCurrentMonth = date.getMonth() === currentDate.getMonth();
|
|
184
|
+
if (!isCurrentMonth &&
|
|
185
|
+
date.getDay() == START_DATE_INDEX_ + startWeekDay &&
|
|
186
|
+
i >= MIN_CELLS_LENGTH) {
|
|
187
|
+
return days;
|
|
188
|
+
}
|
|
189
|
+
const newId = date.toISOString();
|
|
190
|
+
const isSelected = selectedTime === date.getTime() && isCurrentMonth;
|
|
191
|
+
const isInRange = !!(startTime &&
|
|
192
|
+
endTime &&
|
|
193
|
+
date.getTime() >= startTime &&
|
|
194
|
+
date.getTime() <= endTime);
|
|
195
|
+
const resultDay = {
|
|
196
|
+
id: newId,
|
|
197
|
+
date,
|
|
198
|
+
dayNumber: date.getDate(),
|
|
199
|
+
isCurrentMonth,
|
|
200
|
+
isSelected,
|
|
201
|
+
isInRange,
|
|
202
|
+
};
|
|
203
|
+
days.push(resultDay);
|
|
204
|
+
}
|
|
205
|
+
return days;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
class CalenderDecorator {
|
|
209
|
+
getNextDate(date) {
|
|
210
|
+
const newDate = new Date(date.getFullYear(), date.getMonth() + FIRST_INDEX, FIRST_INDEX);
|
|
211
|
+
return newDate;
|
|
212
|
+
}
|
|
213
|
+
getPreviousDate(date) {
|
|
214
|
+
const newDate = new Date(date.getFullYear(), date.getMonth() - FIRST_INDEX, FIRST_INDEX);
|
|
215
|
+
return newDate;
|
|
216
|
+
}
|
|
217
|
+
getNextYear(date, newYear) {
|
|
218
|
+
const newDate = new Date(newYear, date.getMonth(), FIRST_INDEX);
|
|
219
|
+
return newDate;
|
|
220
|
+
}
|
|
221
|
+
getDays(currentDate, params) {
|
|
222
|
+
return generateMonthArray(currentDate, params);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
class BaseCalendarEngine {
|
|
227
|
+
engine;
|
|
228
|
+
constructor(engine) {
|
|
229
|
+
this.engine = engine;
|
|
230
|
+
}
|
|
231
|
+
getNextDate(date) {
|
|
232
|
+
return this.engine.getNextDate(date);
|
|
233
|
+
}
|
|
234
|
+
getPreviousDate(date) {
|
|
235
|
+
return this.engine.getPreviousDate(date);
|
|
236
|
+
}
|
|
237
|
+
getNextYear(date, newYear) {
|
|
238
|
+
return this.engine.getNextYear(date, newYear);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
class FromMondayDecorator extends BaseCalendarEngine {
|
|
243
|
+
fromMonday;
|
|
244
|
+
constructor(engine, fromMonday) {
|
|
245
|
+
super(engine);
|
|
246
|
+
this.fromMonday = fromMonday || false;
|
|
247
|
+
}
|
|
248
|
+
getDays(currentDate, params) {
|
|
249
|
+
const days = this.engine.getDays(currentDate, { ...params, fromMonday: this.fromMonday });
|
|
250
|
+
return days;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
class HolidayDecorator extends BaseCalendarEngine {
|
|
255
|
+
holidays;
|
|
256
|
+
constructor(engine, holidays) {
|
|
257
|
+
(super(engine), (this.holidays = new Set(holidays.map((el) => el.getTime()))));
|
|
258
|
+
}
|
|
259
|
+
getDays(currentDate, params) {
|
|
260
|
+
const days = this.engine.getDays(currentDate, params);
|
|
261
|
+
return days.map((day) => {
|
|
262
|
+
const dayTime = day.date.getTime();
|
|
263
|
+
if (this.holidays.has(dayTime)) {
|
|
264
|
+
return { ...day, isHoliday: true };
|
|
265
|
+
}
|
|
266
|
+
return day;
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const isMinMaxValue = (date, minDate, maxDate) => {
|
|
272
|
+
if (minDate) {
|
|
273
|
+
const currentDate = new Date(date.getFullYear(), date.getMonth() + FIRST_INDEX, START_DATE_INDEX_);
|
|
274
|
+
if (currentDate <= minDate)
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
if (maxDate) {
|
|
278
|
+
const currentDate = new Date(date.getFullYear(), date.getMonth(), START_DATE_INDEX_);
|
|
279
|
+
if (currentDate >= maxDate)
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
return true;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
class MinMaxDecorator extends BaseCalendarEngine {
|
|
286
|
+
minDate;
|
|
287
|
+
maxDate;
|
|
288
|
+
constructor(engine, minDate, maxDate) {
|
|
289
|
+
super(engine);
|
|
290
|
+
this.minDate = minDate || null;
|
|
291
|
+
this.maxDate = maxDate || null;
|
|
292
|
+
}
|
|
293
|
+
isDateInRange(date) {
|
|
294
|
+
return isMinMaxValue(date, this.minDate, this.maxDate);
|
|
295
|
+
}
|
|
296
|
+
getDays(currentDate, params) {
|
|
297
|
+
const days = this.engine.getDays(currentDate, params) || [];
|
|
298
|
+
return days.map((day) => {
|
|
299
|
+
if (day.date.getTime() < (this.minDate?.getTime() || 0) ||
|
|
300
|
+
day.date.getTime() > (this.maxDate?.getTime() || 0)) {
|
|
301
|
+
return { ...day, isDisabled: true, isCurrentMonth: false };
|
|
302
|
+
}
|
|
303
|
+
return day;
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
getNextDate(date) {
|
|
307
|
+
const nextDate = this.engine.getNextDate(date);
|
|
308
|
+
return this.isDateInRange(nextDate) ? nextDate : date;
|
|
309
|
+
}
|
|
310
|
+
getPreviousDate(date) {
|
|
311
|
+
const prevDate = this.engine.getPreviousDate(date);
|
|
312
|
+
return this.isDateInRange(prevDate) ? prevDate : date;
|
|
313
|
+
}
|
|
314
|
+
getNextYear(date, newYear) {
|
|
315
|
+
const nextYear = this.engine.getNextYear(date, newYear);
|
|
316
|
+
return this.isDateInRange(nextYear) ? nextYear : date;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const INPUT_TYPE = {
|
|
321
|
+
START: 'START',
|
|
322
|
+
END: 'END',
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const isSameDates = (firstDate, secondDate) => {
|
|
326
|
+
if (firstDate.getFullYear() === secondDate.getFullYear() &&
|
|
327
|
+
firstDate.getMonth() === secondDate.getMonth() &&
|
|
328
|
+
firstDate.getDate() === secondDate.getDate())
|
|
329
|
+
return true;
|
|
330
|
+
return false;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
class RangeDecorator extends BaseCalendarEngine {
|
|
334
|
+
listeners = new Set();
|
|
335
|
+
rangeStart;
|
|
336
|
+
rangeEnd;
|
|
337
|
+
constructor(_config) {
|
|
338
|
+
super(_config);
|
|
339
|
+
this.rangeStart = null;
|
|
340
|
+
this.rangeEnd = null;
|
|
341
|
+
}
|
|
342
|
+
getDays(currentDate, params) {
|
|
343
|
+
const days = this.engine.getDays(currentDate, params);
|
|
344
|
+
const { rangeStart, rangeEnd } = params;
|
|
345
|
+
if (!rangeStart && !rangeEnd)
|
|
346
|
+
return days;
|
|
347
|
+
return days.map((day) => {
|
|
348
|
+
const isStart = rangeStart ? isSameDates(day.date, rangeStart) : false;
|
|
349
|
+
const isEnd = rangeEnd ? isSameDates(day.date, rangeEnd) : false;
|
|
350
|
+
const inBetween = rangeStart && rangeEnd && day.date > rangeStart && day.date < rangeEnd ? true : false;
|
|
351
|
+
return { ...day, isSelected: isStart || isEnd, isInRange: isStart || isEnd || inBetween };
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
getRangeStart() {
|
|
355
|
+
return this.rangeStart;
|
|
356
|
+
}
|
|
357
|
+
getRangeEnd() {
|
|
358
|
+
return this.rangeEnd;
|
|
359
|
+
}
|
|
360
|
+
setRangeStart(date) {
|
|
361
|
+
this.rangeStart = date;
|
|
362
|
+
if (date && this.rangeEnd && date > this.rangeEnd) {
|
|
363
|
+
this.rangeEnd = null;
|
|
364
|
+
}
|
|
365
|
+
this.notify();
|
|
366
|
+
}
|
|
367
|
+
setRangeEnd(date) {
|
|
368
|
+
if (date && this.rangeStart && date < this.rangeStart) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
this.rangeEnd = date;
|
|
372
|
+
this.notify();
|
|
373
|
+
}
|
|
374
|
+
clearRange(inputType) {
|
|
375
|
+
if (inputType === INPUT_TYPE.END) {
|
|
376
|
+
this.rangeEnd = null;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
this.rangeEnd = null;
|
|
380
|
+
this.rangeStart = null;
|
|
381
|
+
}
|
|
382
|
+
this.notify();
|
|
383
|
+
}
|
|
384
|
+
handleRangeClick(date) {
|
|
385
|
+
if (!this.rangeStart) {
|
|
386
|
+
this.rangeStart = date;
|
|
387
|
+
}
|
|
388
|
+
else if (!this.rangeEnd) {
|
|
389
|
+
if (date < this.rangeStart) {
|
|
390
|
+
this.rangeStart = date;
|
|
391
|
+
this.rangeEnd = null;
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
this.rangeEnd = date;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
this.rangeStart = date;
|
|
399
|
+
this.rangeEnd = null;
|
|
400
|
+
}
|
|
401
|
+
this.notify();
|
|
402
|
+
return {
|
|
403
|
+
rangeStart: this.rangeStart,
|
|
404
|
+
rangeEnd: this.rangeEnd,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
notify() {
|
|
408
|
+
this.listeners.forEach((listener) => listener());
|
|
409
|
+
}
|
|
410
|
+
subscribe(listener) {
|
|
411
|
+
this.listeners.add(listener);
|
|
412
|
+
return () => this.listeners.delete(listener);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
class StorageService {
|
|
417
|
+
key;
|
|
418
|
+
defaultValue;
|
|
419
|
+
constructor(key, defaultValue) {
|
|
420
|
+
this.key = key;
|
|
421
|
+
this.defaultValue = defaultValue;
|
|
422
|
+
}
|
|
423
|
+
get() {
|
|
424
|
+
const data = localStorage.getItem(this.key);
|
|
425
|
+
return data ? JSON.parse(data) : this.defaultValue;
|
|
426
|
+
}
|
|
427
|
+
set(value) {
|
|
428
|
+
localStorage.setItem(this.key, JSON.stringify(value));
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
class TodoDecorator extends BaseCalendarEngine {
|
|
433
|
+
STORAGE_KEY = 'calendar_todos';
|
|
434
|
+
storage = new StorageService(this.STORAGE_KEY, {});
|
|
435
|
+
constructor(engine) {
|
|
436
|
+
super(engine);
|
|
437
|
+
}
|
|
438
|
+
getDateKey(date) {
|
|
439
|
+
return date.toISOString().split('T')[START_DATE_INDEX_];
|
|
440
|
+
}
|
|
441
|
+
getAllTodos() {
|
|
442
|
+
return this.storage.get();
|
|
443
|
+
}
|
|
444
|
+
getTodosByDate(date) {
|
|
445
|
+
const key = this.getDateKey(date);
|
|
446
|
+
return this.getAllTodos()[key] || [];
|
|
447
|
+
}
|
|
448
|
+
addTodo(date, todo) {
|
|
449
|
+
const key = this.getDateKey(date);
|
|
450
|
+
const allTodos = this.getAllTodos();
|
|
451
|
+
allTodos[key] = allTodos[key] || [];
|
|
452
|
+
allTodos[key].push(todo);
|
|
453
|
+
this.storage.set(allTodos);
|
|
454
|
+
}
|
|
455
|
+
deleteTodo(date, todo) {
|
|
456
|
+
const key = this.getDateKey(date);
|
|
457
|
+
const allTodos = this.getAllTodos();
|
|
458
|
+
allTodos[key] = allTodos[key] || [];
|
|
459
|
+
allTodos[key] = allTodos[key].filter((item) => item !== todo);
|
|
460
|
+
this.storage.set(allTodos);
|
|
461
|
+
}
|
|
462
|
+
getDays(currentDate, params) {
|
|
463
|
+
const todos = this.getAllTodos();
|
|
464
|
+
const days = this.engine.getDays(currentDate, params);
|
|
465
|
+
return days.map((day) => {
|
|
466
|
+
const key = this.getDateKey(day.date);
|
|
467
|
+
const haveToDo = todos[key] && todos[key].length > 0;
|
|
468
|
+
return {
|
|
469
|
+
...day,
|
|
470
|
+
todo: todos[key] || [],
|
|
471
|
+
haveToDo,
|
|
472
|
+
};
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
class ViewByWeekDecorator extends BaseCalendarEngine {
|
|
478
|
+
constructor(engine) {
|
|
479
|
+
super(engine);
|
|
480
|
+
}
|
|
481
|
+
getDays(currentDate, params) {
|
|
482
|
+
const daysArray = this.engine.getDays(currentDate, params);
|
|
483
|
+
const currentDateIndex = daysArray.findIndex((day) => isSameDates(day.date, currentDate));
|
|
484
|
+
if (currentDateIndex === -FIRST_INDEX)
|
|
485
|
+
return daysArray.slice(START_DATE_INDEX_, WEEK_LENGHT);
|
|
486
|
+
const weekCount = Math.floor(currentDateIndex / WEEK_LENGHT);
|
|
487
|
+
return daysArray.slice(weekCount * WEEK_LENGHT, weekCount * WEEK_LENGHT + WEEK_LENGHT);
|
|
488
|
+
}
|
|
489
|
+
getNextDate(date) {
|
|
490
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + WEEK_LENGHT);
|
|
491
|
+
}
|
|
492
|
+
getPreviousDate(date) {
|
|
493
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate() - WEEK_LENGHT);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
class WeekdayDecorator extends BaseCalendarEngine {
|
|
498
|
+
constructor(engine) {
|
|
499
|
+
super(engine);
|
|
500
|
+
}
|
|
501
|
+
getDays(currentDate, params) {
|
|
502
|
+
const days = this.engine.getDays(currentDate, params);
|
|
503
|
+
return days.map((day) => {
|
|
504
|
+
const dayDay = day.date.getDay();
|
|
505
|
+
if (dayDay == SUNDAY_INDEX || dayDay == SATURDAY_INDEX)
|
|
506
|
+
return { ...day, isWeekend: true };
|
|
507
|
+
return day;
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const MAX_DAY = 31;
|
|
513
|
+
const MAX_MONTH = 12;
|
|
514
|
+
const MAX_YEAR = 2050;
|
|
515
|
+
const MIN_YEAR = 1970;
|
|
516
|
+
const validateDateInput = (value) => {
|
|
517
|
+
const validationResult = {
|
|
518
|
+
isValid: false,
|
|
519
|
+
error: '',
|
|
520
|
+
date: null,
|
|
521
|
+
};
|
|
522
|
+
const trimmed = value.trim();
|
|
523
|
+
const parts = trimmed.split('.');
|
|
524
|
+
if (parts.length !== DATE_PARTS_LENGTH) {
|
|
525
|
+
return { ...validationResult, error: 'Invalid format. Expected DD.MM.YYYY' };
|
|
526
|
+
}
|
|
527
|
+
if (trimmed.length > MAX_INPUT_LENGTH) {
|
|
528
|
+
return { ...validationResult, error: 'Input too long' };
|
|
529
|
+
}
|
|
530
|
+
const [dayRaw, monthRaw, yearRaw] = parts;
|
|
531
|
+
const day = Number(dayRaw);
|
|
532
|
+
const month = Number(monthRaw);
|
|
533
|
+
const year = Number(yearRaw);
|
|
534
|
+
if (Number.isNaN(day) || Number.isNaN(month) || Number.isNaN(year)) {
|
|
535
|
+
return { ...validationResult, error: 'Invalid numeric values' };
|
|
536
|
+
}
|
|
537
|
+
if (day > MAX_DAY || day < FIRST_INDEX) {
|
|
538
|
+
return { ...validationResult, error: 'The day should be from 1 to 31' };
|
|
539
|
+
}
|
|
540
|
+
if (month > MAX_MONTH || month < FIRST_INDEX) {
|
|
541
|
+
return { ...validationResult, error: 'The month should be from 1 to 12' };
|
|
542
|
+
}
|
|
543
|
+
if (year > MAX_YEAR) {
|
|
544
|
+
return { ...validationResult, error: `The year should be less than ${MAX_YEAR}` };
|
|
545
|
+
}
|
|
546
|
+
if (year < MIN_YEAR) {
|
|
547
|
+
return { ...validationResult, error: `The year should be greater than ${MIN_YEAR}` };
|
|
548
|
+
}
|
|
549
|
+
const newDate = new Date(year, month - FIRST_INDEX, day);
|
|
550
|
+
if (newDate.getFullYear() !== year ||
|
|
551
|
+
newDate.getMonth() !== month - FIRST_INDEX ||
|
|
552
|
+
newDate.getDate() !== day) {
|
|
553
|
+
return { ...validationResult, error: 'Invalid date for the given month/year' };
|
|
554
|
+
}
|
|
555
|
+
return { ...validationResult, isValid: true, date: newDate };
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
const generateYearArray = (minYear, maxYear) => {
|
|
559
|
+
minYear ??= MIN_YEAR;
|
|
560
|
+
maxYear ??= MAX_YEAR;
|
|
561
|
+
const yearArray = [];
|
|
562
|
+
while (minYear <= maxYear) {
|
|
563
|
+
yearArray.push(minYear);
|
|
564
|
+
minYear += FIRST_INDEX;
|
|
565
|
+
}
|
|
566
|
+
return yearArray;
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
class DatepickerService {
|
|
570
|
+
engine;
|
|
571
|
+
viewDate;
|
|
572
|
+
selectedDate;
|
|
573
|
+
listeners = new Set();
|
|
574
|
+
yearsArray = [];
|
|
575
|
+
constructor(_config) {
|
|
576
|
+
this.engine = this.composeEngine(_config);
|
|
577
|
+
this.viewDate = this.selectFirstDay(_config);
|
|
578
|
+
this.selectedDate = _config?.defaultDate || null;
|
|
579
|
+
this.yearsArray = generateYearArray(_config?.minDate?.getFullYear(), _config?.maxDate?.getFullYear());
|
|
580
|
+
}
|
|
581
|
+
composeEngine(_config) {
|
|
582
|
+
let engine = new CalenderDecorator();
|
|
583
|
+
if (_config?.maxDate || _config?.minDate || _config?.defaultDate)
|
|
584
|
+
engine = new MinMaxDecorator(engine, _config.minDate, _config.maxDate);
|
|
585
|
+
if (_config?.withHoliday && _config.holidays)
|
|
586
|
+
engine = new HolidayDecorator(engine, _config.holidays);
|
|
587
|
+
if (_config?.withWeekend)
|
|
588
|
+
engine = new WeekdayDecorator(engine);
|
|
589
|
+
if (_config?.fromMonday)
|
|
590
|
+
engine = new FromMondayDecorator(engine, _config.fromMonday);
|
|
591
|
+
if (_config?.viewByWeek)
|
|
592
|
+
engine = new ViewByWeekDecorator(engine);
|
|
593
|
+
if (_config?.withTodo)
|
|
594
|
+
engine = new TodoDecorator(engine);
|
|
595
|
+
if (_config?.withRange)
|
|
596
|
+
engine = new RangeDecorator(engine);
|
|
597
|
+
return engine;
|
|
598
|
+
}
|
|
599
|
+
subscribe(listener) {
|
|
600
|
+
this.listeners.add(listener);
|
|
601
|
+
return () => this.listeners.delete(listener);
|
|
602
|
+
}
|
|
603
|
+
notify() {
|
|
604
|
+
this.listeners.forEach((listener) => listener());
|
|
605
|
+
}
|
|
606
|
+
next() {
|
|
607
|
+
this.viewDate = this.engine.getNextDate(this.viewDate);
|
|
608
|
+
this.notify();
|
|
609
|
+
}
|
|
610
|
+
prev() {
|
|
611
|
+
this.viewDate = this.engine.getPreviousDate(this.viewDate);
|
|
612
|
+
this.notify();
|
|
613
|
+
}
|
|
614
|
+
handleInputChange(value) {
|
|
615
|
+
const result = validateDateInput(value);
|
|
616
|
+
if (result.isValid && result.date) {
|
|
617
|
+
this.selectedDate = result.date;
|
|
618
|
+
this.viewDate = result.date;
|
|
619
|
+
this.notify();
|
|
620
|
+
}
|
|
621
|
+
return result;
|
|
622
|
+
}
|
|
623
|
+
getInputValue() {
|
|
624
|
+
if (!this.selectedDate) {
|
|
625
|
+
return '';
|
|
626
|
+
}
|
|
627
|
+
return this.selectedDate.toLocaleDateString();
|
|
628
|
+
}
|
|
629
|
+
setSelectedDate(date) {
|
|
630
|
+
this.selectedDate = date;
|
|
631
|
+
if (date)
|
|
632
|
+
this.viewDate = date;
|
|
633
|
+
this.notify();
|
|
634
|
+
}
|
|
635
|
+
getSelectedDate() {
|
|
636
|
+
return this.selectedDate;
|
|
637
|
+
}
|
|
638
|
+
getNextYear(newYear) {
|
|
639
|
+
const newDate = this.engine.getNextYear(this.viewDate, newYear);
|
|
640
|
+
this.viewDate = newDate;
|
|
641
|
+
this.notify();
|
|
642
|
+
return newDate;
|
|
643
|
+
}
|
|
644
|
+
getViewModel() {
|
|
645
|
+
const nextDate = this.engine.getNextDate(this.viewDate);
|
|
646
|
+
const prevDate = this.engine.getPreviousDate(this.viewDate);
|
|
647
|
+
let rangeStart = null;
|
|
648
|
+
let rangeEnd = null;
|
|
649
|
+
if (this.engine instanceof RangeDecorator) {
|
|
650
|
+
rangeStart = this.engine.getRangeStart();
|
|
651
|
+
rangeEnd = this.engine.getRangeEnd();
|
|
652
|
+
}
|
|
653
|
+
return {
|
|
654
|
+
dateArray: this.engine.getDays(this.viewDate, {
|
|
655
|
+
selectedDate: this.selectedDate,
|
|
656
|
+
rangeStart: rangeStart,
|
|
657
|
+
rangeEnd: rangeEnd,
|
|
658
|
+
}),
|
|
659
|
+
fromMonday: false,
|
|
660
|
+
month: this.viewDate.toLocaleString('en-US', { month: 'long' }),
|
|
661
|
+
year: this.viewDate.getFullYear(),
|
|
662
|
+
onClickNext: () => this.next(),
|
|
663
|
+
onClickPrevious: () => this.prev(),
|
|
664
|
+
onDayClick: (date, _event) => this.setSelectedDate(date),
|
|
665
|
+
onDayRangeClick: (date) => this.handleRangeClick(date.toLocaleDateString()),
|
|
666
|
+
onClickNextYear: (newYear) => this.getNextYear(newYear),
|
|
667
|
+
canNext: nextDate.getTime() !== this.viewDate.getTime(),
|
|
668
|
+
canPrev: prevDate.getTime() !== this.viewDate.getTime(),
|
|
669
|
+
yearsArray: this.yearsArray,
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
selectFirstDay(_config) {
|
|
673
|
+
const min = _config?.minDate;
|
|
674
|
+
const max = _config?.maxDate;
|
|
675
|
+
const defaultDate = _config?.defaultDate || new Date();
|
|
676
|
+
const isTooEarly = min && defaultDate.getTime() < min.getTime();
|
|
677
|
+
const isTooLate = max && defaultDate.getTime() > max.getTime();
|
|
678
|
+
if (isTooEarly || isTooLate) {
|
|
679
|
+
return min || max || defaultDate;
|
|
680
|
+
}
|
|
681
|
+
return defaultDate;
|
|
682
|
+
}
|
|
683
|
+
handleRangeClick(value) {
|
|
684
|
+
const date = validateDateInput(value);
|
|
685
|
+
if (date.isValid && date.date && this.engine instanceof RangeDecorator) {
|
|
686
|
+
const { rangeStart, rangeEnd } = this.engine.handleRangeClick(date.date);
|
|
687
|
+
this.selectedDate = rangeEnd || rangeStart;
|
|
688
|
+
this.viewDate = date.date;
|
|
689
|
+
this.notify();
|
|
690
|
+
}
|
|
691
|
+
return date;
|
|
692
|
+
}
|
|
693
|
+
clearRange(inputType) {
|
|
694
|
+
if (this.engine instanceof RangeDecorator) {
|
|
695
|
+
this.engine.clearRange(inputType);
|
|
696
|
+
const start = this.engine.getRangeStart();
|
|
697
|
+
const end = this.engine.getRangeEnd();
|
|
698
|
+
this.selectedDate = end || start;
|
|
699
|
+
this.notify();
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
getRange() {
|
|
703
|
+
if (this.engine instanceof RangeDecorator) {
|
|
704
|
+
return {
|
|
705
|
+
start: this.engine.getRangeStart(),
|
|
706
|
+
end: this.engine.getRangeEnd(),
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
return { start: null, end: null };
|
|
710
|
+
}
|
|
711
|
+
setRangeStart(date) {
|
|
712
|
+
if (this.engine instanceof RangeDecorator) {
|
|
713
|
+
this.engine.setRangeStart(date);
|
|
714
|
+
this.selectedDate = date;
|
|
715
|
+
if (date)
|
|
716
|
+
this.viewDate = date;
|
|
717
|
+
this.notify();
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
setRangeEnd(date) {
|
|
721
|
+
if (this.engine instanceof RangeDecorator) {
|
|
722
|
+
this.engine.setRangeEnd(date);
|
|
723
|
+
this.selectedDate = date;
|
|
724
|
+
if (date)
|
|
725
|
+
this.viewDate = date;
|
|
726
|
+
this.notify();
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
getRangeStart() {
|
|
730
|
+
if (this.engine instanceof RangeDecorator) {
|
|
731
|
+
return this.engine.getRangeStart();
|
|
732
|
+
}
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
getRangeEnd() {
|
|
736
|
+
if (this.engine instanceof RangeDecorator) {
|
|
737
|
+
return this.engine.getRangeEnd();
|
|
738
|
+
}
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
741
|
+
addTodo(date, text) {
|
|
742
|
+
if (this.engine instanceof TodoDecorator) {
|
|
743
|
+
this.engine.addTodo(date, text);
|
|
744
|
+
this.notify();
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
getTodosByDate(date) {
|
|
748
|
+
if (this.engine instanceof TodoDecorator) {
|
|
749
|
+
return this.engine.getTodosByDate(date);
|
|
750
|
+
}
|
|
751
|
+
return [];
|
|
752
|
+
}
|
|
753
|
+
deleteTodo(date, todo) {
|
|
754
|
+
if (this.engine instanceof TodoDecorator) {
|
|
755
|
+
this.engine.deleteTodo(date, todo);
|
|
756
|
+
this.notify();
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
const useCalendarService = (config, withTodo) => {
|
|
762
|
+
const service = react.useMemo(() => new DatepickerService({ ...config, withTodo: withTodo }), [config, withTodo]);
|
|
763
|
+
const [viewModel, setViewModel] = react.useState(service.getViewModel());
|
|
764
|
+
react.useEffect(() => {
|
|
765
|
+
const unsubscribe = service.subscribe(() => {
|
|
766
|
+
setViewModel(service.getViewModel());
|
|
767
|
+
});
|
|
768
|
+
return () => {
|
|
769
|
+
unsubscribe();
|
|
770
|
+
};
|
|
771
|
+
}, [service]);
|
|
772
|
+
return {
|
|
773
|
+
service,
|
|
774
|
+
viewModel,
|
|
775
|
+
};
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
var css_248z$4 = ".ErrorBoundary-module_errorContainer__y4h-A{align-items:center;background-color:rgba(47,128,237,.6);border:1px solid #fff;border-radius:8px;color:#fff;display:flex;height:32px;justify-content:center;padding:0 10px;position:relative;width:100%}.ErrorBoundary-module_errorContainer__y4h-A .ErrorBoundary-module_reload__Afvg-{background-color:transparent;border:0;color:#fff;cursor:pointer;font-size:24px}";
|
|
779
|
+
var styles$4 = {"errorContainer":"ErrorBoundary-module_errorContainer__y4h-A","reload":"ErrorBoundary-module_reload__Afvg-"};
|
|
780
|
+
styleInject(css_248z$4);
|
|
781
|
+
|
|
782
|
+
class ErrorBoundary extends react.Component {
|
|
783
|
+
state = {
|
|
784
|
+
hasError: false,
|
|
785
|
+
};
|
|
786
|
+
static getDerivedStateFromError(_) {
|
|
787
|
+
return { hasError: true };
|
|
788
|
+
}
|
|
789
|
+
componentDidCatch(error, errorInfo) {
|
|
790
|
+
console.error('Uncaught error:', error, errorInfo);
|
|
791
|
+
}
|
|
792
|
+
handleReload = () => {
|
|
793
|
+
const updateState = { hasError: false };
|
|
794
|
+
this.setState(updateState);
|
|
795
|
+
window.location.reload();
|
|
796
|
+
};
|
|
797
|
+
render() {
|
|
798
|
+
if (this.state.hasError) {
|
|
799
|
+
return (jsxRuntime.jsxs("article", { className: styles$4.errorContainer, children: [jsxRuntime.jsx("p", { children: "Something went wrong." }), jsxRuntime.jsx("button", { onClick: this.handleReload, className: styles$4.reload, children: "\u21BB" })] }));
|
|
800
|
+
}
|
|
801
|
+
return this.props.children;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
const Calendar = (config) => {
|
|
806
|
+
const { viewModel } = useCalendarService(config);
|
|
807
|
+
const fromMonday = config?.fromMonday || false;
|
|
808
|
+
const themed = getTheme(config?.darkThemed);
|
|
809
|
+
return (jsxRuntime.jsx(ErrorBoundary, { children: jsxRuntime.jsx("div", { "data-theme": themed, children: jsxRuntime.jsx(BaseCalendar, { ...viewModel, fromMonday: fromMonday }) }) }));
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
const maskDate = (value) => {
|
|
813
|
+
const digits = value.replace(/\D/g, '');
|
|
814
|
+
if (digits.length <= START_MONTH_INDEX) {
|
|
815
|
+
return `${digits}`;
|
|
816
|
+
}
|
|
817
|
+
if (digits.length <= START_YEAR_INDEX) {
|
|
818
|
+
return `${digits.substring(START_DATE_INDEX_, START_MONTH_INDEX)}.${digits.substring(START_MONTH_INDEX)}`;
|
|
819
|
+
}
|
|
820
|
+
return `${digits.substring(START_DATE_INDEX_, START_MONTH_INDEX)}.${digits.substring(START_MONTH_INDEX, START_YEAR_INDEX)}.${digits.substring(START_YEAR_INDEX)}`;
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
const CalendarIcon = () => {
|
|
824
|
+
return (jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("g", { clipPath: "url(#clip0_1_539)", children: jsxRuntime.jsx("path", { d: "M8.2 9.59999C7.97333 9.59999 7.78347 9.52319 7.6304 9.36959C7.4768 9.21653 7.4 9.02666 7.4 8.79999C7.4 8.57333 7.4768 8.38319 7.6304 8.22959C7.78347 8.07653 7.97333 7.99999 8.2 7.99999C8.42667 7.99999 8.6168 8.07653 8.7704 8.22959C8.92347 8.38319 9 8.57333 9 8.79999C9 9.02666 8.92347 9.21653 8.7704 9.36959C8.6168 9.52319 8.42667 9.59999 8.2 9.59999ZM5 9.59999C4.77333 9.59999 4.5832 9.52319 4.4296 9.36959C4.27653 9.21653 4.2 9.02666 4.2 8.79999C4.2 8.57333 4.27653 8.38319 4.4296 8.22959C4.5832 8.07653 4.77333 7.99999 5 7.99999C5.22667 7.99999 5.4168 8.07653 5.5704 8.22959C5.72347 8.38319 5.8 8.57333 5.8 8.79999C5.8 9.02666 5.72347 9.21653 5.5704 9.36959C5.4168 9.52319 5.22667 9.59999 5 9.59999ZM11.4 9.59999C11.1733 9.59999 10.9835 9.52319 10.8304 9.36959C10.6768 9.21653 10.6 9.02666 10.6 8.79999C10.6 8.57333 10.6768 8.38319 10.8304 8.22959C10.9835 8.07653 11.1733 7.99999 11.4 7.99999C11.6267 7.99999 11.8165 8.07653 11.9696 8.22959C12.1232 8.38319 12.2 8.57333 12.2 8.79999C12.2 9.02666 12.1232 9.21653 11.9696 9.36959C11.8165 9.52319 11.6267 9.59999 11.4 9.59999ZM8.2 12.8C7.97333 12.8 7.78347 12.7232 7.6304 12.5696C7.4768 12.4165 7.4 12.2267 7.4 12C7.4 11.7733 7.4768 11.5835 7.6304 11.4304C7.78347 11.2768 7.97333 11.2 8.2 11.2C8.42667 11.2 8.6168 11.2768 8.7704 11.4304C8.92347 11.5835 9 11.7733 9 12C9 12.2267 8.92347 12.4165 8.7704 12.5696C8.6168 12.7232 8.42667 12.8 8.2 12.8ZM5 12.8C4.77333 12.8 4.5832 12.7232 4.4296 12.5696C4.27653 12.4165 4.2 12.2267 4.2 12C4.2 11.7733 4.27653 11.5835 4.4296 11.4304C4.5832 11.2768 4.77333 11.2 5 11.2C5.22667 11.2 5.4168 11.2768 5.5704 11.4304C5.72347 11.5835 5.8 11.7733 5.8 12C5.8 12.2267 5.72347 12.4165 5.5704 12.5696C5.4168 12.7232 5.22667 12.8 5 12.8ZM11.4 12.8C11.1733 12.8 10.9835 12.7232 10.8304 12.5696C10.6768 12.4165 10.6 12.2267 10.6 12C10.6 11.7733 10.6768 11.5835 10.8304 11.4304C10.9835 11.2768 11.1733 11.2 11.4 11.2C11.6267 11.2 11.8165 11.2768 11.9696 11.4304C12.1232 11.5835 12.2 11.7733 12.2 12C12.2 12.2267 12.1232 12.4165 11.9696 12.5696C11.8165 12.7232 11.6267 12.8 11.4 12.8ZM2.6 16C2.16 16 1.7832 15.8435 1.4696 15.5304C1.15653 15.2168 1 14.84 1 14.4V3.19999C1 2.75999 1.15653 2.38346 1.4696 2.07039C1.7832 1.75679 2.16 1.59999 2.6 1.59999H3.4V-7.62939e-06H5V1.59999H11.4V-7.62939e-06H13V1.59999H13.8C14.24 1.59999 14.6168 1.75679 14.9304 2.07039C15.2435 2.38346 15.4 2.75999 15.4 3.19999V14.4C15.4 14.84 15.2435 15.2168 14.9304 15.5304C14.6168 15.8435 14.24 16 13.8 16H2.6ZM2.6 14.4H13.8V6.39999H2.6V14.4Z", fill: "currentColor" }) }), jsxRuntime.jsx("defs", { children: jsxRuntime.jsx("clipPath", { id: "clip0_1_539", children: jsxRuntime.jsx("rect", { width: "16", height: "16", fill: "white" }) }) })] }));
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
const CloseIcon = () => {
|
|
828
|
+
return (jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.jsx("path", { d: "M12.9872 3.04239C10.2573 0.319202 5.77739 0.319202 3.04745 3.04239C0.317516 5.76559 0.317516 10.2344 3.04745 12.9576C5.77739 15.6808 10.1873 15.6808 12.9172 12.9576C15.6471 10.2344 15.7171 5.76559 12.9872 3.04239ZM9.97728 10.9327L8.01733 8.97756L6.05738 10.9327L5.0774 9.95511L7.03735 8L5.0774 6.04489L6.05738 5.06733L8.01733 7.02244L9.97728 5.06733L10.9573 6.04489L8.99731 8L10.9573 9.95511L9.97728 10.9327Z", fill: "currentColor" }) }));
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
var css_248z$3 = ".Input-module_wrapper__input-wrapper__F5nqJ{align-items:center;display:flex;justify-content:center}.Input-module_wrapper__Y1Vag{padding-bottom:10px;position:relative}.Input-module_wrapper__label__RJBKu{color:#333}[data-theme=dark] .Input-module_wrapper__label__RJBKu{color:#f1f1f1}.Input-module_wrapper__label__RJBKu{font-size:14px;font-weight:600;height:20px;padding:0 4px}.Input-module_wrapper__input-wrapper__F5nqJ{background-color:#fff}[data-theme=dark] .Input-module_wrapper__input-wrapper__F5nqJ{background-color:#333}.Input-module_wrapper__input-wrapper__F5nqJ{border:1px solid #f1f1f1;border-radius:8px;margin:4px 0;width:250px}.Input-module_wrapper__input-wrapper__F5nqJ:focus-within{border-color:#aaa}.Input-module_wrapper__field__wgrXi{background-color:#fff}[data-theme=dark] .Input-module_wrapper__field__wgrXi{background-color:#333}.Input-module_wrapper__field__wgrXi{color:#333}[data-theme=dark] .Input-module_wrapper__field__wgrXi{color:#f1f1f1}.Input-module_wrapper__field__wgrXi{border:0;height:42px;margin:0;outline:none;padding:0 8px;width:183px}.Input-module_wrapper__icon__TCCvH{color:#aaa}[data-theme=dark] .Input-module_wrapper__icon__TCCvH{color:#f1f1f1}.Input-module_wrapper__icon__TCCvH{background:transparent;border:0;cursor:pointer;padding:0;transition:opacity .2s}.Input-module_wrapper__icon__TCCvH:hover{color:#2f80ed}.Input-module_wrapper__icon--hidden__3k-Sb{opacity:0;pointer-events:none;visibility:hidden}.Input-module_wrapper__error__CG4kl{bottom:0;color:#d32f2f;font-size:10px;font-weight:600;left:0;margin-left:4px;position:absolute}[data-theme=dark] .Input-module_wrapper__error__CG4kl{color:#d32f2f}";
|
|
832
|
+
var styles$3 = {"wrapper__input-wrapper":"Input-module_wrapper__input-wrapper__F5nqJ","wrapper":"Input-module_wrapper__Y1Vag","wrapper__label":"Input-module_wrapper__label__RJBKu","wrapper__field":"Input-module_wrapper__field__wgrXi","wrapper__icon":"Input-module_wrapper__icon__TCCvH","wrapper__icon--hidden":"Input-module_wrapper__icon--hidden__3k-Sb","wrapper__error":"Input-module_wrapper__error__CG4kl"};
|
|
833
|
+
styleInject(css_248z$3);
|
|
834
|
+
|
|
835
|
+
const Input = (props) => {
|
|
836
|
+
const { value, placeholder, error, label, handleIsOpen, onClear, onChange } = props;
|
|
837
|
+
const inputId = react.useId();
|
|
838
|
+
const handleChange = (e) => {
|
|
839
|
+
const maskedValue = maskDate(e.target.value);
|
|
840
|
+
onChange?.(maskedValue);
|
|
841
|
+
};
|
|
842
|
+
const clearButtonStyles = [styles$3['wrapper__icon'], !value && styles$3['wrapper__icon--hidden']]
|
|
843
|
+
.filter(Boolean)
|
|
844
|
+
.join(' ');
|
|
845
|
+
return (jsxRuntime.jsxs("div", { className: styles$3.wrapper, children: [label && (jsxRuntime.jsx("label", { htmlFor: inputId, className: styles$3.wrapper__label, children: label })), jsxRuntime.jsxs("div", { className: styles$3['wrapper__input-wrapper'], children: [jsxRuntime.jsx("button", { type: "button", onClick: handleIsOpen, className: styles$3.wrapper__icon, "aria-label": "Open calendar", children: jsxRuntime.jsx(CalendarIcon, {}) }), jsxRuntime.jsx("input", { id: inputId, type: "text", placeholder: placeholder ?? 'Choose Date', value: value, className: styles$3.wrapper__field, onChange: handleChange, maxLength: MAX_DATE_LENGTH }), jsxRuntime.jsx("button", { type: "button", onClick: onClear, className: clearButtonStyles, "aria-label": "Clear input", children: jsxRuntime.jsx(CloseIcon, {}) })] }), error && jsxRuntime.jsx("p", { className: styles$3.wrapper__error, children: error })] }));
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
var css_248z$2 = ".CalendarWithInput-module_wrapper__vT2HK{align-items:flex-start;display:flex;flex-direction:column;position:relative}.CalendarWithInput-module_wrapper__calendar__6gnjs{margin-top:12px;position:absolute;top:100%;z-index:1}";
|
|
849
|
+
var styles$2 = {"wrapper":"CalendarWithInput-module_wrapper__vT2HK","wrapper__calendar":"CalendarWithInput-module_wrapper__calendar__6gnjs"};
|
|
850
|
+
styleInject(css_248z$2);
|
|
851
|
+
|
|
852
|
+
const service = new DatepickerService();
|
|
853
|
+
const CalendarWithInput = (props) => {
|
|
854
|
+
const [error, setError] = react.useState('');
|
|
855
|
+
const [inputValue, setInputValue] = react.useState(service.getInputValue());
|
|
856
|
+
const [viewModel, setViewModel] = react.useState(service.getViewModel());
|
|
857
|
+
const [isOpenCalendar, setIsOpenCalendar] = react.useState(false);
|
|
858
|
+
const handleInputChange = (value) => {
|
|
859
|
+
setInputValue(value);
|
|
860
|
+
const { error } = service.handleInputChange(value);
|
|
861
|
+
setError(error);
|
|
862
|
+
};
|
|
863
|
+
const handleClear = () => {
|
|
864
|
+
service.setSelectedDate(null);
|
|
865
|
+
setIsOpenCalendar(false);
|
|
866
|
+
};
|
|
867
|
+
const handleIsOpen = () => {
|
|
868
|
+
setIsOpenCalendar((pr) => !pr);
|
|
869
|
+
};
|
|
870
|
+
react.useEffect(() => {
|
|
871
|
+
const unsubscribe = service.subscribe(() => {
|
|
872
|
+
setViewModel(service.getViewModel());
|
|
873
|
+
setInputValue(service.getInputValue());
|
|
874
|
+
});
|
|
875
|
+
return () => {
|
|
876
|
+
unsubscribe();
|
|
877
|
+
};
|
|
878
|
+
}, []);
|
|
879
|
+
const themed = getTheme(props?.darkThemed);
|
|
880
|
+
return (jsxRuntime.jsx(ErrorBoundary, { children: jsxRuntime.jsxs("div", { "data-theme": themed, className: styles$2.wrapper, children: [jsxRuntime.jsx(Input, { value: inputValue, onChange: handleInputChange, onClear: handleClear, handleIsOpen: handleIsOpen, error: error, ...props }), isOpenCalendar && (jsxRuntime.jsx("div", { className: styles$2.wrapper__calendar, children: jsxRuntime.jsx(BaseCalendar, { ...viewModel }) }))] }) }));
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
var css_248z$1 = ".Portal-module_input-wrapper__IpAvQ,.Portal-module_input-wrapper__input__o75-6{align-items:center;display:flex;justify-content:center}.Portal-module_header__Bnty3,.Portal-module_todo-list__item__zX8SK{align-items:center;display:flex;justify-content:space-between}.Portal-module_portal__ArsYf,.Portal-module_todo-list__a-prJ{align-items:center;display:flex;flex-direction:column;justify-content:flex-start}.Portal-module_portal__ArsYf{background-color:#fff;position:absolute}[data-theme=dark] .Portal-module_portal__ArsYf{background-color:#333}.Portal-module_portal__ArsYf{color:#333}[data-theme=dark] .Portal-module_portal__ArsYf{color:#f1f1f1}.Portal-module_portal__ArsYf{border:1px solid #f1f1f1;border-radius:8px;max-height:230px;overflow-y:auto;padding:8px;z-index:10}.Portal-module_portal__ArsYf::-webkit-scrollbar{background-color:#f1f1f1;display:block;height:8px;width:8px}.Portal-module_portal__ArsYf::-webkit-scrollbar-thumb{background-color:rgba(47,128,237,.102);border-radius:8px}.Portal-module_portal__ArsYf::-webkit-scrollbar-thumb:hover{background-color:rgba(47,128,237,.6)}.Portal-module_header__Bnty3{width:100%}.Portal-module_header__title__yBt3Z{color:#333;font-size:14px;font-weight:600;margin:0 0 10px}[data-theme=dark] .Portal-module_header__title__yBt3Z{color:#f1f1f1}.Portal-module_header__close__2AgM6{background:none;border:none;color:#d32f2f;cursor:pointer;font-size:20px;opacity:.6;padding:0 8px;transition:opacity .2s}.Portal-module_header__close__2AgM6:hover{opacity:1}.Portal-module_input-wrapper__input__o75-6{background-color:#fff}[data-theme=dark] .Portal-module_input-wrapper__input__o75-6{background-color:#333}.Portal-module_input-wrapper__input__o75-6{color:#333}[data-theme=dark] .Portal-module_input-wrapper__input__o75-6{color:#f1f1f1}.Portal-module_input-wrapper__input__o75-6{border:1px solid #f1f1f1;border-radius:8px 0 0 8px;height:24px;margin:4px 0;outline:none;width:180px}.Portal-module_input-wrapper__button__PggCE{background-color:#2f80ed;border:1px solid #2f80ed;border-radius:0 8px 8px 0;color:#fff;cursor:pointer;height:28px;padding:0 8px;transition:background-color .2s}.Portal-module_todo-list__a-prJ{margin-top:10px;max-height:300px;max-width:200px}.Portal-module_todo-list__empty__nHBxJ{color:#aaa;font-size:14px;text-align:center}.Portal-module_todo-list__item__zX8SK{background-color:#fff;min-width:180px;width:100%}[data-theme=dark] .Portal-module_todo-list__item__zX8SK{background-color:#333}.Portal-module_todo-list__item__zX8SK{border-radius:8px;cursor:pointer;margin-bottom:8px;padding:8px 10px;transition:all .2s}.Portal-module_todo-list__item__zX8SK:hover{background-color:rgba(47,128,237,.6);color:#fff;transform:translateX(2px)}.Portal-module_todo-list__text__Z-AHf{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:#333;display:-webkit-box;flex:1;font-size:14px;height:100%;line-height:1.5;margin-right:10px;overflow:hidden;text-overflow:ellipsis;word-break:break-word}[data-theme=dark] .Portal-module_todo-list__text__Z-AHf{color:#f1f1f1}.Portal-module_todo-list__delete__2fMUG{background:none;border:none;color:#333;cursor:pointer;font-size:16px;opacity:.6;padding:0;transition:opacity .2s}[data-theme=dark] .Portal-module_todo-list__delete__2fMUG{color:#f1f1f1}.Portal-module_todo-list__delete__2fMUG:hover{opacity:1}.Portal-module_todo-list__confirm-no__PBqOA,.Portal-module_todo-list__confirm-yes__zfzbp{border:none;border-radius:4px;cursor:pointer;font-size:11px;margin-left:4px;padding:4px 8px;transition:opacity .2s}.Portal-module_todo-list__confirm-no__PBqOA:hover,.Portal-module_todo-list__confirm-yes__zfzbp:hover{background-color:#fff;transform:scale(1.05)}";
|
|
884
|
+
var styles$1 = {"input-wrapper":"Portal-module_input-wrapper__IpAvQ","input-wrapper__input":"Portal-module_input-wrapper__input__o75-6","header":"Portal-module_header__Bnty3","todo-list__item":"Portal-module_todo-list__item__zX8SK","portal":"Portal-module_portal__ArsYf","todo-list":"Portal-module_todo-list__a-prJ","header__title":"Portal-module_header__title__yBt3Z","header__close":"Portal-module_header__close__2AgM6","input-wrapper__button":"Portal-module_input-wrapper__button__PggCE","todo-list__empty":"Portal-module_todo-list__empty__nHBxJ","todo-list__text":"Portal-module_todo-list__text__Z-AHf","todo-list__delete":"Portal-module_todo-list__delete__2fMUG","todo-list__confirm-yes":"Portal-module_todo-list__confirm-yes__zfzbp","todo-list__confirm-no":"Portal-module_todo-list__confirm-no__PBqOA"};
|
|
885
|
+
styleInject(css_248z$1);
|
|
886
|
+
|
|
887
|
+
const containerCreate = (themed) => {
|
|
888
|
+
if (typeof document === 'undefined')
|
|
889
|
+
return null;
|
|
890
|
+
const div = document.createElement('div');
|
|
891
|
+
div.id = `portal-root-${Math.random()}`;
|
|
892
|
+
div.setAttribute('data-theme', themed);
|
|
893
|
+
div.ariaLabel = 'todo-portal';
|
|
894
|
+
return div;
|
|
895
|
+
};
|
|
896
|
+
const Portal = (props) => {
|
|
897
|
+
const [inputValue, setInputValue] = react.useState('');
|
|
898
|
+
const [todoToDelete, setTodoToDelete] = react.useState(null);
|
|
899
|
+
const { anchorEl, portalRef, isOpenPortal, onAddTodo, onClose, selectedDate, getTodos, onDeleteTodo, themed, } = props;
|
|
900
|
+
const container = react.useMemo(() => containerCreate(themed), [themed]);
|
|
901
|
+
const todos = selectedDate ? getTodos(selectedDate) : [];
|
|
902
|
+
react.useEffect(() => {
|
|
903
|
+
if (!container)
|
|
904
|
+
return;
|
|
905
|
+
document.body.appendChild(container);
|
|
906
|
+
return () => {
|
|
907
|
+
document.body.removeChild(container);
|
|
908
|
+
};
|
|
909
|
+
}, [container]);
|
|
910
|
+
const handleClickInside = (event) => {
|
|
911
|
+
event.stopPropagation();
|
|
912
|
+
};
|
|
913
|
+
const getPosition = () => {
|
|
914
|
+
if (!anchorEl)
|
|
915
|
+
return { top: '', left: '' };
|
|
916
|
+
const { bottom, left } = anchorEl.getBoundingClientRect();
|
|
917
|
+
const buttonWidth = anchorEl.offsetWidth || 0;
|
|
918
|
+
const topPosition = `${bottom + window.scrollY}px`;
|
|
919
|
+
let leftPosition = left + window.scrollX + buttonWidth;
|
|
920
|
+
return {
|
|
921
|
+
top: topPosition,
|
|
922
|
+
left: `${leftPosition}px`,
|
|
923
|
+
};
|
|
924
|
+
};
|
|
925
|
+
const handleAdd = () => {
|
|
926
|
+
if (!inputValue.trim() || !selectedDate)
|
|
927
|
+
return;
|
|
928
|
+
onAddTodo(selectedDate, inputValue);
|
|
929
|
+
setInputValue('');
|
|
930
|
+
};
|
|
931
|
+
const askDeleteConfirmation = (todo) => () => {
|
|
932
|
+
setTodoToDelete(todo);
|
|
933
|
+
};
|
|
934
|
+
const cancelDelete = () => {
|
|
935
|
+
setTodoToDelete(null);
|
|
936
|
+
};
|
|
937
|
+
const confirmDelete = () => {
|
|
938
|
+
if (selectedDate && todoToDelete) {
|
|
939
|
+
onDeleteTodo(selectedDate, todoToDelete);
|
|
940
|
+
setTodoToDelete(null);
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
const handleInputChange = (e) => {
|
|
944
|
+
setInputValue(e.target.value);
|
|
945
|
+
};
|
|
946
|
+
const portalPosition = getPosition();
|
|
947
|
+
if (!isOpenPortal || !container)
|
|
948
|
+
return null;
|
|
949
|
+
return reactDom.createPortal(jsxRuntime.jsxs("article", { ref: portalRef, onClick: handleClickInside, className: styles$1.portal, style: {
|
|
950
|
+
position: 'absolute',
|
|
951
|
+
top: portalPosition.top,
|
|
952
|
+
left: portalPosition.left,
|
|
953
|
+
}, children: [jsxRuntime.jsxs("div", { className: styles$1['header'], children: [jsxRuntime.jsx("p", { className: styles$1['header__title'], children: "TO DO" }), jsxRuntime.jsx("button", { className: styles$1['header__close'], onClick: onClose, "aria-label": "Close portal", children: "\u00D7" })] }), jsxRuntime.jsxs("div", { className: styles$1['input-wrapper'], children: [jsxRuntime.jsx("input", { className: styles$1['input-wrapper__input'], type: "text", value: inputValue, onChange: handleInputChange }), jsxRuntime.jsx("button", { className: styles$1['input-wrapper__button'], type: "button", onClick: handleAdd, children: "Add" })] }), jsxRuntime.jsx("div", { className: styles$1['todo-list'], children: todos?.length === 0 ? (jsxRuntime.jsx("div", { className: styles$1['todo-list__empty'], children: "No tasks yet. Add one!" })) : (todos?.map((todo) => (jsxRuntime.jsx("div", { className: styles$1['todo-list__item'], children: todoToDelete === todo ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: styles$1['todo-list__text'], children: "Are you sure?" }), jsxRuntime.jsx("button", { onClick: confirmDelete, className: styles$1['todo-list__confirm-yes'], children: "Yes" }), jsxRuntime.jsx("button", { onClick: cancelDelete, className: styles$1['todo-list__confirm-no'], children: "No" })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [' ', jsxRuntime.jsx("span", { className: styles$1['todo-list__text'], children: todo }), jsxRuntime.jsx("button", { className: styles$1['todo-list__delete'], onClick: askDeleteConfirmation(todo), "aria-label": "Delete task", children: "\u00D7" })] })) }, `todo-item-${todo}`)))) })] }), container);
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
const useCalendarPortal = () => {
|
|
957
|
+
const [isOpenPortal, setIsOpenPortal] = react.useState(false);
|
|
958
|
+
const [selectedDate, setSelectedDate] = react.useState(null);
|
|
959
|
+
const portalRef = react.useRef(null);
|
|
960
|
+
const [anchorEl, setAnchorEl] = react.useState(null);
|
|
961
|
+
const openPortal = react.useCallback((date, element) => {
|
|
962
|
+
setSelectedDate(date);
|
|
963
|
+
setAnchorEl(element);
|
|
964
|
+
setIsOpenPortal(true);
|
|
965
|
+
}, []);
|
|
966
|
+
const closePortal = react.useCallback(() => {
|
|
967
|
+
setIsOpenPortal(false);
|
|
968
|
+
}, []);
|
|
969
|
+
react.useEffect(() => {
|
|
970
|
+
const handleClickOutside = (event) => {
|
|
971
|
+
if (!isOpenPortal)
|
|
972
|
+
return;
|
|
973
|
+
const target = event.target;
|
|
974
|
+
if (!portalRef.current?.contains(target) && !anchorEl?.contains(target)) {
|
|
975
|
+
closePortal();
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
979
|
+
return () => {
|
|
980
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
981
|
+
};
|
|
982
|
+
}, [isOpenPortal, anchorEl, closePortal]);
|
|
983
|
+
return { isOpenPortal, selectedDate, anchorEl, portalRef, openPortal, closePortal };
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
const CalendarWithTodo = (config) => {
|
|
987
|
+
const { service, viewModel } = useCalendarService(config, true);
|
|
988
|
+
const portal = useCalendarPortal();
|
|
989
|
+
const onDayClickDouble = (date, event) => {
|
|
990
|
+
if (!event)
|
|
991
|
+
return;
|
|
992
|
+
service.setSelectedDate(date);
|
|
993
|
+
portal.openPortal(date, event.currentTarget);
|
|
994
|
+
};
|
|
995
|
+
const onDayClick = (date, event) => {
|
|
996
|
+
if (!event)
|
|
997
|
+
return;
|
|
998
|
+
service.setSelectedDate(date);
|
|
999
|
+
};
|
|
1000
|
+
const handleAddTodo = (date, text) => service.addTodo(date, text);
|
|
1001
|
+
const handleDeleteTodo = (date, todo) => service.deleteTodo(date, todo);
|
|
1002
|
+
const handleGetTodo = (date) => service.getTodosByDate(date);
|
|
1003
|
+
const themed = getTheme(config?.darkThemed);
|
|
1004
|
+
return (jsxRuntime.jsx(ErrorBoundary, { children: jsxRuntime.jsxs("article", { "data-theme": themed, children: [jsxRuntime.jsx(BaseCalendar, { ...viewModel, onDayClick: onDayClick, onDayClickDouble: onDayClickDouble }), jsxRuntime.jsx(Portal, { id: `portal-${portal.selectedDate?.getTime()}`, isOpenPortal: portal.isOpenPortal, selectedDate: portal.selectedDate, anchorEl: portal.anchorEl, portalRef: portal.portalRef, onClose: portal.closePortal, onAddTodo: handleAddTodo, onDeleteTodo: handleDeleteTodo, getTodos: handleGetTodo, themed: themed })] }) }));
|
|
1005
|
+
};
|
|
1006
|
+
|
|
1007
|
+
const useDateRangePicker = (config) => {
|
|
1008
|
+
const [_activeInput, setActiveInput] = react.useState(INPUT_TYPE.START);
|
|
1009
|
+
const [localStartDate, setLocalStartDate] = react.useState('');
|
|
1010
|
+
const [localEndDate, setLocalEndDate] = react.useState('');
|
|
1011
|
+
const [error, setError] = react.useState('');
|
|
1012
|
+
const [isOpenCalendar, setIsOpenCalendar] = react.useState(false);
|
|
1013
|
+
const service = react.useMemo(() => new DatepickerService(config), [config]);
|
|
1014
|
+
const [viewModel, setViewModel] = react.useState(service.getViewModel());
|
|
1015
|
+
react.useEffect(() => {
|
|
1016
|
+
const unsubscribe = service.subscribe(() => {
|
|
1017
|
+
setViewModel(service.getViewModel());
|
|
1018
|
+
const range = service.getRange();
|
|
1019
|
+
if (range.start) {
|
|
1020
|
+
setLocalStartDate(maskDate(range.start.toLocaleDateString()));
|
|
1021
|
+
}
|
|
1022
|
+
else {
|
|
1023
|
+
setLocalStartDate('');
|
|
1024
|
+
}
|
|
1025
|
+
if (range.end) {
|
|
1026
|
+
setLocalEndDate(maskDate(range.end.toLocaleDateString()));
|
|
1027
|
+
}
|
|
1028
|
+
else {
|
|
1029
|
+
setLocalEndDate('');
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
return () => {
|
|
1033
|
+
unsubscribe();
|
|
1034
|
+
};
|
|
1035
|
+
}, [service]);
|
|
1036
|
+
const range = service.getRange();
|
|
1037
|
+
const setStartDateDirect = (date) => {
|
|
1038
|
+
service.setRangeStart?.(date);
|
|
1039
|
+
const currentEnd = service.getRangeEnd?.();
|
|
1040
|
+
if (currentEnd && date > currentEnd) {
|
|
1041
|
+
service.setRangeEnd?.(null);
|
|
1042
|
+
setLocalEndDate('');
|
|
1043
|
+
}
|
|
1044
|
+
service.notify?.();
|
|
1045
|
+
};
|
|
1046
|
+
const setEndDateDirect = (date) => {
|
|
1047
|
+
const currentStart = service.getRangeStart?.();
|
|
1048
|
+
if (currentStart && date < currentStart) {
|
|
1049
|
+
setError('End date cannot be earlier than start date');
|
|
1050
|
+
return false;
|
|
1051
|
+
}
|
|
1052
|
+
service.setRangeEnd?.(date);
|
|
1053
|
+
service.notify?.();
|
|
1054
|
+
return true;
|
|
1055
|
+
};
|
|
1056
|
+
const handleManualInput = (value, inputType) => {
|
|
1057
|
+
const maskedValue = maskDate(value);
|
|
1058
|
+
if (inputType === INPUT_TYPE.START) {
|
|
1059
|
+
setLocalStartDate(maskedValue);
|
|
1060
|
+
}
|
|
1061
|
+
else {
|
|
1062
|
+
setLocalEndDate(maskedValue);
|
|
1063
|
+
}
|
|
1064
|
+
if (maskedValue.length === START_DATE_INDEX_) {
|
|
1065
|
+
setError('');
|
|
1066
|
+
if (inputType === INPUT_TYPE.START) {
|
|
1067
|
+
service.setRangeStart?.(null);
|
|
1068
|
+
}
|
|
1069
|
+
else {
|
|
1070
|
+
service.setRangeEnd?.(null);
|
|
1071
|
+
}
|
|
1072
|
+
service.notify?.();
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
const validationResult = validateDateInput(maskedValue);
|
|
1076
|
+
if (maskedValue.length < MAX_INPUT_LENGTH) {
|
|
1077
|
+
setError(validationResult.error);
|
|
1078
|
+
}
|
|
1079
|
+
else {
|
|
1080
|
+
if (validationResult.isValid && validationResult.date) {
|
|
1081
|
+
if (inputType === INPUT_TYPE.START) {
|
|
1082
|
+
setStartDateDirect(validationResult.date);
|
|
1083
|
+
setError('');
|
|
1084
|
+
if (!range.end) {
|
|
1085
|
+
setActiveInput(INPUT_TYPE.END);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
else {
|
|
1089
|
+
const success = setEndDateDirect(validationResult.date);
|
|
1090
|
+
if (success) {
|
|
1091
|
+
setError('');
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
setError(validationResult.error || 'Invalid date');
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
const handleInputFocus = (input) => {
|
|
1101
|
+
setActiveInput(input);
|
|
1102
|
+
setIsOpenCalendar(true);
|
|
1103
|
+
};
|
|
1104
|
+
const handleClear = (inputType) => {
|
|
1105
|
+
if (inputType === INPUT_TYPE.START) {
|
|
1106
|
+
service.setRangeStart?.(null);
|
|
1107
|
+
setLocalStartDate('');
|
|
1108
|
+
}
|
|
1109
|
+
else if (inputType === INPUT_TYPE.END) {
|
|
1110
|
+
service.setRangeEnd?.(null);
|
|
1111
|
+
setLocalEndDate('');
|
|
1112
|
+
}
|
|
1113
|
+
else if (!inputType) {
|
|
1114
|
+
service.clearRange?.();
|
|
1115
|
+
setLocalStartDate('');
|
|
1116
|
+
setLocalEndDate('');
|
|
1117
|
+
setIsOpenCalendar(false);
|
|
1118
|
+
}
|
|
1119
|
+
setError('');
|
|
1120
|
+
service.notify?.();
|
|
1121
|
+
};
|
|
1122
|
+
const toggleCalendar = () => {
|
|
1123
|
+
setIsOpenCalendar((prev) => !prev);
|
|
1124
|
+
};
|
|
1125
|
+
const createInputConfig = (type, localValue, placeholder) => ({
|
|
1126
|
+
value: localValue,
|
|
1127
|
+
onChange: (val) => handleManualInput(val, type),
|
|
1128
|
+
onFocus: () => handleInputFocus(type),
|
|
1129
|
+
placeholder,
|
|
1130
|
+
onClear: () => handleClear(type),
|
|
1131
|
+
});
|
|
1132
|
+
return {
|
|
1133
|
+
viewModel,
|
|
1134
|
+
isOpenCalendar,
|
|
1135
|
+
start: createInputConfig(INPUT_TYPE.START, localStartDate, 'Start Date'),
|
|
1136
|
+
end: createInputConfig(INPUT_TYPE.END, localEndDate, 'End Date'),
|
|
1137
|
+
error,
|
|
1138
|
+
onClear: handleClear,
|
|
1139
|
+
toggleCalendar,
|
|
1140
|
+
};
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1143
|
+
var css_248z = ".RangeCalender-module_container__VOLUe,.RangeCalender-module_container__inputs__36FES{align-items:flex-start;display:flex;flex-direction:column}.RangeCalender-module_container__VOLUe{height:100%;width:auto}.RangeCalender-module_container__clear__vlhtG{background-color:#fff;color:#333}[data-theme=dark] .RangeCalender-module_container__clear__vlhtG{background-color:#333;color:#f1f1f1}.RangeCalender-module_container__clear__vlhtG{border:1px solid #f1f1f1;border-radius:8px;font-size:12px;font-weight:600;min-height:36px;width:250px}";
|
|
1144
|
+
var styles = {"container":"RangeCalender-module_container__VOLUe","container__inputs":"RangeCalender-module_container__inputs__36FES","container__clear":"RangeCalender-module_container__clear__vlhtG"};
|
|
1145
|
+
styleInject(css_248z);
|
|
1146
|
+
|
|
1147
|
+
const RangeCalender = (_config) => {
|
|
1148
|
+
const { start, end, viewModel, error, onClear, isOpenCalendar, toggleCalendar } = useDateRangePicker(_config);
|
|
1149
|
+
const handleClearAll = () => {
|
|
1150
|
+
onClear();
|
|
1151
|
+
};
|
|
1152
|
+
const handleDayClick = (date, _event) => {
|
|
1153
|
+
viewModel.onDayRangeClick(date);
|
|
1154
|
+
};
|
|
1155
|
+
const themed = getTheme(_config?.darkThemed);
|
|
1156
|
+
return (jsxRuntime.jsx(ErrorBoundary, { children: jsxRuntime.jsx("article", { "data-theme": themed, children: jsxRuntime.jsxs("div", { className: styles.container, children: [jsxRuntime.jsxs("div", { className: styles['container__inputs'], children: [jsxRuntime.jsx(Input, { handleIsOpen: toggleCalendar, ...start, error: error }), jsxRuntime.jsx(Input, { handleIsOpen: toggleCalendar, ...end, error: error })] }), isOpenCalendar && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(BaseCalendar, { ...viewModel, onDayClick: handleDayClick }), jsxRuntime.jsx("button", { className: styles['container__clear'], type: "button", onClick: handleClearAll, children: "Clear All" })] }))] }) }) }));
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
exports.Calendar = Calendar;
|
|
1160
|
+
exports.CalendarWithInput = CalendarWithInput;
|
|
1161
|
+
exports.CalendarWithTodo = CalendarWithTodo;
|
|
1162
|
+
exports.RangeCalender = RangeCalender;
|
|
1163
|
+
//# sourceMappingURL=index.cjs.js.map
|