@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 +86 -0
- package/dist/index.cjs +486 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +39 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +456 -0
- package/dist/index.js.map +1 -0
- package/dist/rhf.cjs +2 -0
- package/dist/rhf.cjs.map +1 -0
- package/dist/rhf.d.cts +2 -0
- package/dist/rhf.d.ts +2 -0
- package/dist/rhf.js +1 -0
- package/dist/rhf.js.map +1 -0
- package/package.json +76 -0
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"]}
|
package/dist/index.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
package/dist/rhf.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/rhf.d.cts
ADDED
package/dist/rhf.d.ts
ADDED
package/dist/rhf.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=rhf.js.map
|
package/dist/rhf.js.map
ADDED
|
@@ -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
|
+
}
|