@alfalab/core-components-date-range-input 2.4.0 → 3.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.
Files changed (151) hide show
  1. package/Component-63dec22f.d.ts +167 -0
  2. package/Component-89a3cf4c.d.ts +6 -0
  3. package/Component-8b5756fe.d.ts +380 -0
  4. package/Component.desktop-2e2b2125.d.ts +6 -0
  5. package/Component.desktop-8b5756fe.d.ts +197 -0
  6. package/Component.desktop.d.ts +1 -1
  7. package/Component.desktop.js +4 -3
  8. package/Component.mobile-755fbaa3.d.ts +6 -0
  9. package/Component.mobile-ebda875c.d.ts +6 -0
  10. package/Component.mobile.d.ts +1 -1
  11. package/Component.mobile.js +4 -3
  12. package/Component.responsive-2e2b2125.d.ts +66 -0
  13. package/Component.responsive.d.ts +1 -1
  14. package/Component.responsive.js +5 -2
  15. package/components/date-range-input/Component.d.ts +7 -2
  16. package/components/date-range-input/Component.js +10 -11
  17. package/components/date-range-input/index.css +7 -7
  18. package/components/date-range-input/index.js +1 -2
  19. package/cssm/Component-63dec22f.d.ts +167 -0
  20. package/cssm/Component-89a3cf4c.d.ts +6 -0
  21. package/cssm/Component-8b5756fe.d.ts +380 -0
  22. package/cssm/Component.desktop-2e2b2125.d.ts +6 -0
  23. package/cssm/Component.desktop-8b5756fe.d.ts +197 -0
  24. package/cssm/Component.desktop.d.ts +1 -1
  25. package/cssm/Component.desktop.js +4 -3
  26. package/cssm/Component.mobile-755fbaa3.d.ts +6 -0
  27. package/cssm/Component.mobile-ebda875c.d.ts +6 -0
  28. package/cssm/Component.mobile.d.ts +1 -1
  29. package/cssm/Component.mobile.js +4 -3
  30. package/cssm/Component.responsive-2e2b2125.d.ts +66 -0
  31. package/cssm/Component.responsive.d.ts +1 -1
  32. package/cssm/Component.responsive.js +5 -2
  33. package/cssm/components/date-range-input/Component.d.ts +7 -2
  34. package/cssm/components/date-range-input/Component.js +9 -10
  35. package/cssm/components/date-range-input/index.js +1 -2
  36. package/cssm/desktop-63dec22f.d.ts +2 -0
  37. package/cssm/desktop.js +3 -2
  38. package/cssm/index-89a3cf4c.d.ts +3 -0
  39. package/cssm/index-9211a437.d.ts +2 -0
  40. package/cssm/index-ebda875c.d.ts +35 -0
  41. package/cssm/index-f034f741.d.ts +80 -0
  42. package/cssm/index.d.ts +3 -2
  43. package/cssm/index.js +5 -2
  44. package/cssm/mobile-c219f8ca.d.ts +1 -0
  45. package/cssm/mobile.js +3 -2
  46. package/cssm/types-ebda875c.d.ts +113 -0
  47. package/cssm/typings-5684a67d.d.ts +23 -0
  48. package/cssm/typings-9211a437.d.ts +95 -0
  49. package/cssm/useCalendar-5684a67d.d.ts +97 -0
  50. package/cssm/utils-5684a67d.d.ts +88 -0
  51. package/desktop-63dec22f.d.ts +2 -0
  52. package/desktop.js +3 -2
  53. package/esm/Component-63dec22f.d.ts +167 -0
  54. package/esm/Component-89a3cf4c.d.ts +6 -0
  55. package/esm/Component-8b5756fe.d.ts +380 -0
  56. package/esm/Component.desktop-2e2b2125.d.ts +6 -0
  57. package/esm/Component.desktop-8b5756fe.d.ts +197 -0
  58. package/esm/Component.desktop.d.ts +1 -1
  59. package/esm/Component.desktop.js +4 -3
  60. package/esm/Component.mobile-755fbaa3.d.ts +6 -0
  61. package/esm/Component.mobile-ebda875c.d.ts +6 -0
  62. package/esm/Component.mobile.d.ts +1 -1
  63. package/esm/Component.mobile.js +4 -3
  64. package/esm/Component.responsive-2e2b2125.d.ts +66 -0
  65. package/esm/Component.responsive.d.ts +1 -1
  66. package/esm/Component.responsive.js +5 -2
  67. package/esm/components/date-range-input/Component.d.ts +7 -2
  68. package/esm/components/date-range-input/Component.js +10 -11
  69. package/esm/components/date-range-input/index.css +7 -7
  70. package/esm/components/date-range-input/index.js +1 -2
  71. package/esm/desktop-63dec22f.d.ts +2 -0
  72. package/esm/desktop.js +3 -2
  73. package/esm/index-89a3cf4c.d.ts +3 -0
  74. package/esm/index-9211a437.d.ts +2 -0
  75. package/esm/index-ebda875c.d.ts +35 -0
  76. package/esm/index-f034f741.d.ts +80 -0
  77. package/esm/index.d.ts +3 -2
  78. package/esm/index.js +5 -2
  79. package/esm/mobile-c219f8ca.d.ts +1 -0
  80. package/esm/mobile.js +3 -2
  81. package/esm/types-ebda875c.d.ts +113 -0
  82. package/esm/typings-5684a67d.d.ts +23 -0
  83. package/esm/typings-9211a437.d.ts +95 -0
  84. package/esm/useCalendar-5684a67d.d.ts +97 -0
  85. package/esm/utils-5684a67d.d.ts +88 -0
  86. package/index-89a3cf4c.d.ts +3 -0
  87. package/index-9211a437.d.ts +2 -0
  88. package/index-ebda875c.d.ts +35 -0
  89. package/index-f034f741.d.ts +80 -0
  90. package/index.d.ts +3 -2
  91. package/index.js +5 -2
  92. package/mobile-c219f8ca.d.ts +1 -0
  93. package/mobile.js +3 -2
  94. package/modern/Component-63dec22f.d.ts +167 -0
  95. package/modern/Component-89a3cf4c.d.ts +6 -0
  96. package/modern/Component-8b5756fe.d.ts +380 -0
  97. package/modern/Component.desktop-2e2b2125.d.ts +6 -0
  98. package/modern/Component.desktop-8b5756fe.d.ts +197 -0
  99. package/modern/Component.desktop.d.ts +1 -1
  100. package/modern/Component.desktop.js +4 -3
  101. package/modern/Component.mobile-755fbaa3.d.ts +6 -0
  102. package/modern/Component.mobile-ebda875c.d.ts +6 -0
  103. package/modern/Component.mobile.d.ts +1 -1
  104. package/modern/Component.mobile.js +4 -3
  105. package/modern/Component.responsive-2e2b2125.d.ts +66 -0
  106. package/modern/Component.responsive.d.ts +1 -1
  107. package/modern/Component.responsive.js +5 -2
  108. package/modern/components/date-range-input/Component.d.ts +7 -2
  109. package/modern/components/date-range-input/Component.js +7 -8
  110. package/modern/components/date-range-input/index.css +7 -7
  111. package/modern/components/date-range-input/index.js +1 -2
  112. package/modern/desktop-63dec22f.d.ts +2 -0
  113. package/modern/desktop.js +3 -2
  114. package/modern/index-89a3cf4c.d.ts +3 -0
  115. package/modern/index-9211a437.d.ts +2 -0
  116. package/modern/index-ebda875c.d.ts +35 -0
  117. package/modern/index-f034f741.d.ts +80 -0
  118. package/modern/index.d.ts +3 -2
  119. package/modern/index.js +5 -2
  120. package/modern/mobile-c219f8ca.d.ts +1 -0
  121. package/modern/mobile.js +3 -2
  122. package/modern/types-ebda875c.d.ts +113 -0
  123. package/modern/typings-5684a67d.d.ts +23 -0
  124. package/modern/typings-9211a437.d.ts +95 -0
  125. package/modern/useCalendar-5684a67d.d.ts +97 -0
  126. package/modern/utils-5684a67d.d.ts +88 -0
  127. package/package.json +25 -5
  128. package/src/Component.desktop.tsx +29 -0
  129. package/src/Component.mobile.tsx +30 -0
  130. package/src/Component.responsive.tsx +41 -0
  131. package/src/components/date-range-input/Component.tsx +519 -0
  132. package/src/components/date-range-input/index.module.css +37 -0
  133. package/src/components/date-range-input/index.ts +1 -0
  134. package/src/desktop.ts +1 -0
  135. package/src/index.ts +2 -0
  136. package/src/mobile.ts +1 -0
  137. package/src/utils/format.ts +91 -0
  138. package/src/utils/index.ts +1 -0
  139. package/types-ebda875c.d.ts +113 -0
  140. package/typings-5684a67d.d.ts +23 -0
  141. package/typings-9211a437.d.ts +95 -0
  142. package/useCalendar-5684a67d.d.ts +97 -0
  143. package/utils-5684a67d.d.ts +88 -0
  144. package/cssm/responsive.d.ts +0 -2
  145. package/cssm/responsive.js +0 -27
  146. package/esm/responsive.d.ts +0 -2
  147. package/esm/responsive.js +0 -18
  148. package/modern/responsive.d.ts +0 -2
  149. package/modern/responsive.js +0 -17
  150. package/responsive.d.ts +0 -2
  151. package/responsive.js +0 -26
