@alfalab/core-components-date-input 4.2.10 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Component.js CHANGED
@@ -17,7 +17,7 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
17
17
  var React__default = /*#__PURE__*/_interopDefaultCompat(React);
18
18
  var mergeRefs__default = /*#__PURE__*/_interopDefaultCompat(mergeRefs);
19
19
 
20
- var styles = {"nativeInput":"date-input__nativeInput_lury9"};
20
+ var styles = {"nativeInput":"date-input__nativeInput_w5y8m"};
21
21
  require('./index.css')
22
22
 
23
23
  var DateInput = React.forwardRef(function (_a, ref) {
@@ -20,8 +20,7 @@
20
20
  left: 0;
21
21
  width: 100%;
22
22
  height: 100%;
23
- -webkit-appearance: none;
24
- appearance: none;
23
+ appearance: none;
25
24
  z-index: 1
26
25
  } .nativeInput::-webkit-calendar-picker-indicator {
27
26
  display: none;
package/esm/Component.js CHANGED
@@ -8,7 +8,7 @@ import 'date-fns/format';
8
8
  import 'date-fns/isValid';
9
9
  import 'date-fns/parse';
10
10
 
11
- var styles = {"nativeInput":"date-input__nativeInput_lury9"};
11
+ var styles = {"nativeInput":"date-input__nativeInput_w5y8m"};
12
12
  require('./index.css')
13
13
 
14
14
  var DateInput = forwardRef(function (_a, ref) {
package/esm/index.css CHANGED
@@ -1,4 +1,4 @@
1
- /* hash: 1kf3w */
1
+ /* hash: 13mc4 */
2
2
  :root {
3
3
  } /* deprecated */ :root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
4
4
  } :root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
@@ -14,18 +14,17 @@
14
14
  } :root {
15
15
  } :root {
16
16
  } :root {
17
- } .date-input__nativeInput_lury9 {
17
+ } .date-input__nativeInput_w5y8m {
18
18
  opacity: 0;
19
19
  position: absolute;
20
20
  top: 0;
21
21
  left: 0;
22
22
  width: 100%;
23
23
  height: 100%;
24
- -webkit-appearance: none;
25
- appearance: none;
24
+ appearance: none;
26
25
  z-index: 1
27
- } .date-input__nativeInput_lury9::-webkit-calendar-picker-indicator {
26
+ } .date-input__nativeInput_w5y8m::-webkit-calendar-picker-indicator {
28
27
  display: none;
29
- } .date-input__nativeInput_lury9::-webkit-inner-spin-button {
28
+ } .date-input__nativeInput_w5y8m::-webkit-inner-spin-button {
30
29
  display: none;
31
30
  }
package/index.css CHANGED
@@ -1,4 +1,4 @@
1
- /* hash: 1kf3w */
1
+ /* hash: 13mc4 */
2
2
  :root {
3
3
  } /* deprecated */ :root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
4
4
  } :root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
@@ -14,18 +14,17 @@
14
14
  } :root {
15
15
  } :root {
16
16
  } :root {
17
- } .date-input__nativeInput_lury9 {
17
+ } .date-input__nativeInput_w5y8m {
18
18
  opacity: 0;
19
19
  position: absolute;
20
20
  top: 0;
21
21
  left: 0;
22
22
  width: 100%;
23
23
  height: 100%;
24
- -webkit-appearance: none;
25
- appearance: none;
24
+ appearance: none;
26
25
  z-index: 1
27
- } .date-input__nativeInput_lury9::-webkit-calendar-picker-indicator {
26
+ } .date-input__nativeInput_w5y8m::-webkit-calendar-picker-indicator {
28
27
  display: none;
29
- } .date-input__nativeInput_lury9::-webkit-inner-spin-button {
28
+ } .date-input__nativeInput_w5y8m::-webkit-inner-spin-button {
30
29
  display: none;
31
30
  }
@@ -7,7 +7,7 @@ import 'date-fns/format';
7
7
  import 'date-fns/isValid';
8
8
  import 'date-fns/parse';
