@artemy-tech/datepicker 0.0.1

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/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # @artemy-tech/datepicker
2
+
3
+ React DatePicker с опциональной поддержкой react-hook-form. Построен на базе [react-day-picker v9](https://daypicker.dev/) (headless) и [date-fns v3](https://date-fns.org/).
4
+
5
+ ## Возможности
6
+
7
+ - Выбор одиночной даты и диапазона дат
8
+ - Контролируемый и неконтролируемый режимы
9
+ - Интеграция с react-hook-form (нулевые издержки если не используется)
10
+ - Стилизация через CSS-переменные (`--datepicker-*`)
11
+ - Полная поддержка TypeScript
12
+ - Доступность (через react-day-picker)
13
+
14
+ ## Установка
15
+
16
+ ```bash
17
+ npm install @artemy-tech/datepicker
18
+ ```
19
+
20
+ Для интеграции с react-hook-form:
21
+
22
+ ```bash
23
+ npm install @artemy-tech/datepicker react-hook-form
24
+ ```
25
+
26
+ ## Peer-зависимости
27
+
28
+ ```
29
+ react >= 17.0.0
30
+ react-dom >= 17.0.0
31
+ react-hook-form >= 7.0.0 # опционально
32
+ ```
33
+
34
+ ## Использование
35
+
36
+ ### DatePicker
37
+
38
+ ```tsx
39
+ import { DatePicker } from '@artemy-tech/datepicker'
40
+ import '@artemy-tech/datepicker/styles'
41
+
42
+ // Неконтролируемый
43
+ <DatePicker />
44
+
45
+ // Контролируемый
46
+ const [date, setDate] = useState<Date>()
47
+ <DatePicker value={date} onChange={setDate} />
48
+ ```
49
+
50
+ ### DateRangePicker
51
+
52
+ ```tsx
53
+ import { DateRangePicker } from '@artemy-tech/datepicker'
54
+
55
+ const [range, setRange] = useState<{ from: Date; to?: Date }>()
56
+ <DateRangePicker value={range} onChange={setRange} />
57
+ ```
58
+
59
+ ### С react-hook-form
60
+
61
+ ```tsx
62
+ import { useForm } from 'react-hook-form'
63
+ import { RHFDatePicker } from '@artemy-tech/datepicker/rhf'
64
+
65
+ const { control } = useForm()
66
+
67
+ <RHFDatePicker name="date" control={control} />
68
+ ```
69
+
70
+ ## Стилизация
71
+
72
+ Подключите базовые стили и переопределите через CSS-переменные:
73
+
74
+ ```css
75
+ @import '@artemy-tech/datepicker/styles';
76
+
77
+ :root {
78
+ --datepicker-color-accent: #6366f1;
79
+ --datepicker-radius: 8px;
80
+ --datepicker-font-size: 14px;
81
+ }
82
+ ```
83
+
84
+ ## Лицензия
85
+
86
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,486 @@
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/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ Calendar: () => Calendar,
24
+ DatePicker: () => DatePicker,
25
+ DateRangePicker: () => DateRangePicker,
26
+ VERSION: () => VERSION
27
+ });
28
+ module.exports = __toCommonJS(src_exports);
29
+
30
+ // src/components/Calendar/Calendar.tsx
31
+ var import_react_day_picker = require("react-day-picker");
32
+ var import_jsx_runtime = require("react/jsx-runtime");
33
+ function Calendar({ className, ...props }) {
34
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
35
+ import_react_day_picker.DayPicker,
36
+ {
37
+ className: ["datepicker-calendar", className].filter(Boolean).join(" "),
38
+ ...props
39
+ }
40
+ );
41
+ }
42
+
43
+ // src/components/DatePicker/DatePicker.tsx
44
+ var import_react2 = require("react");
45
+ var import_date_fns = require("date-fns");
46
+ var import_locale = require("date-fns/locale");
47
+
48
+ // src/hooks/useClickOutside.ts
49
+ var import_react = require("react");
50
+ function useClickOutside(ref, handler) {
51
+ (0, import_react.useEffect)(() => {
52
+ function listener(e) {
53
+ if (!ref.current || ref.current.contains(e.target)) return;
54
+ handler();
55
+ }
56
+ document.addEventListener("mousedown", listener);
57
+ return () => document.removeEventListener("mousedown", listener);
58
+ }, [ref, handler]);
59
+ }
60
+
61
+ // src/components/DatePicker/DatePicker.tsx
62
+ var import_jsx_runtime2 = require("react/jsx-runtime");
63
+ var DATE_FORMAT = "dd.MM.yyyy";
64
+ function applyMask(digits) {
65
+ const d = digits.slice(0, 8);
66
+ let result = "";
67
+ for (let i = 0; i < d.length; i++) {
68
+ if (i === 2 || i === 4) result += ".";
69
+ result += d[i];
70
+ }
71
+ return result;
72
+ }
73
+ function getCursorPos(masked, digitCount) {
74
+ if (digitCount === 0) return 0;
75
+ let count = 0;
76
+ for (let i = 0; i < masked.length; i++) {
77
+ if (/\d/.test(masked[i])) {
78
+ count++;
79
+ if (count === digitCount) return i + 1;
80
+ }
81
+ }
82
+ return masked.length;
83
+ }
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;
88
+ }
89
+ function DatePicker({
90
+ value,
91
+ defaultValue,
92
+ onChange,
93
+ label,
94
+ placeholder = "\u0434\u0434.\u043C\u043C.\u0433\u0433\u0433\u0433",
95
+ fromDate,
96
+ toDate,
97
+ disabled = false,
98
+ failed = false,
99
+ className
100
+ }) {
101
+ 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) : ""
107
+ );
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);
111
+ const selected = isControlled ? value : internalDate;
112
+ const filled = inputValue.length > 0;
113
+ const close = (0, import_react2.useCallback)(() => setOpen(false), []);
114
+ useClickOutside(containerRef, close);
115
+ function commit(masked) {
116
+ const digits = masked.replace(/\D/g, "");
117
+ if (digits.length === 0) {
118
+ setInputInvalid(false);
119
+ if (!isControlled) setInternalDate(void 0);
120
+ onChange == null ? void 0 : onChange(void 0);
121
+ } else if (digits.length === 8) {
122
+ const date = parseDate(masked);
123
+ setInputInvalid(!date);
124
+ if (!isControlled) setInternalDate(date);
125
+ onChange == null ? void 0 : onChange(date);
126
+ } else {
127
+ setInputInvalid(false);
128
+ }
129
+ }
130
+ function handleChange(e) {
131
+ var _a;
132
+ const input = e.target;
133
+ const cursorPos = (_a = input.selectionStart) != null ? _a : 0;
134
+ const raw = input.value;
135
+ const digits = raw.replace(/\D/g, "").slice(0, 8);
136
+ const masked = applyMask(digits);
137
+ const digitsBeforeCursor = raw.slice(0, cursorPos).replace(/\D/g, "").length;
138
+ const newCursorPos = getCursorPos(masked, digitsBeforeCursor);
139
+ setInputValue(masked);
140
+ commit(masked);
141
+ requestAnimationFrame(() => {
142
+ var _a2;
143
+ return (_a2 = inputRef.current) == null ? void 0 : _a2.setSelectionRange(newCursorPos, newCursorPos);
144
+ });
145
+ }
146
+ function handleKeyDown(e) {
147
+ var _a;
148
+ const input = e.currentTarget;
149
+ const pos = (_a = input.selectionStart) != null ? _a : 0;
150
+ if (e.key.length === 1 && !/\d/.test(e.key) && !e.ctrlKey && !e.metaKey) {
151
+ e.preventDefault();
152
+ return;
153
+ }
154
+ if (e.key === "Backspace" && pos > 0 && input.value[pos - 1] === ".") {
155
+ e.preventDefault();
156
+ const val = input.value;
157
+ const masked = applyMask((val.slice(0, pos - 2) + val.slice(pos)).replace(/\D/g, ""));
158
+ setInputValue(masked);
159
+ commit(masked);
160
+ requestAnimationFrame(() => input.setSelectionRange(pos - 2, pos - 2));
161
+ }
162
+ }
163
+ function handlePaste(e) {
164
+ e.preventDefault();
165
+ const masked = applyMask(e.clipboardData.getData("text").replace(/\D/g, ""));
166
+ setInputValue(masked);
167
+ commit(masked);
168
+ requestAnimationFrame(() => {
169
+ var _a;
170
+ return (_a = inputRef.current) == null ? void 0 : _a.setSelectionRange(masked.length, masked.length);
171
+ });
172
+ }
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);
179
+ }
180
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
181
+ "div",
182
+ {
183
+ ref: containerRef,
184
+ className: ["datepicker", className].filter(Boolean).join(" "),
185
+ "data-focused": focused || open || void 0,
186
+ "data-filled": filled || void 0,
187
+ "data-failed": failed || inputInvalid || void 0,
188
+ "data-disabled": disabled || void 0,
189
+ 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,
222
+ {
223
+ mode: "single",
224
+ selected,
225
+ onSelect: handleSelect,
226
+ startMonth: fromDate,
227
+ endMonth: toDate,
228
+ locale: import_locale.ru
229
+ }
230
+ ) })
231
+ ]
232
+ }
233
+ );
234
+ }
235
+
236
+ // src/components/DateRangePicker/DateRangePicker.tsx
237
+ var import_react3 = require("react");
238
+ var import_date_fns2 = require("date-fns");
239
+ var import_locale2 = require("date-fns/locale");
240
+ var import_jsx_runtime3 = require("react/jsx-runtime");
241
+ var DATE_FORMAT2 = "dd.MM.yyyy";
242
+ function applyDateMask(digits) {
243
+ const d = digits.slice(0, 8);
244
+ let result = "";
245
+ for (let i = 0; i < d.length; i++) {
246
+ if (i === 2 || i === 4) result += ".";
247
+ result += d[i];
248
+ }
249
+ return result;
250
+ }
251
+ function applyRangeMask(digits) {
252
+ const all = digits.slice(0, 16);
253
+ const fromMasked = applyDateMask(all.slice(0, 8));
254
+ const toDigits = all.slice(8);
255
+ if (toDigits.length === 0) return fromMasked;
256
+ return `${fromMasked} \u2014 ${applyDateMask(toDigits)}`;
257
+ }
258
+ function getRangeCursorPos(masked, digitCount) {
259
+ if (digitCount === 0) return 0;
260
+ let count = 0;
261
+ for (let i = 0; i < masked.length; i++) {
262
+ if (/\d/.test(masked[i])) {
263
+ count++;
264
+ if (count === digitCount) return i + 1;
265
+ }
266
+ }
267
+ return masked.length;
268
+ }
269
+ function parseDate2(masked) {
270
+ if (masked.replace(/\D/g, "").length !== 8) return void 0;
271
+ const date = (0, import_date_fns2.parse)(masked, DATE_FORMAT2, /* @__PURE__ */ new Date());
272
+ return (0, import_date_fns2.isValid)(date) && (0, import_date_fns2.format)(date, DATE_FORMAT2) === masked ? date : void 0;
273
+ }
274
+ function formatRange(from, to) {
275
+ if (!from) return "";
276
+ const fromStr = (0, import_date_fns2.format)(from, DATE_FORMAT2);
277
+ if (!to) return fromStr;
278
+ return `${fromStr} \u2014 ${(0, import_date_fns2.format)(to, DATE_FORMAT2)}`;
279
+ }
280
+ function DateRangePicker({
281
+ value,
282
+ defaultValue,
283
+ onChange,
284
+ label,
285
+ fromDate: fromConstraint,
286
+ toDate: toConstraint,
287
+ disabled = false,
288
+ failed = false,
289
+ className
290
+ }) {
291
+ 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)(
295
+ () => formatRange(defaultValue == null ? void 0 : defaultValue.from, defaultValue == null ? void 0 : defaultValue.to)
296
+ );
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);
304
+ const confirmedFrom = isControlled ? value == null ? void 0 : value.from : internalFrom;
305
+ const confirmedTo = isControlled ? value == null ? void 0 : value.to : internalTo;
306
+ const filled = inputValue.length > 0;
307
+ const close = (0, import_react3.useCallback)(() => {
308
+ setOpen(false);
309
+ setAnchorDate(void 0);
310
+ setHoveredDate(void 0);
311
+ }, []);
312
+ useClickOutside(containerRef, close);
313
+ const calendarSelected = anchorDate ? hoveredDate ? anchorDate <= hoveredDate ? { from: anchorDate, to: hoveredDate } : { from: hoveredDate, to: anchorDate } : { from: anchorDate, to: void 0 } : { from: confirmedFrom, to: confirmedTo };
314
+ function handleDayClick(day) {
315
+ if (!anchorDate) {
316
+ setAnchorDate(day);
317
+ if (!isControlled) {
318
+ setInternalFrom(day);
319
+ setInternalTo(void 0);
320
+ }
321
+ setInputValue((0, import_date_fns2.format)(day, DATE_FORMAT2));
322
+ setInputInvalid(false);
323
+ onChange == null ? void 0 : onChange(day ? { from: day, to: void 0 } : void 0);
324
+ } else {
325
+ let from = anchorDate, to = day;
326
+ if (day < anchorDate) {
327
+ from = day;
328
+ to = anchorDate;
329
+ }
330
+ if (!isControlled) {
331
+ setInternalFrom(from);
332
+ setInternalTo(to);
333
+ }
334
+ setInputValue(formatRange(from, to));
335
+ setInputInvalid(false);
336
+ onChange == null ? void 0 : onChange({ from, to });
337
+ close();
338
+ }
339
+ }
340
+ function handleDayMouseEnter(day) {
341
+ if (anchorDate) setHoveredDate(day);
342
+ }
343
+ function handleChange(e) {
344
+ var _a;
345
+ const input = e.target;
346
+ const cursorPos = (_a = input.selectionStart) != null ? _a : 0;
347
+ const raw = input.value;
348
+ const digits = raw.replace(/\D/g, "").slice(0, 16);
349
+ const masked = applyRangeMask(digits);
350
+ const digitsBeforeCursor = raw.slice(0, cursorPos).replace(/\D/g, "").length;
351
+ setInputValue(masked);
352
+ setAnchorDate(void 0);
353
+ setHoveredDate(void 0);
354
+ const fromDigits = digits.slice(0, 8);
355
+ 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;
358
+ const fromComplete = fromDigits.length === 8;
359
+ const toComplete = toDigits.length === 8;
360
+ setInputInvalid(fromComplete && !parsedFrom || toComplete && !parsedTo);
361
+ if (!isControlled) {
362
+ setInternalFrom(parsedFrom);
363
+ setInternalTo(parsedTo);
364
+ }
365
+ onChange == null ? void 0 : onChange(parsedFrom || parsedTo ? { from: parsedFrom, to: parsedTo } : void 0);
366
+ requestAnimationFrame(
367
+ () => {
368
+ var _a2;
369
+ return (_a2 = inputRef.current) == null ? void 0 : _a2.setSelectionRange(
370
+ getRangeCursorPos(masked, digitsBeforeCursor),
371
+ getRangeCursorPos(masked, digitsBeforeCursor)
372
+ );
373
+ }
374
+ );
375
+ }
376
+ function handleKeyDown(e) {
377
+ var _a, _b, _c;
378
+ const input = e.currentTarget;
379
+ const pos = (_a = input.selectionStart) != null ? _a : 0;
380
+ if (e.key.length === 1 && !/\d/.test(e.key) && !e.ctrlKey && !e.metaKey) {
381
+ e.preventDefault();
382
+ return;
383
+ }
384
+ if (e.key === "Backspace" && pos > 0 && /[\s—]/.test(input.value[pos - 1])) {
385
+ e.preventDefault();
386
+ const val = input.value;
387
+ const charsToSkip = (_c = (_b = val.slice(0, pos).match(/[\s—]+$/)) == null ? void 0 : _b[0].length) != null ? _c : 1;
388
+ const newPos = pos - charsToSkip;
389
+ const masked = applyRangeMask((val.slice(0, newPos - 1) + val.slice(newPos)).replace(/\D/g, ""));
390
+ setInputValue(masked);
391
+ requestAnimationFrame(() => input.setSelectionRange(newPos - 1, newPos - 1));
392
+ }
393
+ }
394
+ function handlePaste(e) {
395
+ e.preventDefault();
396
+ const text = e.clipboardData.getData("text");
397
+ const digits = text.replace(/\D/g, "").slice(0, 16);
398
+ const masked = applyRangeMask(digits);
399
+ setInputValue(masked);
400
+ setAnchorDate(void 0);
401
+ 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;
404
+ setInputInvalid(digits.length >= 8 && !parsedFrom || digits.length >= 16 && !parsedTo);
405
+ if (!isControlled) {
406
+ setInternalFrom(parsedFrom);
407
+ setInternalTo(parsedTo);
408
+ }
409
+ onChange == null ? void 0 : onChange(parsedFrom || parsedTo ? { from: parsedFrom, to: parsedTo } : void 0);
410
+ requestAnimationFrame(() => {
411
+ var _a;
412
+ return (_a = inputRef.current) == null ? void 0 : _a.setSelectionRange(masked.length, masked.length);
413
+ });
414
+ }
415
+ 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)(
417
+ "div",
418
+ {
419
+ ref: containerRef,
420
+ className: ["datepicker", "daterangepicker", className].filter(Boolean).join(" "),
421
+ "data-focused": focused || open || void 0,
422
+ "data-filled": filled || void 0,
423
+ "data-failed": failed || inputInvalid || void 0,
424
+ "data-disabled": disabled || void 0,
425
+ 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,
458
+ {
459
+ mode: "range",
460
+ selected: calendarSelected,
461
+ onSelect: () => {
462
+ },
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
470
+ }
471
+ ) })
472
+ ]
473
+ }
474
+ );
475
+ }
476
+
477
+ // src/index.ts
478
+ var VERSION = "0.0.1";
479
+ // Annotate the CommonJS export names for ESM import in node:
480
+ 0 && (module.exports = {
481
+ Calendar,
482
+ DatePicker,
483
+ DateRangePicker,
484
+ VERSION
485
+ });
486
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/components/Calendar/Calendar.tsx","../src/components/DatePicker/DatePicker.tsx","../src/hooks/useClickOutside.ts","../src/components/DateRangePicker/DateRangePicker.tsx"],"sourcesContent":["export { Calendar } from './components/Calendar'\nexport type { CalendarProps } from './components/Calendar'\n\nexport { DatePicker } from './components/DatePicker'\nexport type { DatePickerProps } from './components/DatePicker'\n\nexport { DateRangePicker } from './components/DateRangePicker'\nexport type { DateRangePickerProps, DateRange } from './components/DateRangePicker'\n\nexport const VERSION = '0.0.1'\n","import { DayPicker, DayPickerProps } from 'react-day-picker'\n\nexport type CalendarProps = DayPickerProps & {\n className?: string\n}\n\nexport function Calendar({ className, ...props }: CalendarProps) {\n return (\n <DayPicker\n className={['datepicker-calendar', className].filter(Boolean).join(' ')}\n {...props}\n />\n )\n}\n","import { useCallback, useRef, useState } from 'react'\nimport { format, isValid, parse } from 'date-fns'\nimport { ru } from 'date-fns/locale'\nimport { useClickOutside } from '../../hooks/useClickOutside'\nimport { Calendar } from '../Calendar'\n\nconst DATE_FORMAT = 'dd.MM.yyyy'\n\nfunction applyMask(digits: string): string {\n const d = digits.slice(0, 8)\n let result = ''\n for (let i = 0; i < d.length; i++) {\n if (i === 2 || i === 4) result += '.'\n result += d[i]\n }\n return result\n}\n\nfunction getCursorPos(masked: string, digitCount: number): number {\n if (digitCount === 0) return 0\n let count = 0\n for (let i = 0; i < masked.length; i++) {\n if (/\\d/.test(masked[i])) {\n count++\n if (count === digitCount) return i + 1\n }\n }\n return masked.length\n}\n\nfunction parseDate(masked: string): Date | undefined {\n if (masked.replace(/\\D/g, '').length !== 8) return undefined\n const date = parse(masked, DATE_FORMAT, new Date())\n // round-trip check prevents day-overflow (e.g. 32.01 → Jan 32 → Feb 1)\n return isValid(date) && format(date, DATE_FORMAT) === masked ? date : undefined\n}\n\nexport interface DatePickerProps {\n value?: Date\n defaultValue?: Date\n onChange?: (date: Date | undefined) => void\n label?: string\n placeholder?: string\n fromDate?: Date\n toDate?: Date\n disabled?: boolean\n failed?: boolean\n className?: string\n}\n\nexport function DatePicker({\n value,\n defaultValue,\n onChange,\n label,\n placeholder = 'дд.мм.гггг',\n fromDate,\n toDate,\n disabled = false,\n failed = false,\n className,\n}: DatePickerProps) {\n const isControlled = value !== undefined\n const [internalDate, setInternalDate] = useState<Date | undefined>(defaultValue)\n const [open, setOpen] = useState(false)\n const [focused, setFocused] = useState(false)\n const [inputValue, setInputValue] = useState(() =>\n defaultValue && isValid(defaultValue) ? format(defaultValue, DATE_FORMAT) : '',\n )\n const [inputInvalid, setInputInvalid] = useState(false)\n\n const inputRef = useRef<HTMLInputElement>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n const selected = isControlled ? value : internalDate\n const filled = inputValue.length > 0\n\n const close = useCallback(() => setOpen(false), [])\n useClickOutside(containerRef, close)\n\n function commit(masked: string) {\n const digits = masked.replace(/\\D/g, '')\n if (digits.length === 0) {\n setInputInvalid(false)\n if (!isControlled) setInternalDate(undefined)\n onChange?.(undefined)\n } else if (digits.length === 8) {\n const date = parseDate(masked)\n setInputInvalid(!date)\n if (!isControlled) setInternalDate(date)\n onChange?.(date)\n } else {\n setInputInvalid(false)\n }\n }\n\n function handleChange(e: React.ChangeEvent<HTMLInputElement>) {\n const input = e.target\n const cursorPos = input.selectionStart ?? 0\n const raw = input.value\n const digits = raw.replace(/\\D/g, '').slice(0, 8)\n const masked = applyMask(digits)\n const digitsBeforeCursor = raw.slice(0, cursorPos).replace(/\\D/g, '').length\n const newCursorPos = getCursorPos(masked, digitsBeforeCursor)\n setInputValue(masked)\n commit(masked)\n requestAnimationFrame(() => inputRef.current?.setSelectionRange(newCursorPos, newCursorPos))\n }\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n const input = e.currentTarget\n const pos = input.selectionStart ?? 0\n\n if (e.key.length === 1 && !/\\d/.test(e.key) && !e.ctrlKey && !e.metaKey) {\n e.preventDefault()\n return\n }\n\n if (e.key === 'Backspace' && pos > 0 && input.value[pos - 1] === '.') {\n e.preventDefault()\n const val = input.value\n const masked = applyMask((val.slice(0, pos - 2) + val.slice(pos)).replace(/\\D/g, ''))\n setInputValue(masked)\n commit(masked)\n requestAnimationFrame(() => input.setSelectionRange(pos - 2, pos - 2))\n }\n }\n\n function handlePaste(e: React.ClipboardEvent<HTMLInputElement>) {\n e.preventDefault()\n const masked = applyMask(e.clipboardData.getData('text').replace(/\\D/g, ''))\n setInputValue(masked)\n commit(masked)\n requestAnimationFrame(() => inputRef.current?.setSelectionRange(masked.length, masked.length))\n }\n\n function handleSelect(date: Date | undefined) {\n if (!isControlled) setInternalDate(date)\n setInputValue(date && isValid(date) ? format(date, DATE_FORMAT) : '')\n setInputInvalid(false)\n onChange?.(date)\n setOpen(false)\n }\n\n return (\n <div\n ref={containerRef}\n className={['datepicker', className].filter(Boolean).join(' ')}\n data-focused={focused || open || undefined}\n data-filled={filled || undefined}\n data-failed={failed || inputInvalid || undefined}\n data-disabled={disabled || undefined}\n >\n <div className=\"datepicker__field\" onClick={() => !disabled && inputRef.current?.focus()}>\n {label && <span className=\"datepicker__label\">{label}</span>}\n <input\n ref={inputRef}\n type=\"text\"\n inputMode=\"numeric\"\n className=\"datepicker__input\"\n value={inputValue}\n placeholder={label && !focused ? undefined : placeholder}\n disabled={disabled}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n onFocus={() => { setFocused(true); if (!disabled) setOpen(true) }}\n onBlur={() => setFocused(false)}\n aria-label={label ?? 'Выберите дату'}\n aria-expanded={open}\n aria-haspopup=\"dialog\"\n aria-invalid={inputInvalid || undefined}\n />\n </div>\n {open && (\n <div className=\"datepicker__popover\" role=\"dialog\" aria-label=\"Календарь\">\n <Calendar\n mode=\"single\"\n selected={selected}\n onSelect={handleSelect}\n startMonth={fromDate}\n endMonth={toDate}\n locale={ru}\n />\n </div>\n )}\n </div>\n )\n}\n","import { RefObject, useEffect } from 'react'\n\nexport function useClickOutside(ref: RefObject<HTMLElement | null>, handler: () => void) {\n useEffect(() => {\n function listener(e: MouseEvent) {\n if (!ref.current || ref.current.contains(e.target as Node)) return\n handler()\n }\n document.addEventListener('mousedown', listener)\n return () => document.removeEventListener('mousedown', listener)\n }, [ref, handler])\n}\n","import { useCallback, useRef, useState } from 'react'\nimport { format, isValid, parse } from 'date-fns'\nimport { ru } from 'date-fns/locale'\nimport type { DateRange } from 'react-day-picker'\nimport { useClickOutside } from '../../hooks/useClickOutside'\nimport { Calendar } from '../Calendar'\n\nconst DATE_FORMAT = 'dd.MM.yyyy'\n\nfunction applyDateMask(digits: string): string {\n const d = digits.slice(0, 8)\n let result = ''\n for (let i = 0; i < d.length; i++) {\n if (i === 2 || i === 4) result += '.'\n result += d[i]\n }\n return result\n}\n\nfunction applyRangeMask(digits: string): string {\n const all = digits.slice(0, 16)\n const fromMasked = applyDateMask(all.slice(0, 8))\n const toDigits = all.slice(8)\n if (toDigits.length === 0) return fromMasked\n return `${fromMasked} — ${applyDateMask(toDigits)}`\n}\n\nfunction getRangeCursorPos(masked: string, digitCount: number): number {\n if (digitCount === 0) return 0\n let count = 0\n for (let i = 0; i < masked.length; i++) {\n if (/\\d/.test(masked[i])) {\n count++\n if (count === digitCount) return i + 1\n }\n }\n return masked.length\n}\n\nfunction parseDate(masked: string): Date | undefined {\n if (masked.replace(/\\D/g, '').length !== 8) return undefined\n const date = parse(masked, DATE_FORMAT, new Date())\n return isValid(date) && format(date, DATE_FORMAT) === masked ? date : undefined\n}\n\nfunction formatRange(from: Date | undefined, to: Date | undefined): string {\n if (!from) return ''\n const fromStr = format(from, DATE_FORMAT)\n if (!to) return fromStr\n return `${fromStr} — ${format(to, DATE_FORMAT)}`\n}\n\nexport type { DateRange }\n\nexport interface DateRangePickerProps {\n value?: DateRange\n defaultValue?: DateRange\n onChange?: (range: DateRange | undefined) => void\n label?: string\n fromDate?: Date\n toDate?: Date\n disabled?: boolean\n failed?: boolean\n className?: string\n}\n\nexport function DateRangePicker({\n value,\n defaultValue,\n onChange,\n label,\n fromDate: fromConstraint,\n toDate: toConstraint,\n disabled = false,\n failed = false,\n className,\n}: DateRangePickerProps) {\n const isControlled = value !== undefined\n\n const [internalFrom, setInternalFrom] = useState<Date | undefined>(defaultValue?.from)\n const [internalTo, setInternalTo] = useState<Date | undefined>(defaultValue?.to)\n const [inputValue, setInputValue] = useState(() =>\n formatRange(defaultValue?.from, defaultValue?.to),\n )\n const [inputInvalid, setInputInvalid] = useState(false)\n const [open, setOpen] = useState(false)\n const [focused, setFocused] = useState(false)\n\n // Calendar picking state — managed separately from confirmed dates\n const [anchorDate, setAnchorDate] = useState<Date | undefined>(undefined)\n const [hoveredDate, setHoveredDate] = useState<Date | undefined>(undefined)\n\n const inputRef = useRef<HTMLInputElement>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n\n const confirmedFrom = isControlled ? value?.from : internalFrom\n const confirmedTo = isControlled ? value?.to : internalTo\n const filled = inputValue.length > 0\n\n const close = useCallback(() => {\n setOpen(false)\n setAnchorDate(undefined)\n setHoveredDate(undefined)\n }, [])\n useClickOutside(containerRef, close)\n\n // Visual range shown in calendar\n const calendarSelected: DateRange | undefined = anchorDate\n ? hoveredDate\n ? anchorDate <= hoveredDate\n ? { from: anchorDate, to: hoveredDate }\n : { from: hoveredDate, to: anchorDate }\n : { from: anchorDate, to: undefined }\n : { from: confirmedFrom, to: confirmedTo }\n\n // ── Calendar handlers ─────────────────────────────────────────\n\n // All selection logic lives here. onSelect is a no-op so calendar display is fully controlled.\n function handleDayClick(day: Date) {\n if (!anchorDate) {\n // Phase 1: pick the \"from\" anchor\n setAnchorDate(day)\n if (!isControlled) { setInternalFrom(day); setInternalTo(undefined) }\n setInputValue(format(day, DATE_FORMAT))\n setInputInvalid(false)\n onChange?.(day ? { from: day, to: undefined } : undefined)\n } else {\n // Phase 2: pick \"to\" and close\n let from = anchorDate, to = day\n if (day < anchorDate) { from = day; to = anchorDate }\n if (!isControlled) { setInternalFrom(from); setInternalTo(to) }\n setInputValue(formatRange(from, to))\n setInputInvalid(false)\n onChange?.({ from, to })\n close()\n }\n }\n\n function handleDayMouseEnter(day: Date) {\n if (anchorDate) setHoveredDate(day)\n }\n\n // ── Text input handlers ───────────────────────────────────────\n\n function handleChange(e: React.ChangeEvent<HTMLInputElement>) {\n const input = e.target\n const cursorPos = input.selectionStart ?? 0\n const raw = input.value\n const digits = raw.replace(/\\D/g, '').slice(0, 16)\n const masked = applyRangeMask(digits)\n const digitsBeforeCursor = raw.slice(0, cursorPos).replace(/\\D/g, '').length\n\n setInputValue(masked)\n setAnchorDate(undefined)\n setHoveredDate(undefined)\n\n const fromDigits = digits.slice(0, 8)\n const toDigits = digits.slice(8)\n const parsedFrom = fromDigits.length === 8 ? parseDate(applyDateMask(fromDigits)) : undefined\n const parsedTo = toDigits.length === 8 ? parseDate(applyDateMask(toDigits)) : undefined\n const fromComplete = fromDigits.length === 8\n const toComplete = toDigits.length === 8\n setInputInvalid((fromComplete && !parsedFrom) || (toComplete && !parsedTo))\n\n if (!isControlled) { setInternalFrom(parsedFrom); setInternalTo(parsedTo) }\n onChange?.(parsedFrom || parsedTo ? { from: parsedFrom, to: parsedTo } : undefined)\n\n requestAnimationFrame(() =>\n inputRef.current?.setSelectionRange(\n getRangeCursorPos(masked, digitsBeforeCursor),\n getRangeCursorPos(masked, digitsBeforeCursor),\n ),\n )\n }\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n const input = e.currentTarget\n const pos = input.selectionStart ?? 0\n\n if (e.key.length === 1 && !/\\d/.test(e.key) && !e.ctrlKey && !e.metaKey) {\n e.preventDefault()\n return\n }\n if (e.key === 'Backspace' && pos > 0 && /[\\s—]/.test(input.value[pos - 1])) {\n // skip over separator characters\n e.preventDefault()\n const val = input.value\n const charsToSkip = val.slice(0, pos).match(/[\\s—]+$/)?.[0].length ?? 1\n const newPos = pos - charsToSkip\n const masked = applyRangeMask((val.slice(0, newPos - 1) + val.slice(newPos)).replace(/\\D/g, ''))\n setInputValue(masked)\n requestAnimationFrame(() => input.setSelectionRange(newPos - 1, newPos - 1))\n }\n }\n\n function handlePaste(e: React.ClipboardEvent<HTMLInputElement>) {\n e.preventDefault()\n const text = e.clipboardData.getData('text')\n const digits = text.replace(/\\D/g, '').slice(0, 16)\n const masked = applyRangeMask(digits)\n setInputValue(masked)\n setAnchorDate(undefined)\n setHoveredDate(undefined)\n\n const parsedFrom = digits.length >= 8 ? parseDate(applyDateMask(digits.slice(0, 8))) : undefined\n const parsedTo = digits.length >= 16 ? parseDate(applyDateMask(digits.slice(8, 16))) : undefined\n setInputInvalid((digits.length >= 8 && !parsedFrom) || (digits.length >= 16 && !parsedTo))\n if (!isControlled) { setInternalFrom(parsedFrom); setInternalTo(parsedTo) }\n onChange?.(parsedFrom || parsedTo ? { from: parsedFrom, to: parsedTo } : undefined)\n\n requestAnimationFrame(() => inputRef.current?.setSelectionRange(masked.length, masked.length))\n }\n\n const placeholder = label && !focused && !filled ? undefined : 'дд.мм.гггг — дд.мм.гггг'\n\n return (\n <div\n ref={containerRef}\n className={['datepicker', 'daterangepicker', className].filter(Boolean).join(' ')}\n data-focused={focused || open || undefined}\n data-filled={filled || undefined}\n data-failed={failed || inputInvalid || undefined}\n data-disabled={disabled || undefined}\n >\n <div className=\"datepicker__field\" onClick={() => !disabled && inputRef.current?.focus()}>\n {label && <span className=\"datepicker__label\">{label}</span>}\n <input\n ref={inputRef}\n type=\"text\"\n inputMode=\"numeric\"\n className=\"datepicker__input\"\n value={inputValue}\n placeholder={placeholder}\n disabled={disabled}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n onFocus={() => { setFocused(true); if (!disabled) setOpen(true) }}\n onBlur={() => setFocused(false)}\n aria-label={label ?? 'Выберите период'}\n aria-expanded={open}\n aria-haspopup=\"dialog\"\n aria-invalid={inputInvalid || undefined}\n />\n </div>\n {open && (\n <div className=\"datepicker__popover\" role=\"dialog\" aria-label=\"Выберите период\">\n <Calendar\n mode=\"range\"\n selected={calendarSelected}\n onSelect={() => {}}\n onDayClick={handleDayClick}\n onDayMouseEnter={handleDayMouseEnter}\n onDayMouseLeave={() => setHoveredDate(undefined)}\n startMonth={fromConstraint}\n endMonth={toConstraint}\n numberOfMonths={2}\n locale={ru}\n />\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","_a","import_react","import_date_fns","import_locale","import_jsx_runtime","DATE_FORMAT","parseDate","_a"]}
@@ -0,0 +1,39 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { DayPickerProps, DateRange } from 'react-day-picker';
3
+ export { DateRange } from 'react-day-picker';
4
+
5
+ type CalendarProps = DayPickerProps & {
6
+ className?: string;
7
+ };
8
+ declare function Calendar({ className, ...props }: CalendarProps): react_jsx_runtime.JSX.Element;
9
+
10
+ interface DatePickerProps {
11
+ value?: Date;
12
+ defaultValue?: Date;
13
+ onChange?: (date: Date | undefined) => void;
14
+ label?: string;
15
+ placeholder?: string;
16
+ fromDate?: Date;
17
+ toDate?: Date;
18
+ disabled?: boolean;
19
+ failed?: boolean;
20
+ className?: string;
21
+ }
22
+ declare function DatePicker({ value, defaultValue, onChange, label, placeholder, fromDate, toDate, disabled, failed, className, }: DatePickerProps): react_jsx_runtime.JSX.Element;
23
+
24
+ interface DateRangePickerProps {
25
+ value?: DateRange;
26
+ defaultValue?: DateRange;
27
+ onChange?: (range: DateRange | undefined) => void;
28
+ label?: string;
29
+ fromDate?: Date;
30
+ toDate?: Date;
31
+ disabled?: boolean;
32
+ failed?: boolean;
33
+ className?: string;
34
+ }
35
+ declare function DateRangePicker({ value, defaultValue, onChange, label, fromDate: fromConstraint, toDate: toConstraint, disabled, failed, className, }: DateRangePickerProps): react_jsx_runtime.JSX.Element;
36
+
37
+ declare const VERSION = "0.0.1";
38
+
39
+ export { Calendar, type CalendarProps, DatePicker, type DatePickerProps, DateRangePicker, type DateRangePickerProps, VERSION };
@@ -0,0 +1,39 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { DayPickerProps, DateRange } from 'react-day-picker';
3
+ export { DateRange } from 'react-day-picker';
4
+
5
+ type CalendarProps = DayPickerProps & {
6
+ className?: string;
7
+ };
8
+ declare function Calendar({ className, ...props }: CalendarProps): react_jsx_runtime.JSX.Element;
9
+
10
+ interface DatePickerProps {
11
+ value?: Date;
12
+ defaultValue?: Date;
13
+ onChange?: (date: Date | undefined) => void;
14
+ label?: string;
15
+ placeholder?: string;
16
+ fromDate?: Date;
17
+ toDate?: Date;
18
+ disabled?: boolean;
19
+ failed?: boolean;
20
+ className?: string;
21
+ }
22
+ declare function DatePicker({ value, defaultValue, onChange, label, placeholder, fromDate, toDate, disabled, failed, className, }: DatePickerProps): react_jsx_runtime.JSX.Element;
23
+
24
+ interface DateRangePickerProps {
25
+ value?: DateRange;
26
+ defaultValue?: DateRange;
27
+ onChange?: (range: DateRange | undefined) => void;
28
+ label?: string;
29
+ fromDate?: Date;
30
+ toDate?: Date;
31
+ disabled?: boolean;
32
+ failed?: boolean;
33
+ className?: string;
34
+ }
35
+ declare function DateRangePicker({ value, defaultValue, onChange, label, fromDate: fromConstraint, toDate: toConstraint, disabled, failed, className, }: DateRangePickerProps): react_jsx_runtime.JSX.Element;
36
+
37
+ declare const VERSION = "0.0.1";
38
+
39
+ export { Calendar, type CalendarProps, DatePicker, type DatePickerProps, DateRangePicker, type DateRangePickerProps, VERSION };
package/dist/index.js ADDED
@@ -0,0 +1,456 @@
1
+ // src/components/Calendar/Calendar.tsx
2
+ import { DayPicker } from "react-day-picker";
3
+ import { jsx } from "react/jsx-runtime";
4
+ function Calendar({ className, ...props }) {
5
+ return /* @__PURE__ */ jsx(
6
+ DayPicker,
7
+ {
8
+ className: ["datepicker-calendar", className].filter(Boolean).join(" "),
9
+ ...props
10
+ }
11
+ );
12
+ }
13
+
14
+ // src/components/DatePicker/DatePicker.tsx
15
+ import { useCallback, useRef, useState } from "react";
16
+ import { format, isValid, parse } from "date-fns";
17
+ import { ru } from "date-fns/locale";
18
+
19
+ // src/hooks/useClickOutside.ts
20
+ import { useEffect } from "react";
21
+ function useClickOutside(ref, handler) {
22
+ useEffect(() => {
23
+ function listener(e) {
24
+ if (!ref.current || ref.current.contains(e.target)) return;
25
+ handler();
26
+ }
27
+ document.addEventListener("mousedown", listener);
28
+ return () => document.removeEventListener("mousedown", listener);
29
+ }, [ref, handler]);
30
+ }
31
+
32
+ // src/components/DatePicker/DatePicker.tsx
33
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
34
+ var DATE_FORMAT = "dd.MM.yyyy";
35
+ function applyMask(digits) {
36
+ const d = digits.slice(0, 8);
37
+ let result = "";
38
+ for (let i = 0; i < d.length; i++) {
39
+ if (i === 2 || i === 4) result += ".";
40
+ result += d[i];
41
+ }
42
+ return result;
43
+ }
44
+ function getCursorPos(masked, digitCount) {
45
+ if (digitCount === 0) return 0;
46
+ let count = 0;
47
+ for (let i = 0; i < masked.length; i++) {
48
+ if (/\d/.test(masked[i])) {
49
+ count++;
50
+ if (count === digitCount) return i + 1;
51
+ }
52
+ }
53
+ return masked.length;
54
+ }
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;
59
+ }
60
+ function DatePicker({
61
+ value,
62
+ defaultValue,
63
+ onChange,
64
+ label,
65
+ placeholder = "\u0434\u0434.\u043C\u043C.\u0433\u0433\u0433\u0433",
66
+ fromDate,
67
+ toDate,
68
+ disabled = false,
69
+ failed = false,
70
+ className
71
+ }) {
72
+ const isControlled = value !== void 0;
73
+ const [internalDate, setInternalDate] = useState(defaultValue);
74
+ const [open, setOpen] = useState(false);
75
+ const [focused, setFocused] = useState(false);
76
+ const [inputValue, setInputValue] = useState(
77
+ () => defaultValue && isValid(defaultValue) ? format(defaultValue, DATE_FORMAT) : ""
78
+ );
79
+ const [inputInvalid, setInputInvalid] = useState(false);
80
+ const inputRef = useRef(null);
81
+ const containerRef = useRef(null);
82
+ const selected = isControlled ? value : internalDate;
83
+ const filled = inputValue.length > 0;
84
+ const close = useCallback(() => setOpen(false), []);
85
+ useClickOutside(containerRef, close);
86
+ function commit(masked) {
87
+ const digits = masked.replace(/\D/g, "");
88
+ if (digits.length === 0) {
89
+ setInputInvalid(false);
90
+ if (!isControlled) setInternalDate(void 0);
91
+ onChange == null ? void 0 : onChange(void 0);
92
+ } else if (digits.length === 8) {
93
+ const date = parseDate(masked);
94
+ setInputInvalid(!date);
95
+ if (!isControlled) setInternalDate(date);
96
+ onChange == null ? void 0 : onChange(date);
97
+ } else {
98
+ setInputInvalid(false);
99
+ }
100
+ }
101
+ function handleChange(e) {
102
+ var _a;
103
+ const input = e.target;
104
+ const cursorPos = (_a = input.selectionStart) != null ? _a : 0;
105
+ const raw = input.value;
106
+ const digits = raw.replace(/\D/g, "").slice(0, 8);
107
+ const masked = applyMask(digits);
108
+ const digitsBeforeCursor = raw.slice(0, cursorPos).replace(/\D/g, "").length;
109
+ const newCursorPos = getCursorPos(masked, digitsBeforeCursor);
110
+ setInputValue(masked);
111
+ commit(masked);
112
+ requestAnimationFrame(() => {
113
+ var _a2;
114
+ return (_a2 = inputRef.current) == null ? void 0 : _a2.setSelectionRange(newCursorPos, newCursorPos);
115
+ });
116
+ }
117
+ function handleKeyDown(e) {
118
+ var _a;
119
+ const input = e.currentTarget;
120
+ const pos = (_a = input.selectionStart) != null ? _a : 0;
121
+ if (e.key.length === 1 && !/\d/.test(e.key) && !e.ctrlKey && !e.metaKey) {
122
+ e.preventDefault();
123
+ return;
124
+ }
125
+ if (e.key === "Backspace" && pos > 0 && input.value[pos - 1] === ".") {
126
+ e.preventDefault();
127
+ const val = input.value;
128
+ const masked = applyMask((val.slice(0, pos - 2) + val.slice(pos)).replace(/\D/g, ""));
129
+ setInputValue(masked);
130
+ commit(masked);
131
+ requestAnimationFrame(() => input.setSelectionRange(pos - 2, pos - 2));
132
+ }
133
+ }
134
+ function handlePaste(e) {
135
+ e.preventDefault();
136
+ const masked = applyMask(e.clipboardData.getData("text").replace(/\D/g, ""));
137
+ setInputValue(masked);
138
+ commit(masked);
139
+ requestAnimationFrame(() => {
140
+ var _a;
141
+ return (_a = inputRef.current) == null ? void 0 : _a.setSelectionRange(masked.length, masked.length);
142
+ });
143
+ }
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);
150
+ }
151
+ return /* @__PURE__ */ jsxs(
152
+ "div",
153
+ {
154
+ ref: containerRef,
155
+ className: ["datepicker", className].filter(Boolean).join(" "),
156
+ "data-focused": focused || open || void 0,
157
+ "data-filled": filled || void 0,
158
+ "data-failed": failed || inputInvalid || void 0,
159
+ "data-disabled": disabled || void 0,
160
+ 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,
193
+ {
194
+ mode: "single",
195
+ selected,
196
+ onSelect: handleSelect,
197
+ startMonth: fromDate,
198
+ endMonth: toDate,
199
+ locale: ru
200
+ }
201
+ ) })
202
+ ]
203
+ }
204
+ );
205
+ }
206
+
207
+ // src/components/DateRangePicker/DateRangePicker.tsx
208
+ import { useCallback as useCallback2, useRef as useRef2, useState as useState2 } from "react";
209
+ import { format as format2, isValid as isValid2, parse as parse2 } from "date-fns";
210
+ import { ru as ru2 } from "date-fns/locale";
211
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
212
+ var DATE_FORMAT2 = "dd.MM.yyyy";
213
+ function applyDateMask(digits) {
214
+ const d = digits.slice(0, 8);
215
+ let result = "";
216
+ for (let i = 0; i < d.length; i++) {
217
+ if (i === 2 || i === 4) result += ".";
218
+ result += d[i];
219
+ }
220
+ return result;
221
+ }
222
+ function applyRangeMask(digits) {
223
+ const all = digits.slice(0, 16);
224
+ const fromMasked = applyDateMask(all.slice(0, 8));
225
+ const toDigits = all.slice(8);
226
+ if (toDigits.length === 0) return fromMasked;
227
+ return `${fromMasked} \u2014 ${applyDateMask(toDigits)}`;
228
+ }
229
+ function getRangeCursorPos(masked, digitCount) {
230
+ if (digitCount === 0) return 0;
231
+ let count = 0;
232
+ for (let i = 0; i < masked.length; i++) {
233
+ if (/\d/.test(masked[i])) {
234
+ count++;
235
+ if (count === digitCount) return i + 1;
236
+ }
237
+ }
238
+ return masked.length;
239
+ }
240
+ function parseDate2(masked) {
241
+ if (masked.replace(/\D/g, "").length !== 8) return void 0;
242
+ const date = parse2(masked, DATE_FORMAT2, /* @__PURE__ */ new Date());
243
+ return isValid2(date) && format2(date, DATE_FORMAT2) === masked ? date : void 0;
244
+ }
245
+ function formatRange(from, to) {
246
+ if (!from) return "";
247
+ const fromStr = format2(from, DATE_FORMAT2);
248
+ if (!to) return fromStr;
249
+ return `${fromStr} \u2014 ${format2(to, DATE_FORMAT2)}`;
250
+ }
251
+ function DateRangePicker({
252
+ value,
253
+ defaultValue,
254
+ onChange,
255
+ label,
256
+ fromDate: fromConstraint,
257
+ toDate: toConstraint,
258
+ disabled = false,
259
+ failed = false,
260
+ className
261
+ }) {
262
+ const isControlled = value !== void 0;
263
+ const [internalFrom, setInternalFrom] = useState2(defaultValue == null ? void 0 : defaultValue.from);
264
+ const [internalTo, setInternalTo] = useState2(defaultValue == null ? void 0 : defaultValue.to);
265
+ const [inputValue, setInputValue] = useState2(
266
+ () => formatRange(defaultValue == null ? void 0 : defaultValue.from, defaultValue == null ? void 0 : defaultValue.to)
267
+ );
268
+ const [inputInvalid, setInputInvalid] = useState2(false);
269
+ const [open, setOpen] = useState2(false);
270
+ const [focused, setFocused] = useState2(false);
271
+ const [anchorDate, setAnchorDate] = useState2(void 0);
272
+ const [hoveredDate, setHoveredDate] = useState2(void 0);
273
+ const inputRef = useRef2(null);
274
+ const containerRef = useRef2(null);
275
+ const confirmedFrom = isControlled ? value == null ? void 0 : value.from : internalFrom;
276
+ const confirmedTo = isControlled ? value == null ? void 0 : value.to : internalTo;
277
+ const filled = inputValue.length > 0;
278
+ const close = useCallback2(() => {
279
+ setOpen(false);
280
+ setAnchorDate(void 0);
281
+ setHoveredDate(void 0);
282
+ }, []);
283
+ useClickOutside(containerRef, close);
284
+ const calendarSelected = anchorDate ? hoveredDate ? anchorDate <= hoveredDate ? { from: anchorDate, to: hoveredDate } : { from: hoveredDate, to: anchorDate } : { from: anchorDate, to: void 0 } : { from: confirmedFrom, to: confirmedTo };
285
+ function handleDayClick(day) {
286
+ if (!anchorDate) {
287
+ setAnchorDate(day);
288
+ if (!isControlled) {
289
+ setInternalFrom(day);
290
+ setInternalTo(void 0);
291
+ }
292
+ setInputValue(format2(day, DATE_FORMAT2));
293
+ setInputInvalid(false);
294
+ onChange == null ? void 0 : onChange(day ? { from: day, to: void 0 } : void 0);
295
+ } else {
296
+ let from = anchorDate, to = day;
297
+ if (day < anchorDate) {
298
+ from = day;
299
+ to = anchorDate;
300
+ }
301
+ if (!isControlled) {
302
+ setInternalFrom(from);
303
+ setInternalTo(to);
304
+ }
305
+ setInputValue(formatRange(from, to));
306
+ setInputInvalid(false);
307
+ onChange == null ? void 0 : onChange({ from, to });
308
+ close();
309
+ }
310
+ }
311
+ function handleDayMouseEnter(day) {
312
+ if (anchorDate) setHoveredDate(day);
313
+ }
314
+ function handleChange(e) {
315
+ var _a;
316
+ const input = e.target;
317
+ const cursorPos = (_a = input.selectionStart) != null ? _a : 0;
318
+ const raw = input.value;
319
+ const digits = raw.replace(/\D/g, "").slice(0, 16);
320
+ const masked = applyRangeMask(digits);
321
+ const digitsBeforeCursor = raw.slice(0, cursorPos).replace(/\D/g, "").length;
322
+ setInputValue(masked);
323
+ setAnchorDate(void 0);
324
+ setHoveredDate(void 0);
325
+ const fromDigits = digits.slice(0, 8);
326
+ 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;
329
+ const fromComplete = fromDigits.length === 8;
330
+ const toComplete = toDigits.length === 8;
331
+ setInputInvalid(fromComplete && !parsedFrom || toComplete && !parsedTo);
332
+ if (!isControlled) {
333
+ setInternalFrom(parsedFrom);
334
+ setInternalTo(parsedTo);
335
+ }
336
+ onChange == null ? void 0 : onChange(parsedFrom || parsedTo ? { from: parsedFrom, to: parsedTo } : void 0);
337
+ requestAnimationFrame(
338
+ () => {
339
+ var _a2;
340
+ return (_a2 = inputRef.current) == null ? void 0 : _a2.setSelectionRange(
341
+ getRangeCursorPos(masked, digitsBeforeCursor),
342
+ getRangeCursorPos(masked, digitsBeforeCursor)
343
+ );
344
+ }
345
+ );
346
+ }
347
+ function handleKeyDown(e) {
348
+ var _a, _b, _c;
349
+ const input = e.currentTarget;
350
+ const pos = (_a = input.selectionStart) != null ? _a : 0;
351
+ if (e.key.length === 1 && !/\d/.test(e.key) && !e.ctrlKey && !e.metaKey) {
352
+ e.preventDefault();
353
+ return;
354
+ }
355
+ if (e.key === "Backspace" && pos > 0 && /[\s—]/.test(input.value[pos - 1])) {
356
+ e.preventDefault();
357
+ const val = input.value;
358
+ const charsToSkip = (_c = (_b = val.slice(0, pos).match(/[\s—]+$/)) == null ? void 0 : _b[0].length) != null ? _c : 1;
359
+ const newPos = pos - charsToSkip;
360
+ const masked = applyRangeMask((val.slice(0, newPos - 1) + val.slice(newPos)).replace(/\D/g, ""));
361
+ setInputValue(masked);
362
+ requestAnimationFrame(() => input.setSelectionRange(newPos - 1, newPos - 1));
363
+ }
364
+ }
365
+ function handlePaste(e) {
366
+ e.preventDefault();
367
+ const text = e.clipboardData.getData("text");
368
+ const digits = text.replace(/\D/g, "").slice(0, 16);
369
+ const masked = applyRangeMask(digits);
370
+ setInputValue(masked);
371
+ setAnchorDate(void 0);
372
+ 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;
375
+ setInputInvalid(digits.length >= 8 && !parsedFrom || digits.length >= 16 && !parsedTo);
376
+ if (!isControlled) {
377
+ setInternalFrom(parsedFrom);
378
+ setInternalTo(parsedTo);
379
+ }
380
+ onChange == null ? void 0 : onChange(parsedFrom || parsedTo ? { from: parsedFrom, to: parsedTo } : void 0);
381
+ requestAnimationFrame(() => {
382
+ var _a;
383
+ return (_a = inputRef.current) == null ? void 0 : _a.setSelectionRange(masked.length, masked.length);
384
+ });
385
+ }
386
+ 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(
388
+ "div",
389
+ {
390
+ ref: containerRef,
391
+ className: ["datepicker", "daterangepicker", className].filter(Boolean).join(" "),
392
+ "data-focused": focused || open || void 0,
393
+ "data-filled": filled || void 0,
394
+ "data-failed": failed || inputInvalid || void 0,
395
+ "data-disabled": disabled || void 0,
396
+ 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,
429
+ {
430
+ mode: "range",
431
+ selected: calendarSelected,
432
+ onSelect: () => {
433
+ },
434
+ onDayClick: handleDayClick,
435
+ onDayMouseEnter: handleDayMouseEnter,
436
+ onDayMouseLeave: () => setHoveredDate(void 0),
437
+ startMonth: fromConstraint,
438
+ endMonth: toConstraint,
439
+ numberOfMonths: 2,
440
+ locale: ru2
441
+ }
442
+ ) })
443
+ ]
444
+ }
445
+ );
446
+ }
447
+
448
+ // src/index.ts
449
+ var VERSION = "0.0.1";
450
+ export {
451
+ Calendar,
452
+ DatePicker,
453
+ DateRangePicker,
454
+ VERSION
455
+ };
456
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Calendar/Calendar.tsx","../src/components/DatePicker/DatePicker.tsx","../src/hooks/useClickOutside.ts","../src/components/DateRangePicker/DateRangePicker.tsx","../src/index.ts"],"sourcesContent":["import { DayPicker, DayPickerProps } from 'react-day-picker'\n\nexport type CalendarProps = DayPickerProps & {\n className?: string\n}\n\nexport function Calendar({ className, ...props }: CalendarProps) {\n return (\n <DayPicker\n className={['datepicker-calendar', className].filter(Boolean).join(' ')}\n {...props}\n />\n )\n}\n","import { useCallback, useRef, useState } from 'react'\nimport { format, isValid, parse } from 'date-fns'\nimport { ru } from 'date-fns/locale'\nimport { useClickOutside } from '../../hooks/useClickOutside'\nimport { Calendar } from '../Calendar'\n\nconst DATE_FORMAT = 'dd.MM.yyyy'\n\nfunction applyMask(digits: string): string {\n const d = digits.slice(0, 8)\n let result = ''\n for (let i = 0; i < d.length; i++) {\n if (i === 2 || i === 4) result += '.'\n result += d[i]\n }\n return result\n}\n\nfunction getCursorPos(masked: string, digitCount: number): number {\n if (digitCount === 0) return 0\n let count = 0\n for (let i = 0; i < masked.length; i++) {\n if (/\\d/.test(masked[i])) {\n count++\n if (count === digitCount) return i + 1\n }\n }\n return masked.length\n}\n\nfunction parseDate(masked: string): Date | undefined {\n if (masked.replace(/\\D/g, '').length !== 8) return undefined\n const date = parse(masked, DATE_FORMAT, new Date())\n // round-trip check prevents day-overflow (e.g. 32.01 → Jan 32 → Feb 1)\n return isValid(date) && format(date, DATE_FORMAT) === masked ? date : undefined\n}\n\nexport interface DatePickerProps {\n value?: Date\n defaultValue?: Date\n onChange?: (date: Date | undefined) => void\n label?: string\n placeholder?: string\n fromDate?: Date\n toDate?: Date\n disabled?: boolean\n failed?: boolean\n className?: string\n}\n\nexport function DatePicker({\n value,\n defaultValue,\n onChange,\n label,\n placeholder = 'дд.мм.гггг',\n fromDate,\n toDate,\n disabled = false,\n failed = false,\n className,\n}: DatePickerProps) {\n const isControlled = value !== undefined\n const [internalDate, setInternalDate] = useState<Date | undefined>(defaultValue)\n const [open, setOpen] = useState(false)\n const [focused, setFocused] = useState(false)\n const [inputValue, setInputValue] = useState(() =>\n defaultValue && isValid(defaultValue) ? format(defaultValue, DATE_FORMAT) : '',\n )\n const [inputInvalid, setInputInvalid] = useState(false)\n\n const inputRef = useRef<HTMLInputElement>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n const selected = isControlled ? value : internalDate\n const filled = inputValue.length > 0\n\n const close = useCallback(() => setOpen(false), [])\n useClickOutside(containerRef, close)\n\n function commit(masked: string) {\n const digits = masked.replace(/\\D/g, '')\n if (digits.length === 0) {\n setInputInvalid(false)\n if (!isControlled) setInternalDate(undefined)\n onChange?.(undefined)\n } else if (digits.length === 8) {\n const date = parseDate(masked)\n setInputInvalid(!date)\n if (!isControlled) setInternalDate(date)\n onChange?.(date)\n } else {\n setInputInvalid(false)\n }\n }\n\n function handleChange(e: React.ChangeEvent<HTMLInputElement>) {\n const input = e.target\n const cursorPos = input.selectionStart ?? 0\n const raw = input.value\n const digits = raw.replace(/\\D/g, '').slice(0, 8)\n const masked = applyMask(digits)\n const digitsBeforeCursor = raw.slice(0, cursorPos).replace(/\\D/g, '').length\n const newCursorPos = getCursorPos(masked, digitsBeforeCursor)\n setInputValue(masked)\n commit(masked)\n requestAnimationFrame(() => inputRef.current?.setSelectionRange(newCursorPos, newCursorPos))\n }\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n const input = e.currentTarget\n const pos = input.selectionStart ?? 0\n\n if (e.key.length === 1 && !/\\d/.test(e.key) && !e.ctrlKey && !e.metaKey) {\n e.preventDefault()\n return\n }\n\n if (e.key === 'Backspace' && pos > 0 && input.value[pos - 1] === '.') {\n e.preventDefault()\n const val = input.value\n const masked = applyMask((val.slice(0, pos - 2) + val.slice(pos)).replace(/\\D/g, ''))\n setInputValue(masked)\n commit(masked)\n requestAnimationFrame(() => input.setSelectionRange(pos - 2, pos - 2))\n }\n }\n\n function handlePaste(e: React.ClipboardEvent<HTMLInputElement>) {\n e.preventDefault()\n const masked = applyMask(e.clipboardData.getData('text').replace(/\\D/g, ''))\n setInputValue(masked)\n commit(masked)\n requestAnimationFrame(() => inputRef.current?.setSelectionRange(masked.length, masked.length))\n }\n\n function handleSelect(date: Date | undefined) {\n if (!isControlled) setInternalDate(date)\n setInputValue(date && isValid(date) ? format(date, DATE_FORMAT) : '')\n setInputInvalid(false)\n onChange?.(date)\n setOpen(false)\n }\n\n return (\n <div\n ref={containerRef}\n className={['datepicker', className].filter(Boolean).join(' ')}\n data-focused={focused || open || undefined}\n data-filled={filled || undefined}\n data-failed={failed || inputInvalid || undefined}\n data-disabled={disabled || undefined}\n >\n <div className=\"datepicker__field\" onClick={() => !disabled && inputRef.current?.focus()}>\n {label && <span className=\"datepicker__label\">{label}</span>}\n <input\n ref={inputRef}\n type=\"text\"\n inputMode=\"numeric\"\n className=\"datepicker__input\"\n value={inputValue}\n placeholder={label && !focused ? undefined : placeholder}\n disabled={disabled}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n onFocus={() => { setFocused(true); if (!disabled) setOpen(true) }}\n onBlur={() => setFocused(false)}\n aria-label={label ?? 'Выберите дату'}\n aria-expanded={open}\n aria-haspopup=\"dialog\"\n aria-invalid={inputInvalid || undefined}\n />\n </div>\n {open && (\n <div className=\"datepicker__popover\" role=\"dialog\" aria-label=\"Календарь\">\n <Calendar\n mode=\"single\"\n selected={selected}\n onSelect={handleSelect}\n startMonth={fromDate}\n endMonth={toDate}\n locale={ru}\n />\n </div>\n )}\n </div>\n )\n}\n","import { RefObject, useEffect } from 'react'\n\nexport function useClickOutside(ref: RefObject<HTMLElement | null>, handler: () => void) {\n useEffect(() => {\n function listener(e: MouseEvent) {\n if (!ref.current || ref.current.contains(e.target as Node)) return\n handler()\n }\n document.addEventListener('mousedown', listener)\n return () => document.removeEventListener('mousedown', listener)\n }, [ref, handler])\n}\n","import { useCallback, useRef, useState } from 'react'\nimport { format, isValid, parse } from 'date-fns'\nimport { ru } from 'date-fns/locale'\nimport type { DateRange } from 'react-day-picker'\nimport { useClickOutside } from '../../hooks/useClickOutside'\nimport { Calendar } from '../Calendar'\n\nconst DATE_FORMAT = 'dd.MM.yyyy'\n\nfunction applyDateMask(digits: string): string {\n const d = digits.slice(0, 8)\n let result = ''\n for (let i = 0; i < d.length; i++) {\n if (i === 2 || i === 4) result += '.'\n result += d[i]\n }\n return result\n}\n\nfunction applyRangeMask(digits: string): string {\n const all = digits.slice(0, 16)\n const fromMasked = applyDateMask(all.slice(0, 8))\n const toDigits = all.slice(8)\n if (toDigits.length === 0) return fromMasked\n return `${fromMasked} — ${applyDateMask(toDigits)}`\n}\n\nfunction getRangeCursorPos(masked: string, digitCount: number): number {\n if (digitCount === 0) return 0\n let count = 0\n for (let i = 0; i < masked.length; i++) {\n if (/\\d/.test(masked[i])) {\n count++\n if (count === digitCount) return i + 1\n }\n }\n return masked.length\n}\n\nfunction parseDate(masked: string): Date | undefined {\n if (masked.replace(/\\D/g, '').length !== 8) return undefined\n const date = parse(masked, DATE_FORMAT, new Date())\n return isValid(date) && format(date, DATE_FORMAT) === masked ? date : undefined\n}\n\nfunction formatRange(from: Date | undefined, to: Date | undefined): string {\n if (!from) return ''\n const fromStr = format(from, DATE_FORMAT)\n if (!to) return fromStr\n return `${fromStr} — ${format(to, DATE_FORMAT)}`\n}\n\nexport type { DateRange }\n\nexport interface DateRangePickerProps {\n value?: DateRange\n defaultValue?: DateRange\n onChange?: (range: DateRange | undefined) => void\n label?: string\n fromDate?: Date\n toDate?: Date\n disabled?: boolean\n failed?: boolean\n className?: string\n}\n\nexport function DateRangePicker({\n value,\n defaultValue,\n onChange,\n label,\n fromDate: fromConstraint,\n toDate: toConstraint,\n disabled = false,\n failed = false,\n className,\n}: DateRangePickerProps) {\n const isControlled = value !== undefined\n\n const [internalFrom, setInternalFrom] = useState<Date | undefined>(defaultValue?.from)\n const [internalTo, setInternalTo] = useState<Date | undefined>(defaultValue?.to)\n const [inputValue, setInputValue] = useState(() =>\n formatRange(defaultValue?.from, defaultValue?.to),\n )\n const [inputInvalid, setInputInvalid] = useState(false)\n const [open, setOpen] = useState(false)\n const [focused, setFocused] = useState(false)\n\n // Calendar picking state — managed separately from confirmed dates\n const [anchorDate, setAnchorDate] = useState<Date | undefined>(undefined)\n const [hoveredDate, setHoveredDate] = useState<Date | undefined>(undefined)\n\n const inputRef = useRef<HTMLInputElement>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n\n const confirmedFrom = isControlled ? value?.from : internalFrom\n const confirmedTo = isControlled ? value?.to : internalTo\n const filled = inputValue.length > 0\n\n const close = useCallback(() => {\n setOpen(false)\n setAnchorDate(undefined)\n setHoveredDate(undefined)\n }, [])\n useClickOutside(containerRef, close)\n\n // Visual range shown in calendar\n const calendarSelected: DateRange | undefined = anchorDate\n ? hoveredDate\n ? anchorDate <= hoveredDate\n ? { from: anchorDate, to: hoveredDate }\n : { from: hoveredDate, to: anchorDate }\n : { from: anchorDate, to: undefined }\n : { from: confirmedFrom, to: confirmedTo }\n\n // ── Calendar handlers ─────────────────────────────────────────\n\n // All selection logic lives here. onSelect is a no-op so calendar display is fully controlled.\n function handleDayClick(day: Date) {\n if (!anchorDate) {\n // Phase 1: pick the \"from\" anchor\n setAnchorDate(day)\n if (!isControlled) { setInternalFrom(day); setInternalTo(undefined) }\n setInputValue(format(day, DATE_FORMAT))\n setInputInvalid(false)\n onChange?.(day ? { from: day, to: undefined } : undefined)\n } else {\n // Phase 2: pick \"to\" and close\n let from = anchorDate, to = day\n if (day < anchorDate) { from = day; to = anchorDate }\n if (!isControlled) { setInternalFrom(from); setInternalTo(to) }\n setInputValue(formatRange(from, to))\n setInputInvalid(false)\n onChange?.({ from, to })\n close()\n }\n }\n\n function handleDayMouseEnter(day: Date) {\n if (anchorDate) setHoveredDate(day)\n }\n\n // ── Text input handlers ───────────────────────────────────────\n\n function handleChange(e: React.ChangeEvent<HTMLInputElement>) {\n const input = e.target\n const cursorPos = input.selectionStart ?? 0\n const raw = input.value\n const digits = raw.replace(/\\D/g, '').slice(0, 16)\n const masked = applyRangeMask(digits)\n const digitsBeforeCursor = raw.slice(0, cursorPos).replace(/\\D/g, '').length\n\n setInputValue(masked)\n setAnchorDate(undefined)\n setHoveredDate(undefined)\n\n const fromDigits = digits.slice(0, 8)\n const toDigits = digits.slice(8)\n const parsedFrom = fromDigits.length === 8 ? parseDate(applyDateMask(fromDigits)) : undefined\n const parsedTo = toDigits.length === 8 ? parseDate(applyDateMask(toDigits)) : undefined\n const fromComplete = fromDigits.length === 8\n const toComplete = toDigits.length === 8\n setInputInvalid((fromComplete && !parsedFrom) || (toComplete && !parsedTo))\n\n if (!isControlled) { setInternalFrom(parsedFrom); setInternalTo(parsedTo) }\n onChange?.(parsedFrom || parsedTo ? { from: parsedFrom, to: parsedTo } : undefined)\n\n requestAnimationFrame(() =>\n inputRef.current?.setSelectionRange(\n getRangeCursorPos(masked, digitsBeforeCursor),\n getRangeCursorPos(masked, digitsBeforeCursor),\n ),\n )\n }\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n const input = e.currentTarget\n const pos = input.selectionStart ?? 0\n\n if (e.key.length === 1 && !/\\d/.test(e.key) && !e.ctrlKey && !e.metaKey) {\n e.preventDefault()\n return\n }\n if (e.key === 'Backspace' && pos > 0 && /[\\s—]/.test(input.value[pos - 1])) {\n // skip over separator characters\n e.preventDefault()\n const val = input.value\n const charsToSkip = val.slice(0, pos).match(/[\\s—]+$/)?.[0].length ?? 1\n const newPos = pos - charsToSkip\n const masked = applyRangeMask((val.slice(0, newPos - 1) + val.slice(newPos)).replace(/\\D/g, ''))\n setInputValue(masked)\n requestAnimationFrame(() => input.setSelectionRange(newPos - 1, newPos - 1))\n }\n }\n\n function handlePaste(e: React.ClipboardEvent<HTMLInputElement>) {\n e.preventDefault()\n const text = e.clipboardData.getData('text')\n const digits = text.replace(/\\D/g, '').slice(0, 16)\n const masked = applyRangeMask(digits)\n setInputValue(masked)\n setAnchorDate(undefined)\n setHoveredDate(undefined)\n\n const parsedFrom = digits.length >= 8 ? parseDate(applyDateMask(digits.slice(0, 8))) : undefined\n const parsedTo = digits.length >= 16 ? parseDate(applyDateMask(digits.slice(8, 16))) : undefined\n setInputInvalid((digits.length >= 8 && !parsedFrom) || (digits.length >= 16 && !parsedTo))\n if (!isControlled) { setInternalFrom(parsedFrom); setInternalTo(parsedTo) }\n onChange?.(parsedFrom || parsedTo ? { from: parsedFrom, to: parsedTo } : undefined)\n\n requestAnimationFrame(() => inputRef.current?.setSelectionRange(masked.length, masked.length))\n }\n\n const placeholder = label && !focused && !filled ? undefined : 'дд.мм.гггг — дд.мм.гггг'\n\n return (\n <div\n ref={containerRef}\n className={['datepicker', 'daterangepicker', className].filter(Boolean).join(' ')}\n data-focused={focused || open || undefined}\n data-filled={filled || undefined}\n data-failed={failed || inputInvalid || undefined}\n data-disabled={disabled || undefined}\n >\n <div className=\"datepicker__field\" onClick={() => !disabled && inputRef.current?.focus()}>\n {label && <span className=\"datepicker__label\">{label}</span>}\n <input\n ref={inputRef}\n type=\"text\"\n inputMode=\"numeric\"\n className=\"datepicker__input\"\n value={inputValue}\n placeholder={placeholder}\n disabled={disabled}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n onFocus={() => { setFocused(true); if (!disabled) setOpen(true) }}\n onBlur={() => setFocused(false)}\n aria-label={label ?? 'Выберите период'}\n aria-expanded={open}\n aria-haspopup=\"dialog\"\n aria-invalid={inputInvalid || undefined}\n />\n </div>\n {open && (\n <div className=\"datepicker__popover\" role=\"dialog\" aria-label=\"Выберите период\">\n <Calendar\n mode=\"range\"\n selected={calendarSelected}\n onSelect={() => {}}\n onDayClick={handleDayClick}\n onDayMouseEnter={handleDayMouseEnter}\n onDayMouseLeave={() => setHoveredDate(undefined)}\n startMonth={fromConstraint}\n endMonth={toConstraint}\n numberOfMonths={2}\n locale={ru}\n />\n </div>\n )}\n </div>\n )\n}\n","export { Calendar } from './components/Calendar'\nexport type { CalendarProps } from './components/Calendar'\n\nexport { DatePicker } from './components/DatePicker'\nexport type { DatePickerProps } from './components/DatePicker'\n\nexport { DateRangePicker } from './components/DateRangePicker'\nexport type { DateRangePickerProps, DateRange } from './components/DateRangePicker'\n\nexport const VERSION = '0.0.1'\n"],"mappings":"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","names":["jsx","_a","useCallback","useRef","useState","format","isValid","parse","ru","jsx","jsxs","DATE_FORMAT","parseDate","parse","isValid","format","useState","useRef","useCallback","_a","ru"]}
package/dist/rhf.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=rhf.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/rhf.d.cts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/rhf.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/rhf.js ADDED
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=rhf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@artemy-tech/datepicker",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "React DatePicker with react-hook-form support",
6
+ "keywords": [
7
+ "react",
8
+ "datepicker",
9
+ "react-hook-form",
10
+ "date-range"
11
+ ],
12
+ "author": "Artemy",
13
+ "license": "MIT",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/твой-username/datepicker.git"
17
+ },
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js",
22
+ "require": "./dist/index.cjs"
23
+ },
24
+ "./rhf": {
25
+ "types": "./dist/rhf.d.ts",
26
+ "import": "./dist/rhf.js",
27
+ "require": "./dist/rhf.cjs"
28
+ },
29
+ "./styles": "./dist/styles/variables.css"
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "scripts": {
35
+ "build": "tsup",
36
+ "dev": "tsup --watch",
37
+ "storybook": "storybook dev -p 6006",
38
+ "build-storybook": "storybook build",
39
+ "test": "vitest",
40
+ "test:coverage": "vitest --coverage",
41
+ "changeset": "changeset",
42
+ "version": "changeset version",
43
+ "release": "tsup && changeset publish"
44
+ },
45
+ "dependencies": {
46
+ "react-day-picker": "^9.0.0",
47
+ "date-fns": "^3.0.0"
48
+ },
49
+ "peerDependencies": {
50
+ "react": ">=17.0.0",
51
+ "react-dom": ">=17.0.0",
52
+ "react-hook-form": ">=7.0.0"
53
+ },
54
+ "peerDependenciesMeta": {
55
+ "react-hook-form": {
56
+ "optional": true
57
+ }
58
+ },
59
+ "devDependencies": {
60
+ "@storybook/addon-essentials": "^8.0.0",
61
+ "@storybook/react-vite": "^8.0.0",
62
+ "@testing-library/react": "^16.0.0",
63
+ "@testing-library/user-event": "^14.0.0",
64
+ "@types/react": "^18.0.0",
65
+ "@types/react-dom": "^18.0.0",
66
+ "@vitejs/plugin-react": "^4.0.0",
67
+ "react": "^18.0.0",
68
+ "react-dom": "^18.0.0",
69
+ "react-hook-form": "^7.0.0",
70
+ "storybook": "^8.0.0",
71
+ "tsup": "^8.0.0",
72
+ "typescript": "^5.0.0",
73
+ "vite": "^5.0.0",
74
+ "vitest": "^2.0.0"
75
+ }
76
+ }