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