9
9
 
10
- const styles = {"nativeInput":"date-input__nativeInput_lury9"};
10
+ const styles = {"nativeInput":"date-input__nativeInput_w5y8m"};
11
11
  require('./index.css')
12
12
 
13
13
  const DateInput = forwardRef(({ mobileMode = 'input', defaultValue = '', rightAddons, error, value: propValue, onBlur, onChange, onComplete, ...restProps }, ref) => {
package/modern/index.css CHANGED
@@ -1,4 +1,4 @@
1
- /* hash: 1kf3w */
1
+ /* hash: 13mc4 */
2
2
  :root {
3
3
  } /* deprecated */ :root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
4
4
  } :root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
@@ -14,18 +14,17 @@
14
14
  } :root {
15
15
  } :root {
16
16
  } :root {
17
- } .date-input__nativeInput_lury9 {
17
+ } .date-input__nativeInput_w5y8m {
18
18
  opacity: 0;
19
19
  position: absolute;
20
20
  top: 0;
21
21
  left: 0;
22
22
  width: 100%;
23
23
  height: 100%;
24
- -webkit-appearance: none;
25
- appearance: none;
24
+ appearance: none;
26
25
  z-index: 1
27
- } .date-input__nativeInput_lury9::-webkit-calendar-picker-indicator {
26
+ } .date-input__nativeInput_w5y8m::-webkit-calendar-picker-indicator {
28
27
  display: none;
29
- } .date-input__nativeInput_lury9::-webkit-inner-spin-button {
28
+ } .date-input__nativeInput_w5y8m::-webkit-inner-spin-button {
30
29
  display: none;
31
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfalab/core-components-date-input",
3
- "version": "4.2.10",
3
+ "version": "4.3.0",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "license": "MIT",
@@ -14,7 +14,7 @@
14
14
  "react": "^16.9.0 || ^17.0.1 || ^18.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@alfalab/core-components-input": "^11.1.18",
17
+ "@alfalab/core-components-input": "^12.0.0",
18
18
  "date-fns": "^2.16.1",
19
19
  "react-merge-refs": "^1.1.0",
20
20
  "tslib": "^2.4.0"
@@ -0,0 +1,167 @@
1
+ import React, {
2
+ ChangeEvent,
3
+ FocusEvent,
4
+ forwardRef,
5
+ useCallback,
6
+ useEffect,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
10
+ import mergeRefs from 'react-merge-refs';
11
+
12
+ import { Input, InputProps } from '@alfalab/core-components-input';
13
+
14
+ import {
15
+ format,
16
+ formatDate,
17
+ isCompleteDateInput,
18
+ isInputDateSupported,
19
+ isValid,
20
+ NATIVE_DATE_FORMAT,
21
+ parseDateString,
22
+ } from './utils';
23
+
24
+ import styles from './index.module.css';
25
+
26
+ export type DateInputProps = Omit<InputProps, 'onChange'> & {
27
+ /**
28
+ * Управление нативным режимом на мобильных устройствах
29
+ */
30
+ mobileMode?: 'input' | 'native';
31
+
32
+ /**
33
+ * Обработчик изменения значения
34
+ */
35
+ onChange?: (
36
+ event: ChangeEvent<HTMLInputElement>,
37
+ payload: { date: Date; value: string },
38
+ ) => void;
39
+
40
+ /**
41
+ * Обработчик окончания ввода
42
+ */
43
+ onComplete?: (
44
+ event: ChangeEvent<HTMLInputElement>,
45
+ payload: { date: Date; value: string },
46
+ ) => void;
47
+ };
48
+
49
+ export const DateInput = forwardRef<HTMLInputElement, DateInputProps>(
50
+ (
51
+ {
52
+ mobileMode = 'input',
53
+ defaultValue = '',
54
+ rightAddons,
55
+ error,
56
+ value: propValue,
57
+ onBlur,
58
+ onChange,
59
+ onComplete,
60
+ ...restProps
61
+ },
62
+ ref,
63
+ ) => {
64
+ const inputRef = useRef<HTMLInputElement>(null);
65
+
66
+ const [shouldRenderNative, setShouldRenderNative] = useState(false);
67
+
68
+ const [value, setValue] = useState(propValue || defaultValue);
69
+
70
+ const handleChange = useCallback(
71
+ (event: ChangeEvent<HTMLInputElement>) => {
72
+ const { value: newValue } = event.target;
73
+
74
+ // Позволяем вводить только цифры и точки
75
+ if (/[^\d.]/.test(newValue)) {
76
+ return;
77
+ }
78
+
79
+ const dots = newValue.match(/\./g);
80
+
81
+ // Не даем вводить больше, чем 2 точки
82
+ if (dots && dots.length > 2) {
83
+ return;
84
+ }
85
+
86
+ // Форматируем введенное значение (добавляем точки)
87
+ const formattedValue = format(newValue);
88
+ const date = parseDateString(formattedValue);
89
+
90
+ setValue(formattedValue);
91
+
92
+ if (onChange) onChange(event, { date, value: formattedValue });
93
+
94
+ if (isCompleteDateInput(formattedValue)) {
95
+ const valid = formattedValue.length > 0 && isValid(formattedValue);
96
+
97
+ if (!valid) return;
98
+
99
+ if (onComplete) onComplete(event, { date, value: formattedValue });
100
+ }
101
+ },
102
+ [onChange, onComplete],
103
+ );
104
+
105
+ const handleNativeInputChange = useCallback(
106
+ (event: ChangeEvent<HTMLInputElement>) => {
107
+ const newDate = parseDateString(event.target.value, NATIVE_DATE_FORMAT);
108
+ const newValue = event.target.value === '' ? '' : formatDate(newDate);
109
+
110
+ setValue(newValue);
111
+
112
+ if (onComplete) onComplete(event, { date: newDate, value: newValue });
113
+ if (onChange) onChange(event, { date: newDate, value: newValue });
114
+ },
115
+ [onComplete, onChange],
116
+ );
117
+
118
+ const handleBlur = useCallback(
119
+ (event: FocusEvent<HTMLInputElement>) => {
120
+ if (onBlur) onBlur(event);
121
+ },
122
+ [onBlur],
123
+ );
124
+
125
+ useEffect(() => {
126
+ if (mobileMode === 'native' && isInputDateSupported()) {
127
+ setShouldRenderNative(true);
128
+ }
129
+ }, [mobileMode]);
130
+
131
+ useEffect(() => {
132
+ if (typeof propValue !== 'undefined') {
133
+ setValue(propValue);
134
+ }
135
+ // eslint-disable-next-line react-hooks/exhaustive-deps
136
+ }, [propValue]);
137
+
138
+ return (
139
+ <Input
140
+ {...restProps}
141
+ ref={mergeRefs([ref, inputRef])}
142
+ value={value}
143
+ inputMode='decimal'
144
+ pattern='[0-9\.]*'
145
+ onChange={handleChange}
146
+ onBlur={handleBlur}
147
+ placeholder='ДД.ММ.ГГГГ'
148
+ error={error}
149
+ rightAddons={
150
+ <React.Fragment>
151
+ {rightAddons}
152
+
153
+ {shouldRenderNative && (
154
+ <input
155
+ type='date'
156
+ ref={ref}
157
+ defaultValue={defaultValue}
158
+ onChange={handleNativeInputChange}
159
+ className={styles.nativeInput}
160
+ />
161
+ )}
162
+ </React.Fragment>
163
+ }
164
+ />
165
+ );
166
+ },
167
+ );
@@ -0,0 +1,19 @@
1
+ @import '@alfalab/core-components-themes/src/default.css';
2
+
3
+ .nativeInput {
4
+ opacity: 0;
5
+ position: absolute;
6
+ top: 0;
7
+ left: 0;
8
+ width: 100%;
9
+ height: 100%;
10
+ appearance: none;
11
+ z-index: 1;
12
+
13
+ &::-webkit-calendar-picker-indicator {
14
+ display: none;
15
+ }
16
+ &::-webkit-inner-spin-button {
17
+ display: none;
18
+ }
19
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './Component';
2
+ export * from './utils';
@@ -0,0 +1,37 @@
1
+ import dateFnsFormat from 'date-fns/format';
2
+ import dateFnsIsValid from 'date-fns/isValid';
3
+ import parse from 'date-fns/parse';
4
+
5
+ export const DATE_FORMAT = 'dd.MM.yyyy';
6
+ export const NATIVE_DATE_FORMAT = 'yyyy-MM-dd';
7
+ export const DATE_MASK = [/\d/, /\d/, '.', /\d/, /\d/, '.', /\d/, /\d/, /\d/, /\d/];
8
+
9
+ export const isCompleteDateInput = (input: string) => input.length === DATE_MASK.length;
10
+
11
+ export const formatDate = (date: Date | number, dateFormat = DATE_FORMAT) =>
12
+ dateFnsFormat(date, dateFormat);
13
+
14
+ export const parseDateString = (value: string, dateFormat = DATE_FORMAT) =>
15
+ parse(value, dateFormat, new Date());
16
+
17
+ export const isValid = (inputValue?: string) =>
18
+ !inputValue || (isCompleteDateInput(inputValue) && dateFnsIsValid(parseDateString(inputValue)));
19
+
20
+ export const format = (value: string): string =>
21
+ value
22
+ .replace(/^(\d\d)(\d)$/, '$1.$2') // 121 => 12.1
23
+ .replace(/^(\d\d)\.(\d\d)(\d)$/, '$1.$2.$3') // 12.122 => 12.12.2
24
+ .replace(/^(\d\d)\d\.(.*)/, '$1.$2') // 123.12.2005 => 12.12.2005
25
+ .replace(/^(\d\d\.\d\d)\d\.(.*)/, '$1.$2') // 12.123.2005 => 12.12.2005
26
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d).*/, '$1') // 12.12.20056 => 12.12.2005
27
+ .replace(/\.$/, '') // 12. => 12
28
+ .replace(/^(\d\d\.\d\d)(\d\d\d\d)/, '$1.$2') // 12.122005 => 12.12.2005
29
+ .replace(/^(\d\d)(\d\d\.\d\d\d\d)/, '$1.$2') // 1212.2005 => 12.12.2005
30
+ .replace(/^(\d\.\d\d\.\d\d\d\d)([0-9]*)/, '$1') // 1.12.2005123123 => 01.12.2005
31
+ .replace(/^(\d\d\.\d\.\d\d\d\d)([0-9]*)/, '$1') // 01.2.20055125125 => 01.2.2005
32
+ .replace(/^(\d\.\d\.\d\d\d\d)([0-9]*)/, '$1') // 1.2.20055125125 => 1.2.2005
33
+ .replace(/^(\d)\.(\d\d)([0-9]*)\.(\d\d\d\d)/, '$1.$2.$4') // 1.123123.2005 => 1.12.2005
34
+ .replace(/^(\d\d)\.()\.(\d\d\d\d)([0-9]*)/, '$1.$2.$3') // 01..2005123123 => 01..2005
35
+ .replace(/^(\d)\.()\.(\d\d\d\d)([0-9]*)/, '$1.$2.$3') // 1..2005123123 => 1..2005
36
+ .replace(/^()\.()\.(\d\d\d\d)([0-9]*)/, '$1.$2.$3') // ..2005123123 => ..2005
37
+ .replace(/^()\.(\d)\.(\d\d\d\d)([0-9]*)/, '$1.$2.$3'); // .2.2005123123 => .2.2005
@@ -0,0 +1,2 @@
1
+ export * from './format';
2
+ export * from './native-supports';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Возвращает `true`, если поддерживается `input[type="date"]`
3
+ */
4
+ export function isInputDateSupported() {
5
+ const input = document.createElement('input');
6
+ const value = 'a';
7
+
8
+ input.setAttribute('type', 'date');
9
+ input.setAttribute('value', value);
10
+
11
+ return input.value !== value;
12
+ }