@artemy-tech/datepicker 0.1.0 → 0.3.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.
@@ -0,0 +1,785 @@
1
+ // src/components/Calendar/Calendar.tsx
2
+ import { DayPicker } from "react-day-picker";
3
+ import { jsx } from "react/jsx-runtime";
4
+ function Calendar({ className, ...props }) {
5
+ return /* @__PURE__ */ jsx(
6
+ DayPicker,
7
+ {
8
+ className: ["datepicker-calendar", className].filter(Boolean).join(" "),
9
+ ...props
10
+ }
11
+ );
12
+ }
13
+
14
+ // src/components/DatePicker/DatePicker.tsx
15
+ import { useCallback, useEffect as useEffect3, useRef as useRef2, useState } from "react";
16
+ import { format, isValid, parse } from "date-fns";
17
+ import { ru } from "date-fns/locale";
18
+
19
+ // src/hooks/useClickOutside.ts
20
+ import { useEffect } from "react";
21
+ function useClickOutside(ref, handler) {
22
+ useEffect(() => {
23
+ function listener(e) {
24
+ if (!ref.current || ref.current.contains(e.target)) return;
25
+ handler();
26
+ }
27
+ document.addEventListener("mousedown", listener);
28
+ return () => document.removeEventListener("mousedown", listener);
29
+ }, [ref, handler]);
30
+ }
31
+
32
+ // src/components/TimePanel/TimePanel.tsx
33
+ import { useEffect as useEffect2, useRef } from "react";
34
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
35
+ var HOURS = Array.from({ length: 24 }, (_, i) => i);
36
+ var MINUTES = Array.from({ length: 60 }, (_, i) => i);
37
+ var SECONDS = Array.from({ length: 60 }, (_, i) => i);
38
+ function pad2(n) {
39
+ return String(n).padStart(2, "0");
40
+ }
41
+ function Column({ values, selected, onSelect }) {
42
+ const selectedRef = useRef(null);
43
+ useEffect2(() => {
44
+ var _a;
45
+ (_a = selectedRef.current) == null ? void 0 : _a.scrollIntoView({ block: "center", behavior: "instant" });
46
+ }, [selected]);
47
+ return /* @__PURE__ */ jsx2("div", { className: "time-panel__column", children: values.map((v) => /* @__PURE__ */ jsx2(
48
+ "button",
49
+ {
50
+ ref: v === selected ? selectedRef : void 0,
51
+ className: "time-panel__item",
52
+ "data-selected": v === selected || void 0,
53
+ onClick: () => onSelect(v),
54
+ type: "button",
55
+ tabIndex: -1,
56
+ children: pad2(v)
57
+ },
58
+ v
59
+ )) });
60
+ }
61
+ function TimePanel({ value, showSeconds, onChange }) {
62
+ var _a, _b, _c;
63
+ const h = (_a = value == null ? void 0 : value.getHours()) != null ? _a : 0;
64
+ const m = (_b = value == null ? void 0 : value.getMinutes()) != null ? _b : 0;
65
+ const s = (_c = value == null ? void 0 : value.getSeconds()) != null ? _c : 0;
66
+ return /* @__PURE__ */ jsxs("div", { className: "time-panel", children: [
67
+ /* @__PURE__ */ jsx2(Column, { values: HOURS, selected: h, onSelect: (v) => onChange(v, m, s) }),
68
+ /* @__PURE__ */ jsx2(Column, { values: MINUTES, selected: m, onSelect: (v) => onChange(h, v, s) }),
69
+ showSeconds && /* @__PURE__ */ jsx2(Column, { values: SECONDS, selected: s, onSelect: (v) => onChange(h, m, v) })
70
+ ] });
71
+ }
72
+
73
+ // src/components/icons/CalendarIcon.tsx
74
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
75
+ function CalendarIcon() {
76
+ return /* @__PURE__ */ jsxs2("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
77
+ /* @__PURE__ */ jsx3("rect", { x: "1", y: "2.5", width: "14", height: "12", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
78
+ /* @__PURE__ */ jsx3("path", { d: "M1 6.5H15", stroke: "currentColor", strokeWidth: "1.5" }),
79
+ /* @__PURE__ */ jsx3("path", { d: "M5 1V4M11 1V4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
80
+ ] });
81
+ }
82
+
83
+ // src/components/icons/Spinner.tsx
84
+ import { jsx as jsx4 } from "react/jsx-runtime";
85
+ function Spinner() {
86
+ return /* @__PURE__ */ jsx4("span", { className: "datepicker-spinner", "aria-hidden": "true" });
87
+ }
88
+
89
+ // src/components/DatePicker/DatePicker.tsx
90
+ import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
91
+ var DATE_FORMAT = "dd.MM.yyyy";
92
+ function resolveTimeFormat(showTime) {
93
+ if (!showTime) return null;
94
+ if (showTime === true) return "HH:mm:ss";
95
+ return showTime.format;
96
+ }
97
+ function buildDateFormat(timeFormat) {
98
+ return timeFormat ? `${DATE_FORMAT} ${timeFormat}` : DATE_FORMAT;
99
+ }
100
+ function buildMaxDigits(timeFormat) {
101
+ if (!timeFormat) return 8;
102
+ return timeFormat === "HH:mm" ? 12 : 14;
103
+ }
104
+ function buildPlaceholder(timeFormat) {
105
+ if (!timeFormat) return "\u0434\u0434.\u043C\u043C.\u0433\u0433\u0433\u0433";
106
+ return timeFormat === "HH:mm" ? "\u0434\u0434.\u043C\u043C.\u0433\u0433\u0433\u0433 \u0447\u0447:\u043C\u043C" : "\u0434\u0434.\u043C\u043C.\u0433\u0433\u0433\u0433 \u0447\u0447:\u043C\u043C:\u0441\u0441";
107
+ }
108
+ function applyMask(digits, maxDigits) {
109
+ const d = digits.slice(0, maxDigits);
110
+ let result = "";
111
+ for (let i = 0; i < d.length; i++) {
112
+ if (i === 2 || i === 4) result += ".";
113
+ else if (i === 8) result += " ";
114
+ else if (i === 10 || i === 12) result += ":";
115
+ result += d[i];
116
+ }
117
+ return result;
118
+ }
119
+ function getCursorPos(masked, digitCount) {
120
+ if (digitCount === 0) return 0;
121
+ let count = 0;
122
+ for (let i = 0; i < masked.length; i++) {
123
+ if (/\d/.test(masked[i])) {
124
+ count++;
125
+ if (count === digitCount) return i + 1;
126
+ }
127
+ }
128
+ return masked.length;
129
+ }
130
+ function parseDateTime(masked, dateFormat, maxDigits) {
131
+ if (masked.replace(/\D/g, "").length !== maxDigits) return void 0;
132
+ const date = parse(masked, dateFormat, /* @__PURE__ */ new Date());
133
+ return isValid(date) && format(date, dateFormat) === masked ? date : void 0;
134
+ }
135
+ function DatePicker({
136
+ value,
137
+ defaultValue,
138
+ onChange,
139
+ label,
140
+ placeholder,
141
+ fromDate,
142
+ toDate,
143
+ disabled = false,
144
+ failed = false,
145
+ loading = false,
146
+ size = "m",
147
+ noCalendar = false,
148
+ showTime,
149
+ icon,
150
+ iconPosition = "end",
151
+ className
152
+ }) {
153
+ const timeFormat = resolveTimeFormat(showTime);
154
+ const dateFormat = buildDateFormat(timeFormat);
155
+ const maxDigits = buildMaxDigits(timeFormat);
156
+ const defaultPlaceholder = placeholder != null ? placeholder : buildPlaceholder(timeFormat);
157
+ const showSeconds = timeFormat === "HH:mm:ss";
158
+ const resolvedIcon = loading ? /* @__PURE__ */ jsx5(Spinner, {}) : icon === false ? null : icon != null ? icon : /* @__PURE__ */ jsx5(CalendarIcon, {});
159
+ const isControlled = value !== void 0;
160
+ const [internalDate, setInternalDate] = useState(defaultValue);
161
+ const [open, setOpen] = useState(false);
162
+ const [focused, setFocused] = useState(false);
163
+ const [inputValue, setInputValue] = useState(
164
+ () => defaultValue && isValid(defaultValue) ? format(defaultValue, dateFormat) : ""
165
+ );
166
+ const [inputInvalid, setInputInvalid] = useState(false);
167
+ const inputRef = useRef2(null);
168
+ const containerRef = useRef2(null);
169
+ const lastValidRef = useRef2(inputValue);
170
+ const lastEmittedRef = useRef2(value !== void 0 ? value : defaultValue);
171
+ const wasControlledRef = useRef2(value !== void 0);
172
+ const selected = isControlled ? value : internalDate;
173
+ const filled = inputValue.length > 0;
174
+ const close = useCallback(() => setOpen(false), []);
175
+ useClickOutside(containerRef, close);
176
+ useEffect3(() => {
177
+ var _a, _b, _c;
178
+ if (value !== void 0) wasControlledRef.current = true;
179
+ const lastTime = (_b = (_a = lastEmittedRef.current) == null ? void 0 : _a.getTime()) != null ? _b : null;
180
+ const valueTime = (_c = value == null ? void 0 : value.getTime()) != null ? _c : null;
181
+ if (valueTime === lastTime) return;
182
+ if (!wasControlledRef.current && value === void 0) return;
183
+ const formatted = value && isValid(value) ? format(value, dateFormat) : "";
184
+ setInputValue(formatted);
185
+ lastValidRef.current = formatted;
186
+ setInputInvalid(false);
187
+ if (!isControlled) setInternalDate(value);
188
+ lastEmittedRef.current = value;
189
+ }, [value]);
190
+ function applyValid(masked, date) {
191
+ lastEmittedRef.current = date;
192
+ lastValidRef.current = masked;
193
+ setInputValue(masked);
194
+ setInputInvalid(false);
195
+ if (!isControlled) setInternalDate(date);
196
+ onChange == null ? void 0 : onChange(date);
197
+ }
198
+ function commit(masked) {
199
+ const digits = masked.replace(/\D/g, "");
200
+ if (digits.length === 0) {
201
+ lastEmittedRef.current = void 0;
202
+ lastValidRef.current = "";
203
+ setInputInvalid(false);
204
+ if (!isControlled) setInternalDate(void 0);
205
+ onChange == null ? void 0 : onChange(void 0);
206
+ } else if (digits.length === maxDigits) {
207
+ const date = parseDateTime(masked, dateFormat, maxDigits);
208
+ lastEmittedRef.current = date;
209
+ if (date) lastValidRef.current = masked;
210
+ setInputInvalid(!date);
211
+ if (!isControlled) setInternalDate(date);
212
+ onChange == null ? void 0 : onChange(date);
213
+ } else {
214
+ setInputInvalid(false);
215
+ }
216
+ }
217
+ function handleBlur() {
218
+ setFocused(false);
219
+ const digits = inputValue.replace(/\D/g, "");
220
+ if (digits.length > 0 && digits.length < maxDigits || inputInvalid) {
221
+ setInputValue(lastValidRef.current);
222
+ setInputInvalid(false);
223
+ }
224
+ }
225
+ function handleChange(e) {
226
+ var _a;
227
+ const input = e.target;
228
+ const cursorPos = (_a = input.selectionStart) != null ? _a : 0;
229
+ const raw = input.value;
230
+ const digits = raw.replace(/\D/g, "").slice(0, maxDigits);
231
+ const masked = applyMask(digits, maxDigits);
232
+ const digitsBeforeCursor = raw.slice(0, cursorPos).replace(/\D/g, "").length;
233
+ const newCursorPos = getCursorPos(masked, digitsBeforeCursor);
234
+ setInputValue(masked);
235
+ commit(masked);
236
+ requestAnimationFrame(() => {
237
+ var _a2;
238
+ return (_a2 = inputRef.current) == null ? void 0 : _a2.setSelectionRange(newCursorPos, newCursorPos);
239
+ });
240
+ }
241
+ function handleKeyDown(e) {
242
+ var _a;
243
+ const input = e.currentTarget;
244
+ const pos = (_a = input.selectionStart) != null ? _a : 0;
245
+ if (e.key.length === 1 && !/\d/.test(e.key) && !e.ctrlKey && !e.metaKey) {
246
+ e.preventDefault();
247
+ return;
248
+ }
249
+ if (e.key === "Backspace" && pos > 0 && /[.: ]/.test(input.value[pos - 1])) {
250
+ e.preventDefault();
251
+ const val = input.value;
252
+ const masked = applyMask((val.slice(0, pos - 2) + val.slice(pos)).replace(/\D/g, ""), maxDigits);
253
+ setInputValue(masked);
254
+ commit(masked);
255
+ requestAnimationFrame(() => input.setSelectionRange(pos - 2, pos - 2));
256
+ }
257
+ }
258
+ function handlePaste(e) {
259
+ e.preventDefault();
260
+ const masked = applyMask(e.clipboardData.getData("text").replace(/\D/g, ""), maxDigits);
261
+ setInputValue(masked);
262
+ commit(masked);
263
+ requestAnimationFrame(() => {
264
+ var _a;
265
+ return (_a = inputRef.current) == null ? void 0 : _a.setSelectionRange(masked.length, masked.length);
266
+ });
267
+ }
268
+ function handleCalendarSelect(date) {
269
+ if (!date || !isValid(date)) {
270
+ applyValid("", void 0);
271
+ if (!timeFormat) setOpen(false);
272
+ return;
273
+ }
274
+ let dateToCommit = date;
275
+ if (timeFormat) {
276
+ const base = selected && isValid(selected) ? selected : /* @__PURE__ */ new Date(0);
277
+ dateToCommit = new Date(
278
+ date.getFullYear(),
279
+ date.getMonth(),
280
+ date.getDate(),
281
+ base.getHours(),
282
+ base.getMinutes(),
283
+ base.getSeconds()
284
+ );
285
+ }
286
+ applyValid(format(dateToCommit, dateFormat), dateToCommit);
287
+ if (!timeFormat) setOpen(false);
288
+ }
289
+ function handleTimeChange(h, m, s) {
290
+ const base = selected && isValid(selected) ? selected : /* @__PURE__ */ new Date();
291
+ const newDate = new Date(base.getFullYear(), base.getMonth(), base.getDate(), h, m, s);
292
+ applyValid(format(newDate, dateFormat), newDate);
293
+ }
294
+ const interactive = !disabled && !loading;
295
+ return /* @__PURE__ */ jsxs3(
296
+ "div",
297
+ {
298
+ ref: containerRef,
299
+ className: ["datepicker", `datepicker--${size}`, className].filter(Boolean).join(" "),
300
+ "data-focused": focused || open || void 0,
301
+ "data-filled": filled || void 0,
302
+ "data-failed": failed || inputInvalid || void 0,
303
+ "data-disabled": !interactive || void 0,
304
+ children: [
305
+ /* @__PURE__ */ jsxs3(
306
+ "div",
307
+ {
308
+ className: "datepicker__field",
309
+ "data-icon-start": resolvedIcon && iconPosition === "start" ? true : void 0,
310
+ "data-icon-end": resolvedIcon && iconPosition === "end" ? true : void 0,
311
+ onClick: () => {
312
+ var _a;
313
+ return interactive && ((_a = inputRef.current) == null ? void 0 : _a.focus());
314
+ },
315
+ children: [
316
+ resolvedIcon && iconPosition === "start" && /* @__PURE__ */ jsx5("span", { className: "datepicker__icon datepicker__icon--start", children: resolvedIcon }),
317
+ label && /* @__PURE__ */ jsx5("span", { className: "datepicker__label", children: label }),
318
+ /* @__PURE__ */ jsx5(
319
+ "input",
320
+ {
321
+ ref: inputRef,
322
+ type: "text",
323
+ inputMode: "numeric",
324
+ className: "datepicker__input",
325
+ value: inputValue,
326
+ placeholder: label && !focused ? void 0 : defaultPlaceholder,
327
+ disabled: !interactive,
328
+ onChange: handleChange,
329
+ onKeyDown: handleKeyDown,
330
+ onPaste: handlePaste,
331
+ onFocus: () => {
332
+ setFocused(true);
333
+ if (interactive && !noCalendar) setOpen(true);
334
+ },
335
+ onBlur: handleBlur,
336
+ "aria-label": label != null ? label : "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u0430\u0442\u0443",
337
+ "aria-expanded": !noCalendar ? open : void 0,
338
+ "aria-haspopup": !noCalendar ? "dialog" : void 0,
339
+ "aria-invalid": inputInvalid || void 0
340
+ }
341
+ ),
342
+ resolvedIcon && iconPosition === "end" && /* @__PURE__ */ jsx5("span", { className: "datepicker__icon datepicker__icon--end", children: resolvedIcon })
343
+ ]
344
+ }
345
+ ),
346
+ !noCalendar && open && /* @__PURE__ */ jsx5(
347
+ "div",
348
+ {
349
+ className: [
350
+ "datepicker__popover",
351
+ `datepicker__popover--${size}`,
352
+ timeFormat && "datepicker__popover--with-time"
353
+ ].filter(Boolean).join(" "),
354
+ role: "dialog",
355
+ "aria-label": "\u041A\u0430\u043B\u0435\u043D\u0434\u0430\u0440\u044C",
356
+ children: timeFormat ? /* @__PURE__ */ jsxs3(Fragment, { children: [
357
+ /* @__PURE__ */ jsxs3("div", { className: "datepicker__popover-body", children: [
358
+ /* @__PURE__ */ jsx5("div", { className: "datepicker__popover-calendar", children: /* @__PURE__ */ jsx5(
359
+ Calendar,
360
+ {
361
+ mode: "single",
362
+ selected,
363
+ onSelect: handleCalendarSelect,
364
+ startMonth: fromDate,
365
+ endMonth: toDate,
366
+ locale: ru
367
+ }
368
+ ) }),
369
+ /* @__PURE__ */ jsx5("div", { className: "datepicker__time-separator" }),
370
+ /* @__PURE__ */ jsx5("div", { className: "datepicker__popover-time", children: /* @__PURE__ */ jsx5(
371
+ TimePanel,
372
+ {
373
+ value: selected,
374
+ showSeconds,
375
+ onChange: handleTimeChange
376
+ }
377
+ ) })
378
+ ] }),
379
+ /* @__PURE__ */ jsx5("div", { className: "datepicker__popover-footer", children: /* @__PURE__ */ jsx5(
380
+ "button",
381
+ {
382
+ className: "datepicker__ok-btn",
383
+ type: "button",
384
+ onClick: () => setOpen(false),
385
+ children: "OK"
386
+ }
387
+ ) })
388
+ ] }) : /* @__PURE__ */ jsx5(
389
+ Calendar,
390
+ {
391
+ mode: "single",
392
+ selected,
393
+ onSelect: handleCalendarSelect,
394
+ startMonth: fromDate,
395
+ endMonth: toDate,
396
+ locale: ru
397
+ }
398
+ )
399
+ }
400
+ )
401
+ ]
402
+ }
403
+ );
404
+ }
405
+
406
+ // src/components/DateRangePicker/DateRangePicker.tsx
407
+ import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef3, useState as useState2 } from "react";
408
+ import { format as format2, isValid as isValid2, parse as parse2 } from "date-fns";
409
+ import { ru as ru2 } from "date-fns/locale";
410
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
411
+ var DATE_FORMAT2 = "dd.MM.yyyy";
412
+ function applyDateMask(digits) {
413
+ const d = digits.slice(0, 8);
414
+ let result = "";
415
+ for (let i = 0; i < d.length; i++) {
416
+ if (i === 2 || i === 4) result += ".";
417
+ result += d[i];
418
+ }
419
+ return result;
420
+ }
421
+ function applyRangeMask(digits) {
422
+ const all = digits.slice(0, 16);
423
+ const fromMasked = applyDateMask(all.slice(0, 8));
424
+ const toDigits = all.slice(8);
425
+ if (toDigits.length === 0) return fromMasked;
426
+ return `${fromMasked} \u2014 ${applyDateMask(toDigits)}`;
427
+ }
428
+ function getRangeCursorPos(masked, digitCount) {
429
+ if (digitCount === 0) return 0;
430
+ let count = 0;
431
+ for (let i = 0; i < masked.length; i++) {
432
+ if (/\d/.test(masked[i])) {
433
+ count++;
434
+ if (count === digitCount) return i + 1;
435
+ }
436
+ }
437
+ return masked.length;
438
+ }
439
+ function parseDate(masked) {
440
+ if (masked.replace(/\D/g, "").length !== 8) return void 0;
441
+ const date = parse2(masked, DATE_FORMAT2, /* @__PURE__ */ new Date());
442
+ return isValid2(date) && format2(date, DATE_FORMAT2) === masked ? date : void 0;
443
+ }
444
+ function formatRange(from, to) {
445
+ if (!from) return "";
446
+ const fromStr = format2(from, DATE_FORMAT2);
447
+ if (!to) return fromStr;
448
+ return `${fromStr} \u2014 ${format2(to, DATE_FORMAT2)}`;
449
+ }
450
+ function resolveShowSeconds(showTime) {
451
+ if (!showTime) return false;
452
+ if (showTime === true) return true;
453
+ return showTime.format === "HH:mm:ss";
454
+ }
455
+ function DateRangePicker({
456
+ value,
457
+ defaultValue,
458
+ onChange,
459
+ label,
460
+ fromDate: fromConstraint,
461
+ toDate: toConstraint,
462
+ disabled = false,
463
+ failed = false,
464
+ loading = false,
465
+ size = "m",
466
+ calendarLayout = "vertical",
467
+ showTime,
468
+ icon,
469
+ iconPosition = "end",
470
+ className
471
+ }) {
472
+ const resolvedIcon = loading ? /* @__PURE__ */ jsx6(Spinner, {}) : icon === false ? null : icon != null ? icon : /* @__PURE__ */ jsx6(CalendarIcon, {});
473
+ const isControlled = value !== void 0;
474
+ const showSeconds = resolveShowSeconds(showTime);
475
+ const [internalFrom, setInternalFrom] = useState2(defaultValue == null ? void 0 : defaultValue.from);
476
+ const [internalTo, setInternalTo] = useState2(defaultValue == null ? void 0 : defaultValue.to);
477
+ const [inputValue, setInputValue] = useState2(
478
+ () => formatRange(defaultValue == null ? void 0 : defaultValue.from, defaultValue == null ? void 0 : defaultValue.to)
479
+ );
480
+ const [inputInvalid, setInputInvalid] = useState2(false);
481
+ const [open, setOpen] = useState2(false);
482
+ const [focused, setFocused] = useState2(false);
483
+ const [anchorDate, setAnchorDate] = useState2(void 0);
484
+ const [hoveredDate, setHoveredDate] = useState2(void 0);
485
+ const inputRef = useRef3(null);
486
+ const containerRef = useRef3(null);
487
+ const lastEmittedFromRef = useRef3(value !== void 0 ? value == null ? void 0 : value.from : defaultValue == null ? void 0 : defaultValue.from);
488
+ const lastEmittedToRef = useRef3(value !== void 0 ? value == null ? void 0 : value.to : defaultValue == null ? void 0 : defaultValue.to);
489
+ const wasControlledRef = useRef3(value !== void 0);
490
+ const confirmedFrom = isControlled ? value == null ? void 0 : value.from : internalFrom;
491
+ const confirmedTo = isControlled ? value == null ? void 0 : value.to : internalTo;
492
+ const filled = inputValue.length > 0;
493
+ const close = useCallback2(() => {
494
+ setOpen(false);
495
+ setAnchorDate(void 0);
496
+ setHoveredDate(void 0);
497
+ }, []);
498
+ useClickOutside(containerRef, close);
499
+ useEffect4(() => {
500
+ var _a, _b, _c, _d, _e, _f;
501
+ if (value !== void 0) wasControlledRef.current = true;
502
+ const newFrom = value == null ? void 0 : value.from;
503
+ const newTo = value == null ? void 0 : value.to;
504
+ const fromTime = (_a = newFrom == null ? void 0 : newFrom.getTime()) != null ? _a : null;
505
+ const toTime = (_b = newTo == null ? void 0 : newTo.getTime()) != null ? _b : null;
506
+ const lastFromTime = (_d = (_c = lastEmittedFromRef.current) == null ? void 0 : _c.getTime()) != null ? _d : null;
507
+ const lastToTime = (_f = (_e = lastEmittedToRef.current) == null ? void 0 : _e.getTime()) != null ? _f : null;
508
+ if (fromTime === lastFromTime && toTime === lastToTime) return;
509
+ if (!wasControlledRef.current && value === void 0) return;
510
+ setInputValue(formatRange(newFrom, newTo));
511
+ setInputInvalid(false);
512
+ if (!isControlled) {
513
+ setInternalFrom(newFrom);
514
+ setInternalTo(newTo);
515
+ }
516
+ setAnchorDate(void 0);
517
+ setHoveredDate(void 0);
518
+ lastEmittedFromRef.current = newFrom;
519
+ lastEmittedToRef.current = newTo;
520
+ }, [value]);
521
+ const calendarSelected = anchorDate ? hoveredDate ? anchorDate <= hoveredDate ? { from: anchorDate, to: hoveredDate } : { from: hoveredDate, to: anchorDate } : { from: anchorDate, to: void 0 } : { from: confirmedFrom, to: confirmedTo };
522
+ function handleDayClick(day) {
523
+ var _a, _b, _c, _d, _e, _f;
524
+ if (!anchorDate) {
525
+ const from = showTime ? new Date(
526
+ day.getFullYear(),
527
+ day.getMonth(),
528
+ day.getDate(),
529
+ (_a = confirmedFrom == null ? void 0 : confirmedFrom.getHours()) != null ? _a : 0,
530
+ (_b = confirmedFrom == null ? void 0 : confirmedFrom.getMinutes()) != null ? _b : 0,
531
+ (_c = confirmedFrom == null ? void 0 : confirmedFrom.getSeconds()) != null ? _c : 0
532
+ ) : day;
533
+ setAnchorDate(from);
534
+ if (!isControlled) {
535
+ setInternalFrom(from);
536
+ setInternalTo(void 0);
537
+ }
538
+ setInputValue(format2(day, DATE_FORMAT2));
539
+ setInputInvalid(false);
540
+ lastEmittedFromRef.current = from;
541
+ lastEmittedToRef.current = void 0;
542
+ onChange == null ? void 0 : onChange({ from, to: void 0 });
543
+ } else {
544
+ let from = anchorDate, to = showTime ? new Date(
545
+ day.getFullYear(),
546
+ day.getMonth(),
547
+ day.getDate(),
548
+ (_d = confirmedTo == null ? void 0 : confirmedTo.getHours()) != null ? _d : 0,
549
+ (_e = confirmedTo == null ? void 0 : confirmedTo.getMinutes()) != null ? _e : 0,
550
+ (_f = confirmedTo == null ? void 0 : confirmedTo.getSeconds()) != null ? _f : 0
551
+ ) : day;
552
+ if (day < anchorDate) {
553
+ const tmp = from;
554
+ from = to;
555
+ to = tmp;
556
+ }
557
+ if (!isControlled) {
558
+ setInternalFrom(from);
559
+ setInternalTo(to);
560
+ }
561
+ setInputValue(formatRange(from, to));
562
+ setInputInvalid(false);
563
+ lastEmittedFromRef.current = from;
564
+ lastEmittedToRef.current = to;
565
+ onChange == null ? void 0 : onChange({ from, to });
566
+ if (!showTime) close();
567
+ else setAnchorDate(void 0);
568
+ }
569
+ }
570
+ function handleDayMouseEnter(day) {
571
+ if (anchorDate) setHoveredDate(day);
572
+ }
573
+ function handleFromTimeChange(h, m, s) {
574
+ const base = confirmedFrom != null ? confirmedFrom : /* @__PURE__ */ new Date();
575
+ const newDate = new Date(base.getFullYear(), base.getMonth(), base.getDate(), h, m, s);
576
+ if (!isControlled) setInternalFrom(newDate);
577
+ lastEmittedFromRef.current = newDate;
578
+ onChange == null ? void 0 : onChange({ from: newDate, to: confirmedTo });
579
+ }
580
+ function handleToTimeChange(h, m, s) {
581
+ const base = confirmedTo != null ? confirmedTo : /* @__PURE__ */ new Date();
582
+ const newDate = new Date(base.getFullYear(), base.getMonth(), base.getDate(), h, m, s);
583
+ if (!isControlled) setInternalTo(newDate);
584
+ lastEmittedToRef.current = newDate;
585
+ onChange == null ? void 0 : onChange({ from: confirmedFrom, to: newDate });
586
+ }
587
+ function handleChange(e) {
588
+ var _a;
589
+ const input = e.target;
590
+ const cursorPos = (_a = input.selectionStart) != null ? _a : 0;
591
+ const raw = input.value;
592
+ const digits = raw.replace(/\D/g, "").slice(0, 16);
593
+ const masked = applyRangeMask(digits);
594
+ const digitsBeforeCursor = raw.slice(0, cursorPos).replace(/\D/g, "").length;
595
+ setInputValue(masked);
596
+ setAnchorDate(void 0);
597
+ setHoveredDate(void 0);
598
+ const fromDigits = digits.slice(0, 8);
599
+ const toDigits = digits.slice(8);
600
+ const parsedFrom = fromDigits.length === 8 ? parseDate(applyDateMask(fromDigits)) : void 0;
601
+ const parsedTo = toDigits.length === 8 ? parseDate(applyDateMask(toDigits)) : void 0;
602
+ const fromComplete = fromDigits.length === 8;
603
+ const toComplete = toDigits.length === 8;
604
+ setInputInvalid(fromComplete && !parsedFrom || toComplete && !parsedTo);
605
+ if (!isControlled) {
606
+ setInternalFrom(parsedFrom);
607
+ setInternalTo(parsedTo);
608
+ }
609
+ lastEmittedFromRef.current = parsedFrom;
610
+ lastEmittedToRef.current = parsedTo;
611
+ onChange == null ? void 0 : onChange(parsedFrom || parsedTo ? { from: parsedFrom, to: parsedTo } : void 0);
612
+ requestAnimationFrame(
613
+ () => {
614
+ var _a2;
615
+ return (_a2 = inputRef.current) == null ? void 0 : _a2.setSelectionRange(
616
+ getRangeCursorPos(masked, digitsBeforeCursor),
617
+ getRangeCursorPos(masked, digitsBeforeCursor)
618
+ );
619
+ }
620
+ );
621
+ }
622
+ function handleKeyDown(e) {
623
+ var _a, _b, _c;
624
+ const input = e.currentTarget;
625
+ const pos = (_a = input.selectionStart) != null ? _a : 0;
626
+ if (e.key.length === 1 && !/\d/.test(e.key) && !e.ctrlKey && !e.metaKey) {
627
+ e.preventDefault();
628
+ return;
629
+ }
630
+ if (e.key === "Backspace" && pos > 0 && /[\s—]/.test(input.value[pos - 1])) {
631
+ e.preventDefault();
632
+ const val = input.value;
633
+ const charsToSkip = (_c = (_b = val.slice(0, pos).match(/[\s—]+$/)) == null ? void 0 : _b[0].length) != null ? _c : 1;
634
+ const newPos = pos - charsToSkip;
635
+ const masked = applyRangeMask((val.slice(0, newPos - 1) + val.slice(newPos)).replace(/\D/g, ""));
636
+ setInputValue(masked);
637
+ requestAnimationFrame(() => input.setSelectionRange(newPos - 1, newPos - 1));
638
+ }
639
+ }
640
+ function handlePaste(e) {
641
+ e.preventDefault();
642
+ const text = e.clipboardData.getData("text");
643
+ const digits = text.replace(/\D/g, "").slice(0, 16);
644
+ const masked = applyRangeMask(digits);
645
+ setInputValue(masked);
646
+ setAnchorDate(void 0);
647
+ setHoveredDate(void 0);
648
+ const parsedFrom = digits.length >= 8 ? parseDate(applyDateMask(digits.slice(0, 8))) : void 0;
649
+ const parsedTo = digits.length >= 16 ? parseDate(applyDateMask(digits.slice(8, 16))) : void 0;
650
+ setInputInvalid(digits.length >= 8 && !parsedFrom || digits.length >= 16 && !parsedTo);
651
+ if (!isControlled) {
652
+ setInternalFrom(parsedFrom);
653
+ setInternalTo(parsedTo);
654
+ }
655
+ lastEmittedFromRef.current = parsedFrom;
656
+ lastEmittedToRef.current = parsedTo;
657
+ onChange == null ? void 0 : onChange(parsedFrom || parsedTo ? { from: parsedFrom, to: parsedTo } : void 0);
658
+ requestAnimationFrame(() => {
659
+ var _a;
660
+ return (_a = inputRef.current) == null ? void 0 : _a.setSelectionRange(masked.length, masked.length);
661
+ });
662
+ }
663
+ const placeholder = label && !focused && !filled ? void 0 : "\u0434\u0434.\u043C\u043C.\u0433\u0433\u0433\u0433 \u2014 \u0434\u0434.\u043C\u043C.\u0433\u0433\u0433\u0433";
664
+ const interactive = !disabled && !loading;
665
+ return /* @__PURE__ */ jsxs4(
666
+ "div",
667
+ {
668
+ ref: containerRef,
669
+ className: ["datepicker", "daterangepicker", `datepicker--${size}`, className].filter(Boolean).join(" "),
670
+ "data-focused": focused || open || void 0,
671
+ "data-filled": filled || void 0,
672
+ "data-failed": failed || inputInvalid || void 0,
673
+ "data-disabled": !interactive || void 0,
674
+ children: [
675
+ /* @__PURE__ */ jsxs4(
676
+ "div",
677
+ {
678
+ className: "datepicker__field",
679
+ "data-icon-start": resolvedIcon && iconPosition === "start" ? true : void 0,
680
+ "data-icon-end": resolvedIcon && iconPosition === "end" ? true : void 0,
681
+ onClick: () => {
682
+ var _a;
683
+ return interactive && ((_a = inputRef.current) == null ? void 0 : _a.focus());
684
+ },
685
+ children: [
686
+ resolvedIcon && iconPosition === "start" && /* @__PURE__ */ jsx6("span", { className: "datepicker__icon datepicker__icon--start", children: resolvedIcon }),
687
+ label && /* @__PURE__ */ jsx6("span", { className: "datepicker__label", children: label }),
688
+ /* @__PURE__ */ jsx6(
689
+ "input",
690
+ {
691
+ ref: inputRef,
692
+ type: "text",
693
+ inputMode: "numeric",
694
+ className: "datepicker__input",
695
+ value: inputValue,
696
+ placeholder,
697
+ disabled: !interactive,
698
+ onChange: handleChange,
699
+ onKeyDown: handleKeyDown,
700
+ onPaste: handlePaste,
701
+ onFocus: () => {
702
+ setFocused(true);
703
+ if (interactive) setOpen(true);
704
+ },
705
+ onBlur: () => setFocused(false),
706
+ "aria-label": label != null ? label : "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043F\u0435\u0440\u0438\u043E\u0434",
707
+ "aria-expanded": open,
708
+ "aria-haspopup": "dialog",
709
+ "aria-invalid": inputInvalid || void 0
710
+ }
711
+ ),
712
+ resolvedIcon && iconPosition === "end" && /* @__PURE__ */ jsx6("span", { className: "datepicker__icon datepicker__icon--end", children: resolvedIcon })
713
+ ]
714
+ }
715
+ ),
716
+ open && /* @__PURE__ */ jsx6(
717
+ "div",
718
+ {
719
+ className: [
720
+ "datepicker__popover",
721
+ `datepicker__popover--${size}`,
722
+ calendarLayout === "horizontal" && "datepicker__popover--horizontal",
723
+ showTime && "datepicker__popover--with-time"
724
+ ].filter(Boolean).join(" "),
725
+ role: "dialog",
726
+ "aria-label": "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043F\u0435\u0440\u0438\u043E\u0434",
727
+ children: showTime ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
728
+ /* @__PURE__ */ jsx6("div", { className: "datepicker__popover-body", children: /* @__PURE__ */ jsx6("div", { className: "datepicker__popover-calendar", children: /* @__PURE__ */ jsx6(
729
+ Calendar,
730
+ {
731
+ mode: "range",
732
+ selected: calendarSelected,
733
+ onSelect: () => {
734
+ },
735
+ onDayClick: handleDayClick,
736
+ onDayMouseEnter: handleDayMouseEnter,
737
+ onDayMouseLeave: () => setHoveredDate(void 0),
738
+ startMonth: fromConstraint,
739
+ endMonth: toConstraint,
740
+ numberOfMonths: 2,
741
+ locale: ru2
742
+ }
743
+ ) }) }),
744
+ /* @__PURE__ */ jsxs4("div", { className: "datepicker__time-row", children: [
745
+ /* @__PURE__ */ jsxs4("div", { className: "datepicker__time-col", children: [
746
+ /* @__PURE__ */ jsx6("span", { className: "datepicker__time-label", children: "\u041D\u0430\u0447\u0430\u043B\u043E" }),
747
+ /* @__PURE__ */ jsx6(TimePanel, { value: confirmedFrom, showSeconds, onChange: handleFromTimeChange })
748
+ ] }),
749
+ /* @__PURE__ */ jsx6("div", { className: "datepicker__time-separator" }),
750
+ /* @__PURE__ */ jsxs4("div", { className: "datepicker__time-col", children: [
751
+ /* @__PURE__ */ jsx6("span", { className: "datepicker__time-label", children: "\u041A\u043E\u043D\u0435\u0446" }),
752
+ /* @__PURE__ */ jsx6(TimePanel, { value: confirmedTo, showSeconds, onChange: handleToTimeChange })
753
+ ] })
754
+ ] }),
755
+ /* @__PURE__ */ jsx6("div", { className: "datepicker__popover-footer", children: /* @__PURE__ */ jsx6("button", { className: "datepicker__ok-btn", type: "button", onClick: close, children: "OK" }) })
756
+ ] }) : /* @__PURE__ */ jsx6(
757
+ Calendar,
758
+ {
759
+ mode: "range",
760
+ selected: calendarSelected,
761
+ onSelect: () => {
762
+ },
763
+ onDayClick: handleDayClick,
764
+ onDayMouseEnter: handleDayMouseEnter,
765
+ onDayMouseLeave: () => setHoveredDate(void 0),
766
+ startMonth: fromConstraint,
767
+ endMonth: toConstraint,
768
+ numberOfMonths: 2,
769
+ locale: ru2
770
+ }
771
+ )
772
+ }
773
+ )
774
+ ]
775
+ }
776
+ );
777
+ }
778
+
779
+ export {
780
+ Calendar,
781
+ Spinner,
782
+ DatePicker,
783
+ DateRangePicker
784
+ };
785
+ //# sourceMappingURL=chunk-DQSX6QKR.js.map