@artemy-tech/datepicker 0.0.3 → 0.2.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/index.js CHANGED
@@ -12,7 +12,7 @@ function Calendar({ className, ...props }) {
12
12
  }
13
13
 
14
14
  // src/components/DatePicker/DatePicker.tsx
15
- import { useCallback, useRef, useState } from "react";
15
+ import { useCallback, useRef as useRef2, useState } from "react";
16
16
  import { format, isValid, parse } from "date-fns";
17
17
  import { ru } from "date-fns/locale";
18
18
 
@@ -29,14 +29,83 @@ function useClickOutside(ref, handler) {
29
29
  }, [ref, handler]);
30
30
  }
31
31
 
32
- // src/components/DatePicker/DatePicker.tsx
32
+ // src/components/TimePanel/TimePanel.tsx
33
+ import { useEffect as useEffect2, useRef } from "react";
33
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/DatePicker/DatePicker.tsx
84
+ import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
34
85
  var DATE_FORMAT = "dd.MM.yyyy";
35
- function applyMask(digits) {
36
- const d = digits.slice(0, 8);
86
+ function resolveTimeFormat(showTime) {
87
+ if (!showTime) return null;
88
+ if (showTime === true) return "HH:mm:ss";
89
+ return showTime.format;
90
+ }
91
+ function buildDateFormat(timeFormat) {
92
+ return timeFormat ? `${DATE_FORMAT} ${timeFormat}` : DATE_FORMAT;
93
+ }
94
+ function buildMaxDigits(timeFormat) {
95
+ if (!timeFormat) return 8;
96
+ return timeFormat === "HH:mm" ? 12 : 14;
97
+ }
98
+ function buildPlaceholder(timeFormat) {
99
+ if (!timeFormat) return "\u0434\u0434.\u043C\u043C.\u0433\u0433\u0433\u0433";
100
+ 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";
101
+ }
102
+ function applyMask(digits, maxDigits) {
103
+ const d = digits.slice(0, maxDigits);
37
104
  let result = "";
38
105
  for (let i = 0; i < d.length; i++) {
39
106
  if (i === 2 || i === 4) result += ".";
107
+ else if (i === 8) result += " ";
108
+ else if (i === 10 || i === 12) result += ":";
40
109
  result += d[i];
41
110
  }
42
111
  return result;
@@ -52,45 +121,66 @@ function getCursorPos(masked, digitCount) {
52
121
  }
53
122
  return masked.length;
54
123
  }
55
- function parseDate(masked) {
56
- if (masked.replace(/\D/g, "").length !== 8) return void 0;
57
- const date = parse(masked, DATE_FORMAT, /* @__PURE__ */ new Date());
58
- return isValid(date) && format(date, DATE_FORMAT) === masked ? date : void 0;
124
+ function parseDateTime(masked, dateFormat, maxDigits) {
125
+ if (masked.replace(/\D/g, "").length !== maxDigits) return void 0;
126
+ const date = parse(masked, dateFormat, /* @__PURE__ */ new Date());
127
+ return isValid(date) && format(date, dateFormat) === masked ? date : void 0;
59
128
  }
60
129
  function DatePicker({
61
130
  value,
62
131
  defaultValue,
63
132
  onChange,
64
133
  label,
65
- placeholder = "\u0434\u0434.\u043C\u043C.\u0433\u0433\u0433\u0433",
134
+ placeholder,
66
135
  fromDate,
67
136
  toDate,
68
137
  disabled = false,
69
138
  failed = false,
139
+ size = "m",
140
+ noCalendar = false,
141
+ showTime,
142
+ icon,
143
+ iconPosition = "end",
70
144
  className
71
145
  }) {
146
+ const timeFormat = resolveTimeFormat(showTime);
147
+ const dateFormat = buildDateFormat(timeFormat);
148
+ const maxDigits = buildMaxDigits(timeFormat);
149
+ const defaultPlaceholder = placeholder != null ? placeholder : buildPlaceholder(timeFormat);
150
+ const showSeconds = timeFormat === "HH:mm:ss";
151
+ const resolvedIcon = icon === false ? null : icon != null ? icon : /* @__PURE__ */ jsx4(CalendarIcon, {});
72
152
  const isControlled = value !== void 0;
73
153
  const [internalDate, setInternalDate] = useState(defaultValue);
74
154
  const [open, setOpen] = useState(false);
75
155
  const [focused, setFocused] = useState(false);
76
156
  const [inputValue, setInputValue] = useState(
77
- () => defaultValue && isValid(defaultValue) ? format(defaultValue, DATE_FORMAT) : ""
157
+ () => defaultValue && isValid(defaultValue) ? format(defaultValue, dateFormat) : ""
78
158
  );
79
159
  const [inputInvalid, setInputInvalid] = useState(false);
80
- const inputRef = useRef(null);
81
- const containerRef = useRef(null);
160
+ const inputRef = useRef2(null);
161
+ const containerRef = useRef2(null);
162
+ const lastValidRef = useRef2(inputValue);
82
163
  const selected = isControlled ? value : internalDate;
83
164
  const filled = inputValue.length > 0;
84
165
  const close = useCallback(() => setOpen(false), []);
85
166
  useClickOutside(containerRef, close);
167
+ function applyValid(masked, date) {
168
+ lastValidRef.current = masked;
169
+ setInputValue(masked);
170
+ setInputInvalid(false);
171
+ if (!isControlled) setInternalDate(date);
172
+ onChange == null ? void 0 : onChange(date);
173
+ }
86
174
  function commit(masked) {
87
175
  const digits = masked.replace(/\D/g, "");
88
176
  if (digits.length === 0) {
177
+ lastValidRef.current = "";
89
178
  setInputInvalid(false);
90
179
  if (!isControlled) setInternalDate(void 0);
91
180
  onChange == null ? void 0 : onChange(void 0);
92
- } else if (digits.length === 8) {
93
- const date = parseDate(masked);
181
+ } else if (digits.length === maxDigits) {
182
+ const date = parseDateTime(masked, dateFormat, maxDigits);
183
+ if (date) lastValidRef.current = masked;
94
184
  setInputInvalid(!date);
95
185
  if (!isControlled) setInternalDate(date);
96
186
  onChange == null ? void 0 : onChange(date);
@@ -98,13 +188,21 @@ function DatePicker({
98
188
  setInputInvalid(false);
99
189
  }
100
190
  }
191
+ function handleBlur() {
192
+ setFocused(false);
193
+ const digits = inputValue.replace(/\D/g, "");
194
+ if (digits.length > 0 && digits.length < maxDigits || inputInvalid) {
195
+ setInputValue(lastValidRef.current);
196
+ setInputInvalid(false);
197
+ }
198
+ }
101
199
  function handleChange(e) {
102
200
  var _a;
103
201
  const input = e.target;
104
202
  const cursorPos = (_a = input.selectionStart) != null ? _a : 0;
105
203
  const raw = input.value;
106
- const digits = raw.replace(/\D/g, "").slice(0, 8);
107
- const masked = applyMask(digits);
204
+ const digits = raw.replace(/\D/g, "").slice(0, maxDigits);
205
+ const masked = applyMask(digits, maxDigits);
108
206
  const digitsBeforeCursor = raw.slice(0, cursorPos).replace(/\D/g, "").length;
109
207
  const newCursorPos = getCursorPos(masked, digitsBeforeCursor);
110
208
  setInputValue(masked);
@@ -122,10 +220,10 @@ function DatePicker({
122
220
  e.preventDefault();
123
221
  return;
124
222
  }
125
- if (e.key === "Backspace" && pos > 0 && input.value[pos - 1] === ".") {
223
+ if (e.key === "Backspace" && pos > 0 && /[.: ]/.test(input.value[pos - 1])) {
126
224
  e.preventDefault();
127
225
  const val = input.value;
128
- const masked = applyMask((val.slice(0, pos - 2) + val.slice(pos)).replace(/\D/g, ""));
226
+ const masked = applyMask((val.slice(0, pos - 2) + val.slice(pos)).replace(/\D/g, ""), maxDigits);
129
227
  setInputValue(masked);
130
228
  commit(masked);
131
229
  requestAnimationFrame(() => input.setSelectionRange(pos - 2, pos - 2));
@@ -133,7 +231,7 @@ function DatePicker({
133
231
  }
134
232
  function handlePaste(e) {
135
233
  e.preventDefault();
136
- const masked = applyMask(e.clipboardData.getData("text").replace(/\D/g, ""));
234
+ const masked = applyMask(e.clipboardData.getData("text").replace(/\D/g, ""), maxDigits);
137
235
  setInputValue(masked);
138
236
  commit(masked);
139
237
  requestAnimationFrame(() => {
@@ -141,74 +239,148 @@ function DatePicker({
141
239
  return (_a = inputRef.current) == null ? void 0 : _a.setSelectionRange(masked.length, masked.length);
142
240
  });
143
241
  }
144
- function handleSelect(date) {
145
- if (!isControlled) setInternalDate(date);
146
- setInputValue(date && isValid(date) ? format(date, DATE_FORMAT) : "");
147
- setInputInvalid(false);
148
- onChange == null ? void 0 : onChange(date);
149
- setOpen(false);
242
+ function handleCalendarSelect(date) {
243
+ if (!date || !isValid(date)) {
244
+ applyValid("", void 0);
245
+ if (!timeFormat) setOpen(false);
246
+ return;
247
+ }
248
+ let dateToCommit = date;
249
+ if (timeFormat) {
250
+ const base = selected && isValid(selected) ? selected : /* @__PURE__ */ new Date(0);
251
+ dateToCommit = new Date(
252
+ date.getFullYear(),
253
+ date.getMonth(),
254
+ date.getDate(),
255
+ base.getHours(),
256
+ base.getMinutes(),
257
+ base.getSeconds()
258
+ );
259
+ }
260
+ applyValid(format(dateToCommit, dateFormat), dateToCommit);
261
+ if (!timeFormat) setOpen(false);
262
+ }
263
+ function handleTimeChange(h, m, s) {
264
+ const base = selected && isValid(selected) ? selected : /* @__PURE__ */ new Date();
265
+ const newDate = new Date(base.getFullYear(), base.getMonth(), base.getDate(), h, m, s);
266
+ applyValid(format(newDate, dateFormat), newDate);
150
267
  }
151
- return /* @__PURE__ */ jsxs(
268
+ return /* @__PURE__ */ jsxs3(
152
269
  "div",
153
270
  {
154
271
  ref: containerRef,
155
- className: ["datepicker", className].filter(Boolean).join(" "),
272
+ className: ["datepicker", `datepicker--${size}`, className].filter(Boolean).join(" "),
156
273
  "data-focused": focused || open || void 0,
157
274
  "data-filled": filled || void 0,
158
275
  "data-failed": failed || inputInvalid || void 0,
159
276
  "data-disabled": disabled || void 0,
160
277
  children: [
161
- /* @__PURE__ */ jsxs("div", { className: "datepicker__field", onClick: () => {
162
- var _a;
163
- return !disabled && ((_a = inputRef.current) == null ? void 0 : _a.focus());
164
- }, children: [
165
- label && /* @__PURE__ */ jsx2("span", { className: "datepicker__label", children: label }),
166
- /* @__PURE__ */ jsx2(
167
- "input",
168
- {
169
- ref: inputRef,
170
- type: "text",
171
- inputMode: "numeric",
172
- className: "datepicker__input",
173
- value: inputValue,
174
- placeholder: label && !focused ? void 0 : placeholder,
175
- disabled,
176
- onChange: handleChange,
177
- onKeyDown: handleKeyDown,
178
- onPaste: handlePaste,
179
- onFocus: () => {
180
- setFocused(true);
181
- if (!disabled) setOpen(true);
182
- },
183
- onBlur: () => setFocused(false),
184
- "aria-label": label != null ? label : "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u0430\u0442\u0443",
185
- "aria-expanded": open,
186
- "aria-haspopup": "dialog",
187
- "aria-invalid": inputInvalid || void 0
188
- }
189
- )
190
- ] }),
191
- open && /* @__PURE__ */ jsx2("div", { className: "datepicker__popover", role: "dialog", "aria-label": "\u041A\u0430\u043B\u0435\u043D\u0434\u0430\u0440\u044C", children: /* @__PURE__ */ jsx2(
192
- Calendar,
278
+ /* @__PURE__ */ jsxs3(
279
+ "div",
280
+ {
281
+ className: "datepicker__field",
282
+ "data-icon-start": resolvedIcon && iconPosition === "start" ? true : void 0,
283
+ "data-icon-end": resolvedIcon && iconPosition === "end" ? true : void 0,
284
+ onClick: () => {
285
+ var _a;
286
+ return !disabled && ((_a = inputRef.current) == null ? void 0 : _a.focus());
287
+ },
288
+ children: [
289
+ resolvedIcon && iconPosition === "start" && /* @__PURE__ */ jsx4("span", { className: "datepicker__icon datepicker__icon--start", children: resolvedIcon }),
290
+ label && /* @__PURE__ */ jsx4("span", { className: "datepicker__label", children: label }),
291
+ /* @__PURE__ */ jsx4(
292
+ "input",
293
+ {
294
+ ref: inputRef,
295
+ type: "text",
296
+ inputMode: "numeric",
297
+ className: "datepicker__input",
298
+ value: inputValue,
299
+ placeholder: label && !focused ? void 0 : defaultPlaceholder,
300
+ disabled,
301
+ onChange: handleChange,
302
+ onKeyDown: handleKeyDown,
303
+ onPaste: handlePaste,
304
+ onFocus: () => {
305
+ setFocused(true);
306
+ if (!disabled && !noCalendar) setOpen(true);
307
+ },
308
+ onBlur: handleBlur,
309
+ "aria-label": label != null ? label : "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u0430\u0442\u0443",
310
+ "aria-expanded": !noCalendar ? open : void 0,
311
+ "aria-haspopup": !noCalendar ? "dialog" : void 0,
312
+ "aria-invalid": inputInvalid || void 0
313
+ }
314
+ ),
315
+ resolvedIcon && iconPosition === "end" && /* @__PURE__ */ jsx4("span", { className: "datepicker__icon datepicker__icon--end", children: resolvedIcon })
316
+ ]
317
+ }
318
+ ),
319
+ !noCalendar && open && /* @__PURE__ */ jsx4(
320
+ "div",
193
321
  {
194
- mode: "single",
195
- selected,
196
- onSelect: handleSelect,
197
- startMonth: fromDate,
198
- endMonth: toDate,
199
- locale: ru
322
+ className: [
323
+ "datepicker__popover",
324
+ `datepicker__popover--${size}`,
325
+ timeFormat && "datepicker__popover--with-time"
326
+ ].filter(Boolean).join(" "),
327
+ role: "dialog",
328
+ "aria-label": "\u041A\u0430\u043B\u0435\u043D\u0434\u0430\u0440\u044C",
329
+ children: timeFormat ? /* @__PURE__ */ jsxs3(Fragment, { children: [
330
+ /* @__PURE__ */ jsxs3("div", { className: "datepicker__popover-body", children: [
331
+ /* @__PURE__ */ jsx4("div", { className: "datepicker__popover-calendar", children: /* @__PURE__ */ jsx4(
332
+ Calendar,
333
+ {
334
+ mode: "single",
335
+ selected,
336
+ onSelect: handleCalendarSelect,
337
+ startMonth: fromDate,
338
+ endMonth: toDate,
339
+ locale: ru
340
+ }
341
+ ) }),
342
+ /* @__PURE__ */ jsx4("div", { className: "datepicker__time-separator" }),
343
+ /* @__PURE__ */ jsx4("div", { className: "datepicker__popover-time", children: /* @__PURE__ */ jsx4(
344
+ TimePanel,
345
+ {
346
+ value: selected,
347
+ showSeconds,
348
+ onChange: handleTimeChange
349
+ }
350
+ ) })
351
+ ] }),
352
+ /* @__PURE__ */ jsx4("div", { className: "datepicker__popover-footer", children: /* @__PURE__ */ jsx4(
353
+ "button",
354
+ {
355
+ className: "datepicker__ok-btn",
356
+ type: "button",
357
+ onClick: () => setOpen(false),
358
+ children: "OK"
359
+ }
360
+ ) })
361
+ ] }) : /* @__PURE__ */ jsx4(
362
+ Calendar,
363
+ {
364
+ mode: "single",
365
+ selected,
366
+ onSelect: handleCalendarSelect,
367
+ startMonth: fromDate,
368
+ endMonth: toDate,
369
+ locale: ru
370
+ }
371
+ )
200
372
  }
201
- ) })
373
+ )
202
374
  ]
203
375
  }
204
376
  );
205
377
  }
206
378
 
207
379
  // src/components/DateRangePicker/DateRangePicker.tsx
208
- import { useCallback as useCallback2, useRef as useRef2, useState as useState2 } from "react";
380
+ import { useCallback as useCallback2, useRef as useRef3, useState as useState2 } from "react";
209
381
  import { format as format2, isValid as isValid2, parse as parse2 } from "date-fns";
210
382
  import { ru as ru2 } from "date-fns/locale";
211
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
383
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
212
384
  var DATE_FORMAT2 = "dd.MM.yyyy";
213
385
  function applyDateMask(digits) {
214
386
  const d = digits.slice(0, 8);
@@ -237,7 +409,7 @@ function getRangeCursorPos(masked, digitCount) {
237
409
  }
238
410
  return masked.length;
239
411
  }
240
- function parseDate2(masked) {
412
+ function parseDate(masked) {
241
413
  if (masked.replace(/\D/g, "").length !== 8) return void 0;
242
414
  const date = parse2(masked, DATE_FORMAT2, /* @__PURE__ */ new Date());
243
415
  return isValid2(date) && format2(date, DATE_FORMAT2) === masked ? date : void 0;
@@ -248,6 +420,11 @@ function formatRange(from, to) {
248
420
  if (!to) return fromStr;
249
421
  return `${fromStr} \u2014 ${format2(to, DATE_FORMAT2)}`;
250
422
  }
423
+ function resolveShowSeconds(showTime) {
424
+ if (!showTime) return false;
425
+ if (showTime === true) return true;
426
+ return showTime.format === "HH:mm:ss";
427
+ }
251
428
  function DateRangePicker({
252
429
  value,
253
430
  defaultValue,
@@ -257,9 +434,16 @@ function DateRangePicker({
257
434
  toDate: toConstraint,
258
435
  disabled = false,
259
436
  failed = false,
437
+ size = "m",
438
+ calendarLayout = "vertical",
439
+ showTime,
440
+ icon,
441
+ iconPosition = "end",
260
442
  className
261
443
  }) {
444
+ const resolvedIcon = icon === false ? null : icon != null ? icon : /* @__PURE__ */ jsx5(CalendarIcon, {});
262
445
  const isControlled = value !== void 0;
446
+ const showSeconds = resolveShowSeconds(showTime);
263
447
  const [internalFrom, setInternalFrom] = useState2(defaultValue == null ? void 0 : defaultValue.from);
264
448
  const [internalTo, setInternalTo] = useState2(defaultValue == null ? void 0 : defaultValue.to);
265
449
  const [inputValue, setInputValue] = useState2(
@@ -270,8 +454,8 @@ function DateRangePicker({
270
454
  const [focused, setFocused] = useState2(false);
271
455
  const [anchorDate, setAnchorDate] = useState2(void 0);
272
456
  const [hoveredDate, setHoveredDate] = useState2(void 0);
273
- const inputRef = useRef2(null);
274
- const containerRef = useRef2(null);
457
+ const inputRef = useRef3(null);
458
+ const containerRef = useRef3(null);
275
459
  const confirmedFrom = isControlled ? value == null ? void 0 : value.from : internalFrom;
276
460
  const confirmedTo = isControlled ? value == null ? void 0 : value.to : internalTo;
277
461
  const filled = inputValue.length > 0;
@@ -283,20 +467,37 @@ function DateRangePicker({
283
467
  useClickOutside(containerRef, close);
284
468
  const calendarSelected = anchorDate ? hoveredDate ? anchorDate <= hoveredDate ? { from: anchorDate, to: hoveredDate } : { from: hoveredDate, to: anchorDate } : { from: anchorDate, to: void 0 } : { from: confirmedFrom, to: confirmedTo };
285
469
  function handleDayClick(day) {
470
+ var _a, _b, _c, _d, _e, _f;
286
471
  if (!anchorDate) {
287
- setAnchorDate(day);
472
+ const from = showTime ? new Date(
473
+ day.getFullYear(),
474
+ day.getMonth(),
475
+ day.getDate(),
476
+ (_a = confirmedFrom == null ? void 0 : confirmedFrom.getHours()) != null ? _a : 0,
477
+ (_b = confirmedFrom == null ? void 0 : confirmedFrom.getMinutes()) != null ? _b : 0,
478
+ (_c = confirmedFrom == null ? void 0 : confirmedFrom.getSeconds()) != null ? _c : 0
479
+ ) : day;
480
+ setAnchorDate(from);
288
481
  if (!isControlled) {
289
- setInternalFrom(day);
482
+ setInternalFrom(from);
290
483
  setInternalTo(void 0);
291
484
  }
292
485
  setInputValue(format2(day, DATE_FORMAT2));
293
486
  setInputInvalid(false);
294
- onChange == null ? void 0 : onChange(day ? { from: day, to: void 0 } : void 0);
487
+ onChange == null ? void 0 : onChange({ from, to: void 0 });
295
488
  } else {
296
- let from = anchorDate, to = day;
489
+ let from = anchorDate, to = showTime ? new Date(
490
+ day.getFullYear(),
491
+ day.getMonth(),
492
+ day.getDate(),
493
+ (_d = confirmedTo == null ? void 0 : confirmedTo.getHours()) != null ? _d : 0,
494
+ (_e = confirmedTo == null ? void 0 : confirmedTo.getMinutes()) != null ? _e : 0,
495
+ (_f = confirmedTo == null ? void 0 : confirmedTo.getSeconds()) != null ? _f : 0
496
+ ) : day;
297
497
  if (day < anchorDate) {
298
- from = day;
299
- to = anchorDate;
498
+ const tmp = from;
499
+ from = to;
500
+ to = tmp;
300
501
  }
301
502
  if (!isControlled) {
302
503
  setInternalFrom(from);
@@ -305,12 +506,25 @@ function DateRangePicker({
305
506
  setInputValue(formatRange(from, to));
306
507
  setInputInvalid(false);
307
508
  onChange == null ? void 0 : onChange({ from, to });
308
- close();
509
+ if (!showTime) close();
510
+ else setAnchorDate(void 0);
309
511
  }
310
512
  }
311
513
  function handleDayMouseEnter(day) {
312
514
  if (anchorDate) setHoveredDate(day);
313
515
  }
516
+ function handleFromTimeChange(h, m, s) {
517
+ const base = confirmedFrom != null ? confirmedFrom : /* @__PURE__ */ new Date();
518
+ const newDate = new Date(base.getFullYear(), base.getMonth(), base.getDate(), h, m, s);
519
+ if (!isControlled) setInternalFrom(newDate);
520
+ onChange == null ? void 0 : onChange({ from: newDate, to: confirmedTo });
521
+ }
522
+ function handleToTimeChange(h, m, s) {
523
+ const base = confirmedTo != null ? confirmedTo : /* @__PURE__ */ new Date();
524
+ const newDate = new Date(base.getFullYear(), base.getMonth(), base.getDate(), h, m, s);
525
+ if (!isControlled) setInternalTo(newDate);
526
+ onChange == null ? void 0 : onChange({ from: confirmedFrom, to: newDate });
527
+ }
314
528
  function handleChange(e) {
315
529
  var _a;
316
530
  const input = e.target;
@@ -324,8 +538,8 @@ function DateRangePicker({
324
538
  setHoveredDate(void 0);
325
539
  const fromDigits = digits.slice(0, 8);
326
540
  const toDigits = digits.slice(8);
327
- const parsedFrom = fromDigits.length === 8 ? parseDate2(applyDateMask(fromDigits)) : void 0;
328
- const parsedTo = toDigits.length === 8 ? parseDate2(applyDateMask(toDigits)) : void 0;
541
+ const parsedFrom = fromDigits.length === 8 ? parseDate(applyDateMask(fromDigits)) : void 0;
542
+ const parsedTo = toDigits.length === 8 ? parseDate(applyDateMask(toDigits)) : void 0;
329
543
  const fromComplete = fromDigits.length === 8;
330
544
  const toComplete = toDigits.length === 8;
331
545
  setInputInvalid(fromComplete && !parsedFrom || toComplete && !parsedTo);
@@ -370,8 +584,8 @@ function DateRangePicker({
370
584
  setInputValue(masked);
371
585
  setAnchorDate(void 0);
372
586
  setHoveredDate(void 0);
373
- const parsedFrom = digits.length >= 8 ? parseDate2(applyDateMask(digits.slice(0, 8))) : void 0;
374
- const parsedTo = digits.length >= 16 ? parseDate2(applyDateMask(digits.slice(8, 16))) : void 0;
587
+ const parsedFrom = digits.length >= 8 ? parseDate(applyDateMask(digits.slice(0, 8))) : void 0;
588
+ const parsedTo = digits.length >= 16 ? parseDate(applyDateMask(digits.slice(8, 16))) : void 0;
375
589
  setInputInvalid(digits.length >= 8 && !parsedFrom || digits.length >= 16 && !parsedTo);
376
590
  if (!isControlled) {
377
591
  setInternalFrom(parsedFrom);
@@ -384,62 +598,115 @@ function DateRangePicker({
384
598
  });
385
599
  }
386
600
  const placeholder = label && !focused && !filled ? void 0 : "\u0434\u0434.\u043C\u043C.\u0433\u0433\u0433\u0433 \u2014 \u0434\u0434.\u043C\u043C.\u0433\u0433\u0433\u0433";
387
- return /* @__PURE__ */ jsxs2(
601
+ return /* @__PURE__ */ jsxs4(
388
602
  "div",
389
603
  {
390
604
  ref: containerRef,
391
- className: ["datepicker", "daterangepicker", className].filter(Boolean).join(" "),
605
+ className: ["datepicker", "daterangepicker", `datepicker--${size}`, className].filter(Boolean).join(" "),
392
606
  "data-focused": focused || open || void 0,
393
607
  "data-filled": filled || void 0,
394
608
  "data-failed": failed || inputInvalid || void 0,
395
609
  "data-disabled": disabled || void 0,
396
610
  children: [
397
- /* @__PURE__ */ jsxs2("div", { className: "datepicker__field", onClick: () => {
398
- var _a;
399
- return !disabled && ((_a = inputRef.current) == null ? void 0 : _a.focus());
400
- }, children: [
401
- label && /* @__PURE__ */ jsx3("span", { className: "datepicker__label", children: label }),
402
- /* @__PURE__ */ jsx3(
403
- "input",
404
- {
405
- ref: inputRef,
406
- type: "text",
407
- inputMode: "numeric",
408
- className: "datepicker__input",
409
- value: inputValue,
410
- placeholder,
411
- disabled,
412
- onChange: handleChange,
413
- onKeyDown: handleKeyDown,
414
- onPaste: handlePaste,
415
- onFocus: () => {
416
- setFocused(true);
417
- if (!disabled) setOpen(true);
418
- },
419
- onBlur: () => setFocused(false),
420
- "aria-label": label != null ? label : "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043F\u0435\u0440\u0438\u043E\u0434",
421
- "aria-expanded": open,
422
- "aria-haspopup": "dialog",
423
- "aria-invalid": inputInvalid || void 0
424
- }
425
- )
426
- ] }),
427
- open && /* @__PURE__ */ jsx3("div", { className: "datepicker__popover", role: "dialog", "aria-label": "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043F\u0435\u0440\u0438\u043E\u0434", children: /* @__PURE__ */ jsx3(
428
- Calendar,
611
+ /* @__PURE__ */ jsxs4(
612
+ "div",
429
613
  {
430
- mode: "range",
431
- selected: calendarSelected,
432
- onSelect: () => {
614
+ className: "datepicker__field",
615
+ "data-icon-start": resolvedIcon && iconPosition === "start" ? true : void 0,
616
+ "data-icon-end": resolvedIcon && iconPosition === "end" ? true : void 0,
617
+ onClick: () => {
618
+ var _a;
619
+ return !disabled && ((_a = inputRef.current) == null ? void 0 : _a.focus());
433
620
  },
434
- onDayClick: handleDayClick,
435
- onDayMouseEnter: handleDayMouseEnter,
436
- onDayMouseLeave: () => setHoveredDate(void 0),
437
- startMonth: fromConstraint,
438
- endMonth: toConstraint,
439
- numberOfMonths: 2,
440
- locale: ru2
621
+ children: [
622
+ resolvedIcon && iconPosition === "start" && /* @__PURE__ */ jsx5("span", { className: "datepicker__icon datepicker__icon--start", children: resolvedIcon }),
623
+ label && /* @__PURE__ */ jsx5("span", { className: "datepicker__label", children: label }),
624
+ /* @__PURE__ */ jsx5(
625
+ "input",
626
+ {
627
+ ref: inputRef,
628
+ type: "text",
629
+ inputMode: "numeric",
630
+ className: "datepicker__input",
631
+ value: inputValue,
632
+ placeholder,
633
+ disabled,
634
+ onChange: handleChange,
635
+ onKeyDown: handleKeyDown,
636
+ onPaste: handlePaste,
637
+ onFocus: () => {
638
+ setFocused(true);
639
+ if (!disabled) setOpen(true);
640
+ },
641
+ onBlur: () => setFocused(false),
642
+ "aria-label": label != null ? label : "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043F\u0435\u0440\u0438\u043E\u0434",
643
+ "aria-expanded": open,
644
+ "aria-haspopup": "dialog",
645
+ "aria-invalid": inputInvalid || void 0
646
+ }
647
+ ),
648
+ resolvedIcon && iconPosition === "end" && /* @__PURE__ */ jsx5("span", { className: "datepicker__icon datepicker__icon--end", children: resolvedIcon })
649
+ ]
650
+ }
651
+ ),
652
+ open && /* @__PURE__ */ jsx5(
653
+ "div",
654
+ {
655
+ className: [
656
+ "datepicker__popover",
657
+ `datepicker__popover--${size}`,
658
+ calendarLayout === "horizontal" && "datepicker__popover--horizontal",
659
+ showTime && "datepicker__popover--with-time"
660
+ ].filter(Boolean).join(" "),
661
+ role: "dialog",
662
+ "aria-label": "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043F\u0435\u0440\u0438\u043E\u0434",
663
+ children: showTime ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
664
+ /* @__PURE__ */ jsx5("div", { className: "datepicker__popover-body", children: /* @__PURE__ */ jsx5("div", { className: "datepicker__popover-calendar", children: /* @__PURE__ */ jsx5(
665
+ Calendar,
666
+ {
667
+ mode: "range",
668
+ selected: calendarSelected,
669
+ onSelect: () => {
670
+ },
671
+ onDayClick: handleDayClick,
672
+ onDayMouseEnter: handleDayMouseEnter,
673
+ onDayMouseLeave: () => setHoveredDate(void 0),
674
+ startMonth: fromConstraint,
675
+ endMonth: toConstraint,
676
+ numberOfMonths: 2,
677
+ locale: ru2
678
+ }
679
+ ) }) }),
680
+ /* @__PURE__ */ jsxs4("div", { className: "datepicker__time-row", children: [
681
+ /* @__PURE__ */ jsxs4("div", { className: "datepicker__time-col", children: [
682
+ /* @__PURE__ */ jsx5("span", { className: "datepicker__time-label", children: "\u041D\u0430\u0447\u0430\u043B\u043E" }),
683
+ /* @__PURE__ */ jsx5(TimePanel, { value: confirmedFrom, showSeconds, onChange: handleFromTimeChange })
684
+ ] }),
685
+ /* @__PURE__ */ jsx5("div", { className: "datepicker__time-separator" }),
686
+ /* @__PURE__ */ jsxs4("div", { className: "datepicker__time-col", children: [
687
+ /* @__PURE__ */ jsx5("span", { className: "datepicker__time-label", children: "\u041A\u043E\u043D\u0435\u0446" }),
688
+ /* @__PURE__ */ jsx5(TimePanel, { value: confirmedTo, showSeconds, onChange: handleToTimeChange })
689
+ ] })
690
+ ] }),
691
+ /* @__PURE__ */ jsx5("div", { className: "datepicker__popover-footer", children: /* @__PURE__ */ jsx5("button", { className: "datepicker__ok-btn", type: "button", onClick: close, children: "OK" }) })
692
+ ] }) : /* @__PURE__ */ jsx5(
693
+ Calendar,
694
+ {
695
+ mode: "range",
696
+ selected: calendarSelected,
697
+ onSelect: () => {
698
+ },
699
+ onDayClick: handleDayClick,
700
+ onDayMouseEnter: handleDayMouseEnter,
701
+ onDayMouseLeave: () => setHoveredDate(void 0),
702
+ startMonth: fromConstraint,
703
+ endMonth: toConstraint,
704
+ numberOfMonths: 2,
705
+ locale: ru2
706
+ }
707
+ )
441
708
  }
442
- ) })
709
+ )
443
710
  ]
444
711
  }
445
712
  );