@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 CHANGED
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "@alfalab/core-components-phone-input",
3
- "version": "7.1.17",
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.1.17",
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
- });