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