@alfalab/core-components-calendar-range 7.3.28 → 7.5.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/components/divider/Component.js +1 -1
- package/components/divider/index.css +14 -11
- package/cssm/components/divider/index.module.css +6 -3
- package/cssm/views/index.module.css +3 -2
- package/esm/components/divider/Component.js +1 -1
- package/esm/components/divider/index.css +14 -11
- package/esm/index.module-26bd927f.js +4 -0
- package/esm/views/index.css +10 -9
- package/esm/views/popover.js +1 -1
- package/esm/views/static.js +1 -1
- package/index.module-2f1cd576.js +6 -0
- package/modern/components/divider/Component.js +1 -1
- package/modern/components/divider/index.css +14 -11
- package/modern/index.module-1b7b175c.js +4 -0
- package/modern/views/index.css +10 -9
- package/modern/views/popover.js +1 -1
- package/modern/views/static.js +1 -1
- package/moderncssm/Component.d.ts +90 -0
- package/moderncssm/Component.js +10 -0
- package/moderncssm/components/divider/Component.d.ts +8 -0
- package/moderncssm/components/divider/Component.js +14 -0
- package/moderncssm/components/divider/index.d.ts +1 -0
- package/moderncssm/components/divider/index.js +1 -0
- package/moderncssm/components/divider/index.module.css +55 -0
- package/moderncssm/hooks.d.ts +28 -0
- package/moderncssm/hooks.js +134 -0
- package/moderncssm/index.d.ts +1 -0
- package/moderncssm/index.js +1 -0
- package/moderncssm/utils.d.ts +2 -0
- package/moderncssm/utils.js +3 -0
- package/moderncssm/views/index.module.css +42 -0
- package/moderncssm/views/popover.d.ts +5 -0
- package/moderncssm/views/popover.js +128 -0
- package/moderncssm/views/static.d.ts +10 -0
- package/moderncssm/views/static.js +176 -0
- package/package.json +6 -6
- package/src/components/divider/index.module.css +3 -3
- package/src/views/index.module.css +3 -3
- package/views/index.css +10 -9
- package/views/popover.js +1 -1
- package/views/static.js +1 -1
- package/esm/index.module-6b92c205.js +0 -4
- package/index.module-ebe5324e.js +0 -6
- package/modern/index.module-4b1345e2.js +0 -4
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { CalendarInputProps } from "@alfalab/core-components-calendar-input";
|
|
3
|
+
type CalendarRangeProps = {
|
|
4
|
+
/**
|
|
5
|
+
* Дополнительный класс
|
|
6
|
+
*/
|
|
7
|
+
className?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Значение инпута (используется и для календаря)
|
|
10
|
+
*/
|
|
11
|
+
valueFrom?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Значение инпута (используется и для календаря)
|
|
14
|
+
*/
|
|
15
|
+
valueTo?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Месяц в календаре по умолчанию
|
|
18
|
+
*/
|
|
19
|
+
defaultMonth?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Минимальная дата, доступная для выбора (timestamp)
|
|
22
|
+
*/
|
|
23
|
+
minDate?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Максимальная дата, доступная для выбора (timestamp)
|
|
26
|
+
*/
|
|
27
|
+
maxDate?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Обработчик изменения даты от
|
|
30
|
+
*/
|
|
31
|
+
onDateFromChange?: (payload: {
|
|
32
|
+
date: number | null;
|
|
33
|
+
value: string;
|
|
34
|
+
}) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Обработчик изменения даты до
|
|
37
|
+
*/
|
|
38
|
+
onDateToChange?: (payload: {
|
|
39
|
+
date: number | null;
|
|
40
|
+
value: string;
|
|
41
|
+
}) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Обработчик изменения
|
|
44
|
+
*/
|
|
45
|
+
onChange?: (payload: {
|
|
46
|
+
dateFrom: number | null;
|
|
47
|
+
valueFrom: string;
|
|
48
|
+
dateTo: number | null;
|
|
49
|
+
valueTo: string;
|
|
50
|
+
}) => void;
|
|
51
|
+
/**
|
|
52
|
+
* Коллбэк, срабатывающий при возникновении ошибок валидации дат внутри компонента.
|
|
53
|
+
*/
|
|
54
|
+
onError?: (hasError: boolean) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Список событий
|
|
57
|
+
*/
|
|
58
|
+
events?: Array<Date | number>;
|
|
59
|
+
/**
|
|
60
|
+
* Список выходных
|
|
61
|
+
*/
|
|
62
|
+
offDays?: Array<Date | number>;
|
|
63
|
+
/**
|
|
64
|
+
* Пропсы для инпута даты от
|
|
65
|
+
*/
|
|
66
|
+
inputFromProps?: CalendarInputProps;
|
|
67
|
+
/**
|
|
68
|
+
* Пропсы для инпута даты до
|
|
69
|
+
*/
|
|
70
|
+
inputToProps?: CalendarInputProps;
|
|
71
|
+
/**
|
|
72
|
+
* Идентификатор для систем автоматизированного тестирования
|
|
73
|
+
*/
|
|
74
|
+
dataTestId?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Определяет, как рендерить календарь — в поповере или снизу инпута
|
|
77
|
+
*/
|
|
78
|
+
calendarPosition?: 'static' | 'popover';
|
|
79
|
+
/**
|
|
80
|
+
* calendarPosition = static
|
|
81
|
+
* Отображать начальный месяц слева или справа (влияет только на начальный рендер)
|
|
82
|
+
*/
|
|
83
|
+
defaultMonthPosition?: 'left' | 'right';
|
|
84
|
+
/**
|
|
85
|
+
* Возвращать невалидную дату для кастомной валидации
|
|
86
|
+
*/
|
|
87
|
+
returnInvalidDates?: boolean;
|
|
88
|
+
};
|
|
89
|
+
declare const CalendarRange: FC<CalendarRangeProps>;
|
|
90
|
+
export { CalendarRangeProps, CalendarRange };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CalendarRangePopover } from './views/popover.js';
|
|
3
|
+
import { CalendarRangeStatic } from './views/static.js';
|
|
4
|
+
|
|
5
|
+
const CalendarRange = ({ calendarPosition = 'static', ...restProps }) => {
|
|
6
|
+
const View = calendarPosition === 'popover' ? CalendarRangePopover : CalendarRangeStatic;
|
|
7
|
+
return React.createElement(View, { ...restProps });
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export { CalendarRange };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { CalendarRangeProps } from "../../Component";
|
|
3
|
+
type Props = {
|
|
4
|
+
inputFromProps?: CalendarRangeProps['inputFromProps'];
|
|
5
|
+
inputToProps?: CalendarRangeProps['inputToProps'];
|
|
6
|
+
};
|
|
7
|
+
declare const Divider: FC<Props>;
|
|
8
|
+
export { Divider };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
import styles from './index.module.css';
|
|
4
|
+
|
|
5
|
+
const Divider = ({ inputFromProps, inputToProps }) => {
|
|
6
|
+
const outer = inputFromProps?.label &&
|
|
7
|
+
inputFromProps?.labelView === 'outer' &&
|
|
8
|
+
inputToProps?.label &&
|
|
9
|
+
inputToProps?.labelView === 'outer';
|
|
10
|
+
const size = inputFromProps?.size || inputToProps?.size || 's';
|
|
11
|
+
return React.createElement("span", { className: cn(styles.component, styles[size], { [styles.outer]: outer }) });
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export { Divider };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Component";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Divider } from './Component.js';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* */
|
|
2
|
+
:root {
|
|
3
|
+
|
|
4
|
+
/* Кнопки выбора месяцев и годов */
|
|
5
|
+
|
|
6
|
+
/* Шапка */
|
|
7
|
+
|
|
8
|
+
/* День */
|
|
9
|
+
|
|
10
|
+
/* today */
|
|
11
|
+
|
|
12
|
+
/* highlighted */
|
|
13
|
+
|
|
14
|
+
/* holidays */
|
|
15
|
+
|
|
16
|
+
/* range */
|
|
17
|
+
|
|
18
|
+
/* selected */
|
|
19
|
+
|
|
20
|
+
/* disabled */
|
|
21
|
+
|
|
22
|
+
/* marker */
|
|
23
|
+
}
|
|
24
|
+
.component {
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: center;
|
|
28
|
+
width: 16px;
|
|
29
|
+
margin: var(--gap-0) var(--gap-8)
|
|
30
|
+
}
|
|
31
|
+
.component:after {
|
|
32
|
+
content: '';
|
|
33
|
+
display: block;
|
|
34
|
+
width: 100%;
|
|
35
|
+
height: 1px;
|
|
36
|
+
background-color: var(--color-light-neutral-translucent-1300);
|
|
37
|
+
}
|
|
38
|
+
.outer {
|
|
39
|
+
position: relative;
|
|
40
|
+
|
|
41
|
+
/* FormControl .above height + margin-bottom */
|
|
42
|
+
top: var(--gap-24);
|
|
43
|
+
}
|
|
44
|
+
.s {
|
|
45
|
+
height: var(--size-s-height);
|
|
46
|
+
}
|
|
47
|
+
.m {
|
|
48
|
+
height: var(--size-m-height);
|
|
49
|
+
}
|
|
50
|
+
.l {
|
|
51
|
+
height: var(--size-l-height);
|
|
52
|
+
}
|
|
53
|
+
.xl {
|
|
54
|
+
height: var(--size-xl-height);
|
|
55
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
declare function usePopoverViewMonthes({ dateFrom, dateTo, defaultMonth, resetKey, }: {
|
|
2
|
+
defaultMonth: number;
|
|
3
|
+
dateFrom: number | null;
|
|
4
|
+
dateTo: number | null;
|
|
5
|
+
resetKey?: number;
|
|
6
|
+
}): {
|
|
7
|
+
monthFrom: number | undefined;
|
|
8
|
+
monthTo: number | undefined;
|
|
9
|
+
handleMonthFromChange: (newMonthFrom: number) => void;
|
|
10
|
+
handleMonthToChange: (newMonthTo: number) => void;
|
|
11
|
+
};
|
|
12
|
+
declare function useStaticViewMonthes({ selectedFrom, selectedTo, defaultMonth, defaultMonthPosition, }: {
|
|
13
|
+
selectedFrom?: number;
|
|
14
|
+
selectedTo?: number;
|
|
15
|
+
defaultMonth: number;
|
|
16
|
+
defaultMonthPosition?: 'left' | 'right';
|
|
17
|
+
}): {
|
|
18
|
+
monthFrom: number;
|
|
19
|
+
monthTo: number;
|
|
20
|
+
handleMonthFromChange: (newMonthFrom: number) => void;
|
|
21
|
+
handleMonthToChange: (newMonthTo: number) => void;
|
|
22
|
+
};
|
|
23
|
+
declare function useSelectionProps(from?: number, to?: number, highlighted?: number): {
|
|
24
|
+
rangeComplete: boolean;
|
|
25
|
+
selectedFrom: number | undefined;
|
|
26
|
+
selectedTo: number | undefined;
|
|
27
|
+
};
|
|
28
|
+
export { usePopoverViewMonthes, useStaticViewMonthes, useSelectionProps };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect, useMemo } from 'react';
|
|
2
|
+
import addMonths from 'date-fns/addMonths';
|
|
3
|
+
import isEqual from 'date-fns/isEqual';
|
|
4
|
+
import max from 'date-fns/max';
|
|
5
|
+
import min from 'date-fns/min';
|
|
6
|
+
import startOfMonth from 'date-fns/startOfMonth';
|
|
7
|
+
import subMonths from 'date-fns/subMonths';
|
|
8
|
+
|
|
9
|
+
function usePopoverViewMonthes({ dateFrom, dateTo, defaultMonth, resetKey, }) {
|
|
10
|
+
const [monthFrom, setMonthFrom] = useState();
|
|
11
|
+
const [monthTo, setMonthTo] = useState();
|
|
12
|
+
const handleMonthFromChange = useCallback((newMonthFrom) => {
|
|
13
|
+
setMonthFrom(newMonthFrom);
|
|
14
|
+
if (!dateTo) {
|
|
15
|
+
setMonthTo(newMonthFrom);
|
|
16
|
+
}
|
|
17
|
+
}, [dateTo]);
|
|
18
|
+
const handleMonthToChange = useCallback((newMonthTo) => {
|
|
19
|
+
setMonthTo(newMonthTo);
|
|
20
|
+
if (!dateFrom) {
|
|
21
|
+
setMonthFrom(newMonthTo);
|
|
22
|
+
}
|
|
23
|
+
}, [dateFrom]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
setMonthFrom(dateFrom ? startOfMonth(dateFrom).getTime() : defaultMonth);
|
|
26
|
+
}, [defaultMonth, dateFrom, resetKey]);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
setMonthTo(dateTo ? startOfMonth(dateTo).getTime() : monthFrom);
|
|
29
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
30
|
+
}, [dateTo, resetKey]);
|
|
31
|
+
return {
|
|
32
|
+
monthFrom,
|
|
33
|
+
monthTo,
|
|
34
|
+
handleMonthFromChange,
|
|
35
|
+
handleMonthToChange,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function useStaticViewMonthes({ selectedFrom, selectedTo, defaultMonth, defaultMonthPosition, }) {
|
|
39
|
+
/**
|
|
40
|
+
* Если указана начальная дата — левый месяц равен ей, иначе используется дата конца.
|
|
41
|
+
* Если обе даты не указаны, то используется дефолтный месяц
|
|
42
|
+
*/
|
|
43
|
+
let initialMonthFrom = useMemo(() => startOfMonth(selectedFrom || selectedTo || defaultMonth).getTime(),
|
|
44
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
45
|
+
[]);
|
|
46
|
+
/**
|
|
47
|
+
* Правый месяц должен быть как минимум на 1 месяц больше левого
|
|
48
|
+
*/
|
|
49
|
+
let initialMonthTo = useMemo(() => max([
|
|
50
|
+
selectedTo ? startOfMonth(selectedTo) : 0,
|
|
51
|
+
addMonths(initialMonthFrom, 1),
|
|
52
|
+
]).getTime(),
|
|
53
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
54
|
+
[]);
|
|
55
|
+
if (defaultMonthPosition === 'right') {
|
|
56
|
+
initialMonthTo = initialMonthFrom;
|
|
57
|
+
initialMonthFrom = subMonths(initialMonthFrom, 1).getTime();
|
|
58
|
+
}
|
|
59
|
+
const [monthFrom, setMonthFrom] = useState(initialMonthFrom);
|
|
60
|
+
const [monthTo, setMonthTo] = useState(initialMonthTo);
|
|
61
|
+
const handleMonthFromChange = useCallback((newMonthFrom) => {
|
|
62
|
+
setMonthFrom(newMonthFrom);
|
|
63
|
+
if (monthTo && isEqual(newMonthFrom, monthTo)) {
|
|
64
|
+
const nextMonth = addMonths(newMonthFrom, 1).getTime();
|
|
65
|
+
setMonthTo(nextMonth);
|
|
66
|
+
}
|
|
67
|
+
}, [monthTo]);
|
|
68
|
+
const handleMonthToChange = useCallback((newMonthTo) => {
|
|
69
|
+
setMonthTo(newMonthTo);
|
|
70
|
+
if (monthFrom && isEqual(newMonthTo, monthFrom)) {
|
|
71
|
+
const prevMonth = subMonths(newMonthTo, 1).getTime();
|
|
72
|
+
setMonthFrom(prevMonth);
|
|
73
|
+
}
|
|
74
|
+
}, [monthFrom]);
|
|
75
|
+
// eslint-disable-next-line complexity
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const selectedFromMonth = selectedFrom ? startOfMonth(selectedFrom).getTime() : undefined;
|
|
78
|
+
const selectedToMonth = selectedTo ? startOfMonth(selectedTo).getTime() : undefined;
|
|
79
|
+
// Проверяем, показываются ли выбранные месяцы в левой или правой части компонента
|
|
80
|
+
const fromMonthOnLeft = selectedFromMonth && selectedFromMonth === monthFrom;
|
|
81
|
+
const fromMonthOnRight = selectedFromMonth && selectedFromMonth === monthTo;
|
|
82
|
+
const toMonthOnRight = selectedToMonth && selectedToMonth === monthTo;
|
|
83
|
+
const toMonthOnLeft = selectedToMonth && selectedToMonth === monthFrom;
|
|
84
|
+
const fromMonthOnScreen = fromMonthOnLeft || fromMonthOnRight;
|
|
85
|
+
const toMonthOnScreen = toMonthOnLeft || toMonthOnRight;
|
|
86
|
+
if (fromMonthOnLeft && toMonthOnLeft) {
|
|
87
|
+
setMonthTo(max([addMonths(selectedFromMonth, 1), monthTo]).getTime());
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (fromMonthOnRight && toMonthOnRight) {
|
|
91
|
+
setMonthFrom(min([subMonths(selectedToMonth, 1), monthFrom]).getTime());
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (selectedFromMonth && selectedToMonth) {
|
|
95
|
+
setMonthFrom(selectedFromMonth);
|
|
96
|
+
setMonthTo(max([addMonths(selectedFromMonth, 1), selectedToMonth]).getTime());
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (selectedFromMonth && !selectedToMonth && !fromMonthOnScreen) {
|
|
100
|
+
setMonthFrom(selectedFromMonth);
|
|
101
|
+
setMonthTo(max([addMonths(selectedFromMonth, 1), monthTo]).getTime());
|
|
102
|
+
}
|
|
103
|
+
if (selectedToMonth && !selectedFromMonth && !toMonthOnScreen) {
|
|
104
|
+
setMonthTo(selectedToMonth);
|
|
105
|
+
setMonthFrom(min([subMonths(selectedToMonth, 1), monthFrom]).getTime());
|
|
106
|
+
}
|
|
107
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
108
|
+
}, [selectedFrom, selectedTo]);
|
|
109
|
+
return {
|
|
110
|
+
monthFrom,
|
|
111
|
+
monthTo,
|
|
112
|
+
handleMonthFromChange,
|
|
113
|
+
handleMonthToChange,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function useSelectionProps(from, to, highlighted) {
|
|
117
|
+
return useMemo(() => {
|
|
118
|
+
if (from && to) {
|
|
119
|
+
return {
|
|
120
|
+
rangeComplete: true,
|
|
121
|
+
selectedFrom: min([from, to]).getTime(),
|
|
122
|
+
selectedTo: max([from, to]).getTime(),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const dates = [from, to, highlighted].filter((date) => date !== undefined);
|
|
126
|
+
return {
|
|
127
|
+
rangeComplete: false,
|
|
128
|
+
selectedFrom: from || dates.length === 2 ? min(dates).getTime() : undefined,
|
|
129
|
+
selectedTo: to || dates.length === 2 ? max(dates).getTime() : undefined,
|
|
130
|
+
};
|
|
131
|
+
}, [from, highlighted, to]);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export { usePopoverViewMonthes, useSelectionProps, useStaticViewMonthes };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Component";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { CalendarRange } from './Component.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* */
|
|
2
|
+
:root {
|
|
3
|
+
--calendar-inner-width: 280px;
|
|
4
|
+
|
|
5
|
+
/* Кнопки выбора месяцев и годов */
|
|
6
|
+
|
|
7
|
+
/* Шапка */
|
|
8
|
+
|
|
9
|
+
/* День */
|
|
10
|
+
|
|
11
|
+
/* today */
|
|
12
|
+
|
|
13
|
+
/* highlighted */
|
|
14
|
+
|
|
15
|
+
/* holidays */
|
|
16
|
+
|
|
17
|
+
/* range */
|
|
18
|
+
|
|
19
|
+
/* selected */
|
|
20
|
+
|
|
21
|
+
/* disabled */
|
|
22
|
+
|
|
23
|
+
/* marker */
|
|
24
|
+
}
|
|
25
|
+
.component {
|
|
26
|
+
display: flex
|
|
27
|
+
}
|
|
28
|
+
.component button[aria-selected='true'] {
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
}
|
|
31
|
+
.component *[class*='errorIcon_'] {
|
|
32
|
+
display: none;
|
|
33
|
+
}
|
|
34
|
+
.component *[class*='calendarIcon_'] {
|
|
35
|
+
margin-right: var(--gap-0);
|
|
36
|
+
}
|
|
37
|
+
.static .calendar {
|
|
38
|
+
width: var(--calendar-inner-width)
|
|
39
|
+
}
|
|
40
|
+
.static .calendar > div:first-child {
|
|
41
|
+
padding: var(--gap-16) var(--gap-0) var(--gap-12);
|
|
42
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { CalendarRangeProps } from "../Component";
|
|
3
|
+
type CalendarRangePopoverProps = Omit<CalendarRangeProps, 'calendarPosition'>;
|
|
4
|
+
declare const CalendarRangePopover: FC<CalendarRangePopoverProps>;
|
|
5
|
+
export { CalendarRangePopoverProps, CalendarRangePopover };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import React, { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
import startOfMonth from 'date-fns/startOfMonth';
|
|
4
|
+
import { CalendarInput } from '@alfalab/core-components-calendar-input/moderncssm';
|
|
5
|
+
import { isValidInputValue, parseDateString } from '@alfalab/core-components-calendar-input/moderncssm/shared';
|
|
6
|
+
import { isCompleteDateInput } from '@alfalab/core-components-date-input/moderncssm';
|
|
7
|
+
import { useDidUpdateEffect } from '@alfalab/hooks';
|
|
8
|
+
import { Divider } from '../components/divider/Component.js';
|
|
9
|
+
import { usePopoverViewMonthes } from '../hooks.js';
|
|
10
|
+
import styles from './index.module.css';
|
|
11
|
+
|
|
12
|
+
const CalendarRangePopover = ({ className, defaultMonth = startOfMonth(new Date()).getTime(), minDate, maxDate, valueFrom = '', valueTo = '', onDateFromChange = () => null, onDateToChange = () => null, onChange = () => null, onError, inputFromProps = {}, inputToProps = {}, offDays, events, returnInvalidDates = false, dataTestId, }) => {
|
|
13
|
+
const [inputFromValue, setInputFromValue] = useState(valueFrom);
|
|
14
|
+
const [inputToValue, setInputToValue] = useState(valueTo);
|
|
15
|
+
/**
|
|
16
|
+
* Ключ для сброса календарей
|
|
17
|
+
* Пользователь открыл календарь, изменил месяц, но ничего не выбрал
|
|
18
|
+
* — при следующем открытии в календаре будет установлен начальный месяц
|
|
19
|
+
*/
|
|
20
|
+
const [resetKey, setResetKey] = useState(0);
|
|
21
|
+
const dateFrom = isValidInputValue(inputFromValue, minDate, maxDate, offDays)
|
|
22
|
+
? parseDateString(inputFromValue).getTime()
|
|
23
|
+
: null;
|
|
24
|
+
const dateTo = isValidInputValue(inputToValue, dateFrom || minDate, maxDate, offDays)
|
|
25
|
+
? parseDateString(inputToValue).getTime()
|
|
26
|
+
: null;
|
|
27
|
+
const [inputFromInvalid, setInputFromInvalid] = useState(isCompleteDateInput(inputFromValue) && dateFrom === null);
|
|
28
|
+
const [inputToInvalid, setInputToInvalid] = useState(isCompleteDateInput(inputToValue) && dateTo === null);
|
|
29
|
+
const bothInvalid = isCompleteDateInput(inputFromValue) &&
|
|
30
|
+
isCompleteDateInput(inputToValue) &&
|
|
31
|
+
parseDateString(inputFromValue).getTime() > parseDateString(inputToValue).getTime();
|
|
32
|
+
const hasValidateError = inputFromInvalid || inputToInvalid || bothInvalid;
|
|
33
|
+
const { monthFrom, monthTo, handleMonthFromChange, handleMonthToChange } = usePopoverViewMonthes({
|
|
34
|
+
dateFrom,
|
|
35
|
+
dateTo,
|
|
36
|
+
defaultMonth,
|
|
37
|
+
resetKey,
|
|
38
|
+
});
|
|
39
|
+
const handleValidInputFrom = useCallback(() => {
|
|
40
|
+
setInputFromInvalid(inputFromValue !== '' && !isValidInputValue(inputFromValue, minDate, maxDate, offDays));
|
|
41
|
+
}, [inputFromValue, maxDate, minDate, offDays]);
|
|
42
|
+
const handleValidInputTo = useCallback(() => {
|
|
43
|
+
setInputToInvalid(inputToValue !== '' &&
|
|
44
|
+
!isValidInputValue(inputToValue, dateFrom || minDate, maxDate, offDays));
|
|
45
|
+
}, [dateFrom, inputToValue, maxDate, minDate, offDays]);
|
|
46
|
+
const handleInputFromChange = (event, payload) => {
|
|
47
|
+
setInputFromValue(payload.value);
|
|
48
|
+
inputFromProps.onInputChange?.(event, payload);
|
|
49
|
+
};
|
|
50
|
+
const handleInputToChange = (event, payload) => {
|
|
51
|
+
setInputToValue(payload.value);
|
|
52
|
+
inputToProps.onInputChange?.(event, payload);
|
|
53
|
+
};
|
|
54
|
+
const handleInputFromBlur = useCallback(() => {
|
|
55
|
+
handleValidInputFrom();
|
|
56
|
+
setResetKey(+new Date());
|
|
57
|
+
}, [handleValidInputFrom]);
|
|
58
|
+
const handleInputToBlur = useCallback(() => {
|
|
59
|
+
handleValidInputTo();
|
|
60
|
+
setResetKey(+new Date());
|
|
61
|
+
}, [handleValidInputTo]);
|
|
62
|
+
const handleFromChange = useCallback((_, payload) => {
|
|
63
|
+
setInputFromValue(payload.value);
|
|
64
|
+
}, []);
|
|
65
|
+
const handleToChange = useCallback((_, payload) => {
|
|
66
|
+
setInputToValue(payload.value);
|
|
67
|
+
}, []);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
setInputFromValue(valueFrom);
|
|
70
|
+
}, [valueFrom]);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
setInputToValue(valueTo);
|
|
73
|
+
}, [valueTo]);
|
|
74
|
+
useDidUpdateEffect(() => {
|
|
75
|
+
onDateFromChange({ value: inputFromValue, date: dateFrom });
|
|
76
|
+
onChange({
|
|
77
|
+
valueFrom: inputFromValue,
|
|
78
|
+
valueTo: inputToValue,
|
|
79
|
+
dateFrom,
|
|
80
|
+
dateTo,
|
|
81
|
+
});
|
|
82
|
+
if (!inputFromValue || isCompleteDateInput(inputFromValue)) {
|
|
83
|
+
handleValidInputFrom();
|
|
84
|
+
}
|
|
85
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
86
|
+
}, [inputFromValue]);
|
|
87
|
+
useDidUpdateEffect(() => {
|
|
88
|
+
onDateToChange({ value: inputToValue, date: dateTo });
|
|
89
|
+
// eslint-disable-next-line no-nested-ternary
|
|
90
|
+
const inputDateTo = returnInvalidDates
|
|
91
|
+
? isCompleteDateInput(inputToValue)
|
|
92
|
+
? parseDateString(inputToValue).getTime()
|
|
93
|
+
: null
|
|
94
|
+
: dateTo;
|
|
95
|
+
onChange({
|
|
96
|
+
valueFrom: inputFromValue,
|
|
97
|
+
valueTo: inputToValue,
|
|
98
|
+
dateFrom,
|
|
99
|
+
dateTo: inputDateTo,
|
|
100
|
+
});
|
|
101
|
+
if (!inputToValue || isCompleteDateInput(inputToValue)) {
|
|
102
|
+
handleValidInputTo();
|
|
103
|
+
}
|
|
104
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
105
|
+
}, [inputToValue]);
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (onError) {
|
|
108
|
+
onError(hasValidateError);
|
|
109
|
+
}
|
|
110
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
111
|
+
}, [hasValidateError]);
|
|
112
|
+
return (React.createElement("div", { className: cn(styles.component, className), "data-test-id": dataTestId },
|
|
113
|
+
React.createElement(CalendarInput, { ...inputFromProps, useAnchorWidth: false, calendarPosition: 'popover', popoverPosition: 'bottom-start', error: inputFromInvalid || bothInvalid || inputFromProps.error, onChange: handleFromChange, onInputChange: handleInputFromChange, onBlur: handleInputFromBlur, value: inputFromValue, minDate: minDate, maxDate: maxDate, offDays: offDays, events: events, calendarProps: {
|
|
114
|
+
...inputFromProps.calendarProps,
|
|
115
|
+
month: monthFrom,
|
|
116
|
+
onMonthChange: handleMonthFromChange,
|
|
117
|
+
selectorView: 'full',
|
|
118
|
+
} }),
|
|
119
|
+
React.createElement(Divider, { inputFromProps: inputFromProps, inputToProps: inputToProps }),
|
|
120
|
+
React.createElement(CalendarInput, { ...inputToProps, useAnchorWidth: false, calendarPosition: 'popover', popoverPosition: 'bottom-end', error: inputToInvalid || bothInvalid || inputToProps.error, onChange: handleToChange, onInputChange: handleInputToChange, onBlur: handleInputToBlur, value: inputToValue, minDate: dateFrom || minDate, maxDate: maxDate, offDays: offDays, events: events, calendarProps: {
|
|
121
|
+
...inputToProps.calendarProps,
|
|
122
|
+
month: monthTo,
|
|
123
|
+
onMonthChange: handleMonthToChange,
|
|
124
|
+
selectorView: 'full',
|
|
125
|
+
} })));
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export { CalendarRangePopover };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { CalendarRangeProps } from "../Component";
|
|
3
|
+
type CalendarRangeStaticProps = Omit<CalendarRangeProps, 'calendarPosition'> & {
|
|
4
|
+
/**
|
|
5
|
+
* Отображать начальный месяц слева или справа (влияет только на начальный рендер)
|
|
6
|
+
*/
|
|
7
|
+
defaultMonthPosition?: 'left' | 'right';
|
|
8
|
+
};
|
|
9
|
+
declare const CalendarRangeStatic: FC<CalendarRangeStaticProps>;
|
|
10
|
+
export { CalendarRangeStaticProps, CalendarRangeStatic };
|