@alfalab/core-components-phone-input 7.1.17 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -5
- package/src/Component.tsx +130 -0
- package/src/index.ts +1 -0
- package/src/utils/index.ts +43 -0
- package/send-stats.js +0 -82
package/package.json
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfalab/core-components-phone-input",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"module": "./esm/index.js",
|
|
9
|
-
"scripts": {
|
|
10
|
-
"postinstall": "node -e \"if (require('fs').existsSync('./send-stats.js')){require('./send-stats.js')} \""
|
|
11
|
-
},
|
|
12
9
|
"publishConfig": {
|
|
13
10
|
"access": "public",
|
|
14
11
|
"directory": "dist"
|
|
@@ -17,7 +14,7 @@
|
|
|
17
14
|
"react": "^16.9.0 || ^17.0.1 || ^18.0.0"
|
|
18
15
|
},
|
|
19
16
|
"dependencies": {
|
|
20
|
-
"@alfalab/core-components-masked-input": "^6.
|
|
17
|
+
"@alfalab/core-components-masked-input": "^6.2.0",
|
|
21
18
|
"text-mask-core": "^5.1.2",
|
|
22
19
|
"tslib": "^2.4.0"
|
|
23
20
|
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import React, { useCallback, useImperativeHandle, useRef } from 'react';
|
|
2
|
+
import { conformToMask, TextMaskConfig } from 'text-mask-core';
|
|
3
|
+
|
|
4
|
+
import { MaskedInput, MaskedInputProps } from '@alfalab/core-components-masked-input';
|
|
5
|
+
|
|
6
|
+
import { deleteFormatting, getInsertedNumber, setCaretPosition } from './utils';
|
|
7
|
+
|
|
8
|
+
const mask = [
|
|
9
|
+
'+',
|
|
10
|
+
'7',
|
|
11
|
+
' ',
|
|
12
|
+
/([0-6]|[8-9])/,
|
|
13
|
+
/\d/,
|
|
14
|
+
/\d/,
|
|
15
|
+
' ',
|
|
16
|
+
/\d/,
|
|
17
|
+
/\d/,
|
|
18
|
+
/\d/,
|
|
19
|
+
'-',
|
|
20
|
+
/\d/,
|
|
21
|
+
/\d/,
|
|
22
|
+
'-',
|
|
23
|
+
/\d/,
|
|
24
|
+
/\d/,
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const countryPrefix = '+7 ';
|
|
28
|
+
|
|
29
|
+
export type PhoneInputProps = Omit<MaskedInputProps, 'onBeforeDisplay' | 'type' | 'mask'> & {
|
|
30
|
+
clearableCountryCode?: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(
|
|
34
|
+
({ clearableCountryCode = true, ...restProps }, ref) => {
|
|
35
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
36
|
+
|
|
37
|
+
// Оставляет возможность прокинуть ref извне
|
|
38
|
+
useImperativeHandle(ref, () => inputRef.current as HTMLInputElement);
|
|
39
|
+
|
|
40
|
+
const handleBeforeDisplay = useCallback(
|
|
41
|
+
(conformedValue: string, config: TextMaskConfig) => {
|
|
42
|
+
const { rawValue, previousConformedValue, currentCaretPosition } = config;
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
* код ниже нужен для фикса следующих багов библиотеки text-mask:
|
|
46
|
+
* 1) так как код страны указан в маске жестко как "+7",
|
|
47
|
+
* то при удалении цифры перед ним каретка устанавливается перед кодом страны
|
|
48
|
+
* 2) в номере телефона есть пробелы и дефисы,
|
|
49
|
+
* при редактировании цифр рядом с этими символами каретка перескакивает через них,
|
|
50
|
+
* а не остается на том же месте, на котором была до редактирования
|
|
51
|
+
*/
|
|
52
|
+
const previousValueWithoutFormatting = previousConformedValue
|
|
53
|
+
? deleteFormatting(previousConformedValue)
|
|
54
|
+
: '';
|
|
55
|
+
const currentValueWithoutFormatting = deleteFormatting(conformedValue) || '';
|
|
56
|
+
|
|
57
|
+
if (
|
|
58
|
+
previousConformedValue &&
|
|
59
|
+
(([3, 6].includes(currentCaretPosition) &&
|
|
60
|
+
Math.abs(
|
|
61
|
+
previousValueWithoutFormatting.length -
|
|
62
|
+
currentValueWithoutFormatting.length,
|
|
63
|
+
) === 1) ||
|
|
64
|
+
([7, 10, 13].includes(currentCaretPosition) &&
|
|
65
|
+
previousConformedValue.length > currentCaretPosition))
|
|
66
|
+
) {
|
|
67
|
+
setCaretPosition({ position: currentCaretPosition, inputRef });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Удаление цифры перед кодом страны удаляет только саму цифру, код остается ("+7 1" -> "+7 ")
|
|
71
|
+
if (rawValue === countryPrefix) {
|
|
72
|
+
return rawValue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Вставка номера с 10 цифрами без кода страны
|
|
76
|
+
if (rawValue.length === 10 && conformedValue.length === mask.length) {
|
|
77
|
+
const masked = conformToMask(`+7${rawValue}`, mask, config);
|
|
78
|
+
|
|
79
|
+
return masked.conformedValue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const insertedNumber = getInsertedNumber({
|
|
83
|
+
rawValue,
|
|
84
|
+
clearableCountryCode,
|
|
85
|
+
countryPrefix,
|
|
86
|
+
previousConformedValue,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Вставка номера, начинающегося с 8 или 7: 89990313131, 71112223344
|
|
90
|
+
if (
|
|
91
|
+
conformedValue.length === mask.length &&
|
|
92
|
+
(insertedNumber.startsWith('8') || insertedNumber.startsWith('7'))
|
|
93
|
+
) {
|
|
94
|
+
const masked = conformToMask(`+7${insertedNumber.slice(1)}`, mask, config);
|
|
95
|
+
|
|
96
|
+
return masked.conformedValue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Если ввод начат с 7 или 8 - выводит "+7 " и дает продолжить ввод со след. цифры
|
|
100
|
+
if (rawValue.length === 1 && ['7', '8'].includes(rawValue[0])) {
|
|
101
|
+
return countryPrefix;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const abortCountryCodeClearing = !clearableCountryCode && !conformedValue;
|
|
105
|
+
|
|
106
|
+
if (abortCountryCodeClearing) {
|
|
107
|
+
setCaretPosition({ position: countryPrefix.length, inputRef });
|
|
108
|
+
|
|
109
|
+
if (!rawValue.length) return countryPrefix;
|
|
110
|
+
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return conformedValue;
|
|
115
|
+
},
|
|
116
|
+
[clearableCountryCode],
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<MaskedInput
|
|
121
|
+
{...restProps}
|
|
122
|
+
defaultValue={clearableCountryCode ? restProps.defaultValue : countryPrefix}
|
|
123
|
+
mask={mask}
|
|
124
|
+
onBeforeDisplay={handleBeforeDisplay}
|
|
125
|
+
type='tel'
|
|
126
|
+
ref={inputRef}
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
129
|
+
},
|
|
130
|
+
);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Component';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Удаляет форматирование номера телефона
|
|
3
|
+
* @param phone Номер телефона
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const deleteFormatting = (phone: string) =>
|
|
7
|
+
phone.replace('+', '').replace(/^7/, '').replace(/\s/g, '').replace(/-/g, '');
|
|
8
|
+
|
|
9
|
+
export function setCaretPosition({
|
|
10
|
+
position,
|
|
11
|
+
inputRef,
|
|
12
|
+
}: {
|
|
13
|
+
position: number;
|
|
14
|
+
inputRef: React.RefObject<HTMLInputElement>;
|
|
15
|
+
}) {
|
|
16
|
+
window.requestAnimationFrame(() => {
|
|
17
|
+
if (inputRef === null || !inputRef.current) return;
|
|
18
|
+
|
|
19
|
+
inputRef.current.setSelectionRange(position, position);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getInsertedNumber({
|
|
24
|
+
rawValue,
|
|
25
|
+
clearableCountryCode,
|
|
26
|
+
countryPrefix,
|
|
27
|
+
previousConformedValue,
|
|
28
|
+
}: {
|
|
29
|
+
rawValue: string;
|
|
30
|
+
clearableCountryCode: boolean;
|
|
31
|
+
countryPrefix: string;
|
|
32
|
+
previousConformedValue: string;
|
|
33
|
+
}) {
|
|
34
|
+
if (!clearableCountryCode && previousConformedValue === countryPrefix) {
|
|
35
|
+
if (rawValue.startsWith('7') || rawValue.startsWith('8')) {
|
|
36
|
+
return rawValue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return rawValue.slice(countryPrefix.length);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return rawValue;
|
|
43
|
+
}
|
package/send-stats.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
const http = require('http');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const { promisify } = require('util');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
const readFile = promisify(fs.readFile);
|
|
7
|
-
|
|
8
|
-
async function main() {
|
|
9
|
-
const remoteHost = process.env.NIS_HOST || 'digital';
|
|
10
|
-
const remotePort = process.env.NIS_PORT || 80;
|
|
11
|
-
const remotePath = process.env.NIS_PATH || '/npm-install-stats/api/install-stats';
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
const [_, node, os, arch] =
|
|
15
|
-
/node\/v(\d+\.\d+\.\d+) (\w+) (\w+)/.exec(process.env.npm_config_user_agent) || [];
|
|
16
|
-
const [__, npm] = /npm\/(\d+\.\d+\.\d+)/.exec(process.env.npm_config_user_agent) || [];
|
|
17
|
-
const [___, yarn] = /yarn\/(\d+\.\d+\.\d+)/.exec(process.env.npm_config_user_agent) || [];
|
|
18
|
-
|
|
19
|
-
let ownPackageJson, packageJson;
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
const result = await Promise.all([
|
|
23
|
-
readFile(path.join(process.cwd(), 'package.json'), 'utf-8'),
|
|
24
|
-
readFile(path.join(process.cwd(), '../../../package.json'), 'utf-8'),
|
|
25
|
-
]);
|
|
26
|
-
|
|
27
|
-
ownPackageJson = JSON.parse(result[0]);
|
|
28
|
-
packageJson = JSON.parse(result[1]);
|
|
29
|
-
} catch (err) {
|
|
30
|
-
ownPackageJson = '';
|
|
31
|
-
packageJson = '';
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const data = {
|
|
35
|
-
node,
|
|
36
|
-
npm,
|
|
37
|
-
yarn,
|
|
38
|
-
os,
|
|
39
|
-
arch,
|
|
40
|
-
ownPackageJson: JSON.stringify(ownPackageJson),
|
|
41
|
-
packageJson: JSON.stringify(packageJson),
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const body = JSON.stringify(data);
|
|
45
|
-
|
|
46
|
-
const options = {
|
|
47
|
-
host: remoteHost,
|
|
48
|
-
port: remotePort,
|
|
49
|
-
path: remotePath,
|
|
50
|
-
method: 'POST',
|
|
51
|
-
headers: {
|
|
52
|
-
'Content-Type': 'application/json',
|
|
53
|
-
'Content-Length': body.length,
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
return new Promise((resolve, reject) => {
|
|
58
|
-
const req = http.request(options, (res) => {
|
|
59
|
-
res.on('end', () => {
|
|
60
|
-
resolve();
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
req.on('error', () => {
|
|
65
|
-
reject();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
req.write(body);
|
|
69
|
-
req.end();
|
|
70
|
-
});
|
|
71
|
-
} catch (error) {
|
|
72
|
-
throw error;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
main()
|
|
77
|
-
.then(() => {
|
|
78
|
-
process.exit(0);
|
|
79
|
-
})
|
|
80
|
-
.catch(() => {
|
|
81
|
-
process.exit(0);
|
|
82
|
-
});
|