@@ -0,0 +1,519 @@
1
+ /* eslint-disable no-useless-escape, jsx-a11y/click-events-have-key-events */
2
+ import React, {
3
+ ChangeEvent,
4
+ ElementType,
5
+ FocusEvent,
6
+ KeyboardEvent,
7
+ MouseEvent,
8
+ useEffect,
9
+ useRef,
10
+ useState,
11
+ } from 'react';
12
+ import mergeRefs from 'react-merge-refs';
13
+ import cn from 'classnames';
14
+ import { startOfMonth } from 'date-fns';
15
+ import dateFnsIsValid from 'date-fns/isValid';
16
+
17
+ import type { CalendarDesktopProps } from '@alfalab/core-components-calendar/desktop';
18
+ import type { CalendarMobileProps } from '@alfalab/core-components-calendar/mobile';
19
+ import { usePeriod } from '@alfalab/core-components-calendar/shared';
20
+ import { IconButton } from '@alfalab/core-components-icon-button';
21
+ import { InputProps } from '@alfalab/core-components-input';
22
+ import { Popover, PopoverProps } from '@alfalab/core-components-popover';
23
+ import { useDidUpdateEffect } from '@alfalab/hooks';
24
+ import { CalendarMIcon } from '@alfalab/icons-glyph/CalendarMIcon';
25
+
26
+ import {
27
+ DATE_FORMAT,
28
+ DATE_MASK,
29
+ format,
30
+ isCompleteDateInput,
31
+ isValid,
32
+ parseDateString,
33
+ parseTimestampToDate,
34
+ } from '../../utils';
35
+
36
+ import styles from './index.module.css';
37
+
38
+ export type ConditionalProps =
39
+ | {
40
+ /**
41
+ * Обработчик изменения значения
42
+ */
43
+ picker: true;
44
+
45
+ /**
46
+ * Обработчик закрытия календаря
47
+ */
48
+ onClose?: () => void;
49
+ }
50
+ | { picker?: false; onClose?: never };
51
+
52
+ export type DateRangeInputProps = Omit<InputProps, 'onChange'> &
53
+ ConditionalProps & {
54
+ /**
55
+ * Дополнительный класс
56
+ */
57
+ className?: string;
58
+
59
+ /**
60
+ * Дополнительный класс для инпута
61
+ */
62
+ inputClassName?: string;
63
+
64
+ /**
65
+ * Дополнительный класс для поповера
66
+ */
67
+ popoverClassName?: string;
68
+
69
+ /**
70
+ * Обработчик изменения значения
71
+ */
72
+ onChange?: (
73
+ payload: { dateFrom?: Date; dateTo?: Date; value: string },
74
+ event?: ChangeEvent<HTMLInputElement>,
75
+ ) => void;
76
+
77
+ /**
78
+ * Обработчик окончания ввода
79
+ */
80
+ onComplete?: (
81
+ payload: { dateFrom: Date; dateTo: Date; value: string },
82
+ event?: ChangeEvent<HTMLInputElement>,
83
+ ) => void;
84
+
85
+ /**
86
+ * Компонент календаря
87
+ */
88
+ Calendar?: ElementType;
89
+
90
+ /**
91
+ * Доп. пропсы для календаря
92
+ */
93
+ calendarProps?:
94
+ | (CalendarDesktopProps & Record<string, unknown>)
95
+ | (CalendarMobileProps & Record<string, unknown>);
96
+
97
+ /**
98
+ * Месяц в календаре по умолчанию (timestamp)
99
+ */
100
+ defaultMonth?: number;
101
+
102
+ /**
103
+ * Минимальная дата, доступная для выбора (timestamp)
104
+ */
105
+ minDate?: number;
106
+
107
+ /**
108
+ * Максимальная дата, доступная для выбора (timestamp)
109
+ */
110
+ maxDate?: number;
111
+
112
+ /**
113
+ * Список событий
114
+ */
115
+ events?: Array<Date | number>;
116
+
117
+ /**
118
+ * Список выходных
119
+ */
120
+ offDays?: Array<Date | number>;
121
+
122
+ /**
123
+ * Состояние открытия по умолчанию
124
+ */
125
+ defaultOpen?: boolean;
126
+
127
+ /**
128
+ * Позиционирование поповера с календарем
129
+ */
130
+ popoverPosition?: PopoverProps['position'];
131
+
132
+ /**
133
+ * z-index Popover
134
+ */
135
+ zIndexPopover?: PopoverProps['zIndex'];
136
+
137
+ /**
138
+ * Запрещает поповеру менять свою позицию.
139
+ * Например, если места снизу недостаточно, то он все равно будет показан снизу
140
+ */
141
+ preventFlip?: boolean;
142
+
143
+ /**
144
+ * Календарь будет принимать ширину инпута
145
+ */
146
+ useAnchorWidth?: boolean;
147
+
148
+ /**
149
+ * Растягивает компонент на ширину контейнера
150
+ */
151
+ block?: boolean;
152
+
153
+ /**
154
+ * Отображение компонента в мобильном или десктопном виде
155
+ */
156
+ view?: 'desktop' | 'mobile';
157
+
158
+ /**
159
+ * Компонент инпута
160
+ */
161
+ InputComponent?: ElementType;
162
+
163
+ /**
164
+ * Запретить ввод с клавиатуры
165
+ */
166
+ disableUserInput?: boolean;
167
+ };
168
+
169
+ type GetDatesRet = { formattedValue: string; dateFrom?: Date; dateTo?: Date; dateArr: string[] };
170
+
171
+ export const DateRangeInput = React.forwardRef<HTMLInputElement, DateRangeInputProps>(
172
+ (
173
+ {
174
+ className,
175
+ inputClassName,
176
+ popoverClassName,
177
+ disabled,
178
+ readOnly,
179
+ disableUserInput = false,
180
+ picker,
181
+ defaultValue = '',
182
+ value: propValue,
183
+ onChange,
184
+ onComplete,
185
+ onClose,
186
+ rightAddons,
187
+ useAnchorWidth,
188
+ block,
189
+ popoverPosition = 'bottom-start',
190
+ zIndexPopover,
191
+ preventFlip,
192
+ InputComponent,
193
+ Calendar,
194
+ calendarProps = {},
195
+ defaultMonth,
196
+ minDate = calendarProps.minDate,
197
+ maxDate = calendarProps.maxDate,
198
+ offDays = calendarProps.offDays || [],
199
+ events = calendarProps.events || [],
200
+ defaultOpen = false,
201
+ view = 'desktop',
202
+ ...restProps
203
+ },
204
+ ref,
205
+ ) => {
206
+ const inputRef = useRef<HTMLInputElement>(null);
207
+ const iconRef = useRef<HTMLButtonElement>(null);
208
+ const calendarRef = useRef<HTMLDivElement>(null);
209
+
210
+ const [value, setValue] = useState(propValue || defaultValue);
211
+ const [open, setOpen] = useState(defaultOpen);
212
+
213
+ const inputDisabled = disabled || readOnly;
214
+
215
+ const calendarResponsive = calendarProps?.responsive ?? true;
216
+
217
+ const { selectedFrom, selectedTo, updatePeriod, resetPeriod, setStart, setEnd } = usePeriod(
218
+ { onPeriodChange: handlePeriodChange },
219
+ );
220
+
221
+ useEffect(() => {
222
+ if (value) {
223
+ setCalendarPeriod(getDates(value));
224
+ }
225
+ // eslint-disable-next-line react-hooks/exhaustive-deps
226
+ }, []);
227
+
228
+ useEffect(() => {
229
+ setValue((prevValue) => {
230
+ if (selectedFrom && selectedTo) {
231
+ const from = parseTimestampToDate(selectedFrom);
232
+ const to = parseTimestampToDate(selectedTo);
233
+
234
+ return `${from} - ${to}`;
235
+ }
236
+ if (selectedFrom && prevValue.length < DATE_FORMAT.length) {
237
+ return parseTimestampToDate(selectedFrom);
238
+ }
239
+
240
+ return prevValue;
241
+ });
242
+ }, [selectedFrom, selectedTo]);
243
+
244
+ useDidUpdateEffect(() => {
245
+ const newPropValue = propValue || '';
246
+
247
+ setValue((prevValue) => {
248
+ if (prevValue === newPropValue) {
249
+ return prevValue;
250
+ }
251
+
252
+ const dates = getDates(newPropValue);
253
+
254
+ setCalendarPeriod(dates);
255
+
256
+ return dates.formattedValue;
257
+ });
258
+ }, [propValue]);
259
+
260
+ function getDates(val: string): GetDatesRet {
261
+ const formattedValue = format(val);
262
+
263
+ const dateArr = formattedValue.split('-').map((v) => v.trim());
264
+ const dateFrom = dateArr[0] ? parseDateString(dateArr[0]) : undefined;
265
+ const dateTo = dateArr[1] ? parseDateString(dateArr[1]) : undefined;
266
+
267
+ return { formattedValue, dateFrom, dateTo, dateArr };
268
+ }
269
+
270
+ function setCalendarPeriod({ dateFrom, dateTo }: GetDatesRet) {
271
+ setStart(dateFrom?.getTime());
272
+ setEnd(dateTo?.getTime());
273
+ }
274
+
275
+ function handlePeriodChange(from?: number, to?: number) {
276
+ if (from && !to && value.length === DATE_MASK.length) {
277
+ setValue(parseTimestampToDate(from));
278
+ } else if (
279
+ (!from && !to && value.length === DATE_FORMAT.length) ||
280
+ (from === to && value.length === DATE_MASK.length)
281
+ ) {
282
+ setValue('');
283
+ }
284
+
285
+ const dateFrom = from ? new Date(from) : undefined;
286
+ const dateTo = to ? new Date(to) : undefined;
287
+
288
+ const newValue = ([from, to].filter(Boolean) as number[])
289
+ .map((timestamp) => parseTimestampToDate(timestamp))
290
+ .join(' - ');
291
+
292
+ onChange?.({
293
+ dateFrom,
294
+ dateTo,
295
+ value: newValue,
296
+ });
297
+
298
+ if (dateFrom && dateTo) {
299
+ onComplete?.({
300
+ dateFrom,
301
+ dateTo,
302
+ value: newValue,
303
+ });
304
+ }
305
+ }
306
+
307
+ const handleInputWrapperFocus = (event: FocusEvent<HTMLDivElement>) => {
308
+ if (view === 'desktop') {
309
+ if (picker) {
310
+ setOpen(true);
311
+ }
312
+
313
+ if (!open && event.target.tagName !== 'INPUT' && calendarRef.current) {
314
+ calendarRef.current.focus();
315
+ }
316
+ }
317
+ };
318
+
319
+ const handleInputKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
320
+ const isCopy = (event.metaKey || event.ctrlKey) && event.key === 'c';
321
+
322
+ if (disableUserInput && !isCopy) {
323
+ event.preventDefault();
324
+ }
325
+ };
326
+
327
+ const handleBlur = (event: FocusEvent<HTMLDivElement>) => {
328
+ if (view === 'desktop') {
329
+ const target = (event.relatedTarget || document.activeElement) as HTMLElement;
330
+
331
+ if (
332
+ calendarRef.current?.contains(target) === false &&
333
+ inputRef.current?.contains(target) === false &&
334
+ iconRef.current?.contains(target) === false
335
+ ) {
336
+ setOpen(false);
337
+
338
+ if (onClose) {
339
+ onClose();
340
+ }
341
+ }
342
+ }
343
+ };
344
+
345
+ // eslint-disable-next-line complexity
346
+ const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
347
+ const { value: newValue } = event.target;
348
+
349
+ if (newValue.length > DATE_MASK.length) return;
350
+
351
+ // Позволяем вводить только цифры, точки, дефис и пробелы
352
+ if (/[^\d. -]/.test(newValue)) {
353
+ return;
354
+ }
355
+
356
+ const dots = newValue.match(/\./g);
357
+ const hyphen = newValue.match(/\-/g);
358
+
359
+ // Не даем вводить больше, чем 4 точки и 1 дефис
360
+ if ((dots && dots.length > 4) || (hyphen && hyphen.length > 1)) {
361
+ return;
362
+ }
363
+
364
+ const { formattedValue, dateFrom, dateTo, dateArr } = getDates(newValue);
365
+
366
+ if (!dateFrom && !dateTo) {
367
+ resetPeriod();
368
+ } else if (selectedFrom && formattedValue.length < DATE_FORMAT.length) {
369
+ setStart();
370
+ } else if (selectedFrom && selectedTo) {
371
+ setEnd();
372
+ } else if (
373
+ dateFrom &&
374
+ dateFnsIsValid(dateFrom) &&
375
+ dateArr[0]?.length === DATE_FORMAT.length &&
376
+ dateFrom.getTime() !== selectedFrom
377
+ ) {
378
+ setStart(dateFrom.getTime());
379
+ } else if (
380
+ dateTo &&
381
+ dateFnsIsValid(dateTo) &&
382
+ dateArr[1]?.length === DATE_FORMAT.length &&
383
+ dateTo.getTime() !== selectedTo
384
+ ) {
385
+ setEnd(dateTo.getTime());
386
+ }
387
+
388
+ setValue(formattedValue);
389
+
390
+ onChange?.({ dateFrom, dateTo, value: formattedValue }, event);
391
+
392
+ if (isCompleteDateInput(formattedValue)) {
393
+ const valid = isValid(formattedValue, dateArr[0], dateArr[1]);
394
+
395
+ if (!valid) return;
396
+
397
+ if (dateFrom && dateTo) {
398
+ onComplete?.({ dateFrom, dateTo, value: formattedValue }, event);
399
+ }
400
+ }
401
+ };
402
+
403
+ const handleCalendarClose = () => {
404
+ if (view === 'mobile' && onClose) {
405
+ onClose();
406
+ }
407
+
408
+ setOpen(false);
409
+ };
410
+
411
+ const handleClear = () => {
412
+ setValue('');
413
+ resetPeriod();
414
+ };
415
+
416
+ const handleCalendarChange = (date?: number) => {
417
+ if (date) {
418
+ updatePeriod(date);
419
+ }
420
+ };
421
+
422
+ const handleCalendarWrapperMouseDown = (event: MouseEvent<HTMLDivElement>) => {
423
+ // Не дает инпуту терять фокус при выборе даты
424
+ event.preventDefault();
425
+ };
426
+
427
+ const handleIconButtonClick = () => {
428
+ if (!open) setOpen(true);
429
+
430
+ if (view === 'desktop' && inputRef.current) {
431
+ inputRef.current.focus();
432
+ }
433
+ };
434
+
435
+ const renderCalendar = () => {
436
+ const activeMonth =
437
+ (selectedTo && startOfMonth(selectedTo)) ||
438
+ (selectedFrom && startOfMonth(selectedFrom));
439
+
440
+ return Calendar ? (
441
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
442
+ <div onMouseDown={handleCalendarWrapperMouseDown}>
443
+ <Calendar
444
+ {...calendarProps}
445
+ responsive={calendarResponsive}
446
+ open={open}
447
+ onClose={handleCalendarClose}
448
+ ref={calendarRef}
449
+ defaultMonth={activeMonth || defaultMonth}
450
+ selectedFrom={selectedFrom}
451
+ selectedTo={selectedTo}
452
+ onChange={handleCalendarChange}
453
+ minDate={minDate}
454
+ maxDate={maxDate}
455
+ offDays={offDays}
456
+ events={events}
457
+ />
458
+ </div>
459
+ ) : null;
460
+ };
461
+
462
+ return (
463
+ <div
464
+ className={cn(styles.component, className, {
465
+ [styles.block]: block,
466
+ })}
467
+ onFocus={inputDisabled ? undefined : handleInputWrapperFocus}
468
+ onBlur={handleBlur}
469
+ >
470
+ {InputComponent ? (
471
+ <InputComponent
472
+ {...restProps}
473
+ block={block}
474
+ ref={mergeRefs([ref, inputRef])}
475
+ value={value}
476
+ onChange={handleChange}
477
+ disabled={disabled}
478
+ readOnly={readOnly}
479
+ className={inputClassName}
480
+ onClear={handleClear}
481
+ onKeyDown={handleInputKeyDown}
482
+ rightAddons={
483
+ <React.Fragment>
484
+ {rightAddons}
485
+ {picker && (
486
+ <IconButton
487
+ className={styles.calendarIcon}
488
+ ref={iconRef}
489
+ onClick={inputDisabled ? undefined : handleIconButtonClick}
490
+ icon={CalendarMIcon}
491
+ size='s'
492
+ />
493
+ )}
494
+ </React.Fragment>
495
+ }
496
+ />
497
+ ) : null}
498
+ {picker && (
499
+ <Popover
500
+ open={open}
501
+ useAnchorWidth={useAnchorWidth}
502
+ anchorElement={inputRef.current as HTMLElement}
503
+ popperClassName={cn(styles.calendarContainer, {
504
+ [styles.calendarResponsive]: calendarResponsive,
505
+ })}
506
+ className={popoverClassName}
507
+ position={popoverPosition}
508
+ offset={[0, 8]}
509
+ withTransition={false}
510
+ preventFlip={preventFlip}
511
+ zIndex={zIndexPopover}
512
+ >
513
+ {renderCalendar()}
514
+ </Popover>
515
+ )}
516
+ </div>
517
+ );
518
+ },
519
+ );
@@ -0,0 +1,37 @@
1
+ @import '@alfalab/core-components-themes/src/default.css';
2
+ @import '@alfalab/core-components-calendar/src/vars.css';
3
+
4
+ :root {
5
+ --calendar-popover-border-radius: var(--border-radius-m);
6
+ }
7
+
8
+ .component {
9
+ display: inline-block;
10
+ outline: none;
11
+ position: relative;
12
+ }
13
+
14
+ .calendarContainer {
15
+ display: inline-block;
16
+ box-sizing: border-box;
17
+ border-radius: var(--calendar-popover-border-radius);
18
+ border: 1px solid var(--color-light-border-secondary);
19
+
20
+ @media (max-width: 374px) {
21
+ width: 100%;
22
+ min-width: 288px;
23
+ }
24
+ }
25
+
26
+ .calendarResponsive {
27
+ width: var(--calendar-width);
28
+ }
29
+
30
+ .block {
31
+ width: 100%;
32
+ }
33
+
34
+ .calendarIcon {
35
+ margin-right: var(--gap-s-neg);
36
+ height: 100%;
37
+ }
@@ -0,0 +1 @@
1
+ export * from './Component';
package/src/desktop.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './Component.desktop';
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { DateRangeInputResponsive as DateRangeInput } from './Component.responsive';
2
+ export type { DateRangeInputResponsiveProps as DateRangeInputProps } from './Component.responsive';
package/src/mobile.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './Component.mobile';
@@ -0,0 +1,91 @@
1
+ /* eslint-disable no-useless-escape */
2
+
3
+ import dateFnsIsValid from 'date-fns/isValid';
4
+ import parse from 'date-fns/parse';
5
+
6
+ export const DATE_FORMAT = 'dd.MM.yyyy';
7
+ export const DATE_MASK = [
8
+ /\d/,
9
+ /\d/,
10
+ '.',
11
+ /\d/,
12
+ /\d/,
13
+ '.',
14
+ /\d/,
15
+ /\d/,
16
+ /\d/,
17
+ /\d/,
18
+ ' ',
19
+ '-',
20
+ ' ',
21
+ /\d/,
22
+ /\d/,
23
+ '.',
24
+ /\d/,
25
+ /\d/,
26
+ '.',
27
+ /\d/,
28
+ /\d/,
29
+ /\d/,
30
+ /\d/,
31
+ ];
32
+
33
+ export const isCompleteDateInput = (input: string) => input.length === DATE_MASK.length;
34
+
35
+ export const parseDateString = (value: string, dateFormat = DATE_FORMAT) =>
36
+ parse(value, dateFormat, new Date());
37
+
38
+ export const isValid = (inputValue: string, dateFrom: string, dateTo: string) =>
39
+ !inputValue ||
40
+ (isCompleteDateInput(inputValue) &&
41
+ dateFnsIsValid(parseDateString(dateFrom)) &&
42
+ dateFnsIsValid(parseDateString(dateTo)));
43
+
44
+ export const format = (value: string): string =>
45
+ value
46
+ .replace(/^(\d\d)(\d)$/, '$1.$2') // 121 => 12.1
47
+ .replace(/^(\d\d)\.(\d\d)(\d)$/, '$1.$2.$3') // 12.122 => 12.12.2
48
+ .replace(/^(\d\d)\d\.(.*)/, '$1.$2') // 123.12.2005 => 12.12.2005
49
+ .replace(/^(\d\d\.\d\d)\d\.(.*)/, '$1.$2') // 12.123.2005 => 12.12.2005
50
+ .replace(/\.$/, '') // 12. => 12
51
+ .replace(/\ $/, '') // 1 2 => 12
52
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d)(\d) - (\d.*)/, '$1 - $3') // 12.12.20051 - 12.12.200 => 12.12.2005 - 12.12.200
53
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d) (\d)- (\d.*)/, '$1 - $3') // 12.12.2005 1- 12.12.200 => 12.12.2005 - 12.12.200
54
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d) -(\d) (\d.*)/, '$1 - $3') // 12.12.2005 -1 12.12.200 => 12.12.2005 - 12.12.200
55
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d) - (\d)(\d\d.\d\d.\d\d\d)/, '$1 - $3') // 12.12.2005 - 112.12.200 => 12.12.2005 - 12.12.200
56
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d) - (\d)(\d\d.\d.\d\d\d\d)/, '$1 - $3') // 12.12.2005 - 112.1.2001 => 12.12.2005 - 12.1.2001
57
+ .replace(/^(\d\d\.\d\d)(\d\d\d\d)/, '$1.$2') // 12.122005 => 12.12.2005
58
+ .replace(/^(\d\d)(\d\d\.\d\d\d\d)/, '$1.$2') // 1212.2005 => 12.12.2005
59
+ .replace(/^(\d\d)(\d.*)/, '$1.$2') // 1212 => 12.12
60
+ .replace(/^(\d\d.\d\d)(\d.*)/, '$1.$2') // 12.122 => 12.12.2
61
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d)(\d)/, '$1 - $2') // 12.12.20056 => 12.12.2005 - 6
62
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d) - (\d\d)(\d)/, '$1 - $2.$3') // 12.12.2005 - 123 => 12.12.2005 - 12.3
63
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d) - (\d\d).(\d\d)(\d)/, '$1 - $2.$3.$4') // 12.12.2005 - 12.123 => 12.12.2005 - 12.12.3
64
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d)- (\d.*)/, '$1 - $2') // 12.12.2005- 12.12.2005 => 12.12.2005 - 12.12.2005
65
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d) -(\d.*)/, '$1 - $2') // 12.12.2005 -12.12.2005 => 12.12.2005 - 12.12.2005
66
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d) -/, '$1') // 12.12.2005 - => 12.12.2005
67
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d) (\d.*)/, '$1 - $2') // 12.12.2005 12.12.2005 => 12.12.2005 - 12.12.2005
68
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d) {2}(\d.*)/, '$1 - $2') // 12.12.2005 12.12.2005 => 12.12.2005 - 12.12.2005
69
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d)-/, '$1') // 12.12.2005- => 12.12.2005
70
+ .replace(/^(\d\.\d\d\.\d\d\d\d)([0-9]*)/, '$1') // 1.12.2005123123 => 1.12.2005
71
+ .replace(/^(\d\d\.\d\.\d\d\d\d)([0-9]*)/, '$1') // 01.2.20055125125 => 01.2.2005
72
+ .replace(/^(\d)\.(\d\d)([0-9]*)\.(\d\d\d\d)/, '$1.$2.$4') // 1.123123.2005 => 1.12.2005
73
+ .replace(/^(\d\.\d\.\d\d\d\d)([0-9]*)/, '$1') // 1.2.20055125125 => 1.2.2005
74
+ .replace(/^()\.(\d)\.(\d\d\d\d)([0-9]*)/, '$1.$2.$3'); // .2.2005123123 => .2.2005
75
+
76
+ export const parseTimestampToDate = (timestamp: number): string => {
77
+ const date = new Date(timestamp);
78
+ const year = date.getFullYear();
79
+
80
+ let month: number | string = date.getMonth() + 1;
81
+ let day: number | string = date.getDate();
82
+
83
+ if (month < 10) {
84
+ month = `0${month}`;
85
+ }
86
+ if (day < 10) {
87
+ day = `0${day}`;
88
+ }
89
+
90
+ return `${day}.${month}.${year}`;
91
+ };
@@ -0,0 +1 @@
1
+ export * from './format';