@hh.ru/magritte-ui-pincode-input 1.0.2 → 1.1.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/PincodeInput.d.ts +8 -0
- package/PincodeInput.js +108 -89
- package/PincodeInput.js.map +1 -1
- package/index.css +60 -33
- package/index.d.ts +1 -0
- package/index.js +3 -1
- package/index.js.map +1 -1
- package/package.json +6 -5
- package/useOTP.d.ts +1 -0
- package/useOTP.js +29 -0
- package/useOTP.js.map +1 -0
package/PincodeInput.d.ts
CHANGED
|
@@ -20,6 +20,14 @@ export interface PincodeInputProps {
|
|
|
20
20
|
description?: string;
|
|
21
21
|
/** Текст сообщения об ошибке */
|
|
22
22
|
errorMessage?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Если код предполагается отправлять посредством sms то для автозаполнения на Android требуется получение от
|
|
25
|
+
* пользователя разрешения на доступ к sms. Для этого используется WebOTP API. Часть этого API находится в
|
|
26
|
+
* статусе экспериментального и поддерживается далеко не всеми браузерами. В браузерах которые поддерживают это
|
|
27
|
+
* API у пользователя будет запрошено разрешение, поэтому используйте этот флаг только в случае отправки OTP
|
|
28
|
+
* через sms.
|
|
29
|
+
*/
|
|
30
|
+
requestCredentials?: boolean;
|
|
23
31
|
'data-qa'?: string;
|
|
24
32
|
}
|
|
25
33
|
export declare const PincodeInput: import("react").ForwardRefExoticComponent<PincodeInputProps & import("react").RefAttributes<HTMLDivElement>>;
|
package/PincodeInput.js
CHANGED
|
@@ -1,82 +1,110 @@
|
|
|
1
1
|
import './index.css';
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { forwardRef, useState, useRef } from 'react';
|
|
3
|
+
import { forwardRef, useState, useRef, useLayoutEffect } from 'react';
|
|
4
4
|
import classnames from 'classnames';
|
|
5
|
-
import { keyboardMatch, keyboardKeys } from '@hh.ru/magritte-common-keyboard';
|
|
6
|
-
import {
|
|
5
|
+
import { keyboardMatch, keyboardKeys, keyboardMatches } from '@hh.ru/magritte-common-keyboard';
|
|
6
|
+
import { useMultipleRefs } from '@hh.ru/magritte-common-use-multiple-refs';
|
|
7
7
|
import { FormHelper } from '@hh.ru/magritte-ui-form-helper';
|
|
8
|
+
import { useOTP } from './useOTP.js';
|
|
8
9
|
|
|
9
|
-
var styles = {"pincode-input":"magritte-pincode-input___RpWyq_1-0
|
|
10
|
+
var styles = {"pincode-input":"magritte-pincode-input___RpWyq_1-1-0","pincodeInput":"magritte-pincode-input___RpWyq_1-1-0","medium":"magritte-medium___wfRSc_1-1-0","placeholder":"magritte-placeholder___10kQ0_1-1-0","focus":"magritte-focus___IuhXb_1-1-0","invalid":"magritte-invalid___hB8h2_1-1-0","focus-visible":"magritte-focus-visible___BN1eD_1-1-0","focusVisible":"magritte-focus-visible___BN1eD_1-1-0","digit-box-container":"magritte-digit-box-container___BqMWw_1-1-0","digitBoxContainer":"magritte-digit-box-container___BqMWw_1-1-0","digit-box":"magritte-digit-box___6J0Q2_1-1-0","digitBox":"magritte-digit-box___6J0Q2_1-1-0","digits-input":"magritte-digits-input___4QeLj_1-1-0","digitsInput":"magritte-digits-input___4QeLj_1-1-0","caret-left":"magritte-caret-left___iZMuA_1-1-0","caretLeft":"magritte-caret-left___iZMuA_1-1-0","caret-right":"magritte-caret-right___u8wNk_1-1-0","caretRight":"magritte-caret-right___u8wNk_1-1-0","caret-blink":"magritte-caret-blink___-bhg7_1-1-0","caretBlink":"magritte-caret-blink___-bhg7_1-1-0","value-container":"magritte-value-container___9JR3e_1-1-0","valueContainer":"magritte-value-container___9JR3e_1-1-0","ghost-value":"magritte-ghost-value___jPQwY_1-1-0","ghostValue":"magritte-ghost-value___jPQwY_1-1-0"};
|
|
10
11
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
// Поэтому двигаем её асинхронно, чтобы сдвиг происходил после нативного, иначе она окажется не в нужной позиции
|
|
14
|
-
requestAnimationFrame(() => {
|
|
15
|
-
if (ref.current) {
|
|
16
|
-
Selection.setCaretPosition(ref.current, position);
|
|
17
|
-
callback?.();
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
const getCaretPosition = (ref) => ref.current ? (Selection.getCaretPosition(ref.current) ?? 0) : 0;
|
|
21
|
-
const PincodeInput = forwardRef(({ digitsCount: _digitsCount = null, size = 'large', invalid = false, onFocus, onBlur, onChange, description, errorMessage, 'data-qa': dataQa = 'magritte-pincode-input', }, ref) => {
|
|
12
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
13
|
+
const PincodeInput = forwardRef(({ digitsCount: _digitsCount = null, size = 'large', invalid = false, onFocus, onBlur, onChange, description, errorMessage, requestCredentials = false, 'data-qa': dataQa = 'magritte-pincode-input', }, ref) => {
|
|
22
14
|
const digitsCount = _digitsCount ?? (size === 'medium' ? 6 : 4);
|
|
23
15
|
const [value, setValue] = useState('');
|
|
24
16
|
const [caretPosition, setCaretPosition] = useState(0);
|
|
25
17
|
const [focused, setFocused] = useState(false);
|
|
26
18
|
const [focusVisible, setFocusVisible] = useState(false);
|
|
19
|
+
const [inputKey, setInputKey] = useState(0);
|
|
20
|
+
const ghostLastDigitRef = useRef(null);
|
|
21
|
+
const containerRef = useRef(null);
|
|
27
22
|
const inputRef = useRef(null);
|
|
28
|
-
const
|
|
23
|
+
const autofillValueRef = useRef('');
|
|
29
24
|
const updateValue = (newValue) => {
|
|
30
25
|
setValue(newValue);
|
|
31
26
|
onChange?.(newValue);
|
|
32
27
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
};
|
|
58
|
-
return (jsxs("div", { ref: ref, "data-qa": dataQa, children: [jsxs("div", { "data-qa": "magritte-pincode-input-wrapper", className: classnames(styles.pincodeInput, {
|
|
28
|
+
useOTP((OPTCode) => {
|
|
29
|
+
if (!OPTCode) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
updateValue(OPTCode);
|
|
33
|
+
setCaretPosition(OPTCode.length - 1);
|
|
34
|
+
setInputKey((key) => key + 1);
|
|
35
|
+
}, requestCredentials);
|
|
36
|
+
// внутри этого хука безопасно не мемоизировать рефколлбек, коллбек который будет возвращен будет мемоизирован
|
|
37
|
+
// и не будет меняться при изменении ссылок на аргументы
|
|
38
|
+
const inputRefMultiplexer = useMultipleRefs(inputRef, (element) => {
|
|
39
|
+
if (element && focused) {
|
|
40
|
+
element.focus();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
useLayoutEffect(() => {
|
|
44
|
+
if (!containerRef.current || !ghostLastDigitRef.current) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const lastWidth = ghostLastDigitRef.current.getBoundingClientRect().width;
|
|
48
|
+
containerRef.current.style.setProperty('--magritte-pincode-last-digit-width', `${lastWidth}px`);
|
|
49
|
+
}, [caretPosition, value]);
|
|
50
|
+
const caretPos = clamp(caretPosition, 0, value.length);
|
|
51
|
+
return (jsxs("div", { ref: ref, "data-qa": dataQa, tabIndex: 0, onFocus: () => inputRef.current?.focus(), children: [jsxs("div", { "data-qa": "magritte-pincode-input-wrapper", ref: containerRef, className: classnames(styles.pincodeInput, {
|
|
59
52
|
[styles.focus]: focused,
|
|
60
53
|
[styles.focusVisible]: focusVisible,
|
|
61
54
|
[styles.medium]: size === 'medium',
|
|
62
55
|
[styles.invalid]: invalid,
|
|
63
|
-
}),
|
|
56
|
+
}), onMouseDown: (event) => {
|
|
57
|
+
if (event.target === inputRef.current) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
event.preventDefault();
|
|
64
61
|
const caretPosition = Math.min(Number(event.target?.dataset?.digitIndex ?? value.length), value.length);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
if (!focused) {
|
|
63
|
+
inputRef.current?.focus();
|
|
64
|
+
}
|
|
65
|
+
setCaretPosition(caretPosition);
|
|
66
|
+
}, style: {
|
|
67
|
+
'--magritte-pincode-digits-count': digitsCount,
|
|
68
|
+
'--magritte-pincode-caret-position': caretPos,
|
|
69
|
+
}, children: [jsx("input", { ref: inputRefMultiplexer, tabIndex: -1, autoComplete: "one-time-code", pattern: `\\d{${digitsCount}}`, "data-qa": "magritte-pincode-input-field", className: styles.digitsInput, type: "text", inputMode: "numeric", maxLength: digitsCount,
|
|
70
|
+
// оставляем инпут всегда пустым, т.к. он нам нужен только для показа контекстного меню вставки,
|
|
71
|
+
// обработки события вставки и обработки фокуса
|
|
72
|
+
value: "", onChange: (event) => {
|
|
73
|
+
if (!/\d/.test(event.target.value)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Вставку в инпут и нажатия клавиш цифр мы перехватываем
|
|
77
|
+
// поэтому если попали сюда, значит использовался autofill (например из sms).
|
|
78
|
+
// На iOS autofill генерит несколько событий, по количеству заполняемых символов.
|
|
79
|
+
// Мы не меняем значение самого инпута, поэтому каждое событие будет содержать только
|
|
80
|
+
// один символ. Сохраняем их и когда их количество достигнет ожидаемого (digitsCount)
|
|
81
|
+
// подставляем в значение
|
|
82
|
+
autofillValueRef.current += event.target.value;
|
|
83
|
+
if (autofillValueRef.current.length >= digitsCount) {
|
|
84
|
+
updateValue(autofillValueRef.current.slice(0, digitsCount));
|
|
85
|
+
autofillValueRef.current = '';
|
|
86
|
+
setCaretPosition(digitsCount - 1);
|
|
87
|
+
// Кроме того при автозаполнении инпут красится в желтый цвет, что в нашем случае
|
|
88
|
+
// недопустимо т.к. инпут располагается поверх цифр и должен быть прозрачным. Это нельзя
|
|
89
|
+
// переопределить с помощью css, есть только хак когда задают transition для цвета фона
|
|
90
|
+
// это замедлит смену цвета, но в конечном итоге инпут все же поменяет цвет фона.
|
|
91
|
+
// Поэтому меняем key у инпута, чтобы он перемонтировался и браузер не определял его как
|
|
92
|
+
// автозаполненный инпут. Важно после перемонтирования не забыть вернуть в него фокус,
|
|
93
|
+
// иначе для вызова меню вставки потребуется два тапа вместо одного.
|
|
94
|
+
setInputKey((key) => key + 1);
|
|
95
|
+
}
|
|
96
|
+
}, onFocus: (event) => {
|
|
97
|
+
if (focused) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
72
100
|
onFocus?.(event);
|
|
73
|
-
refineCaretPosition();
|
|
74
101
|
if (!event.defaultPrevented) {
|
|
75
102
|
setFocused(true);
|
|
76
103
|
setFocusVisible(!!inputRef.current?.classList.contains('focus-visible'));
|
|
77
104
|
if (invalid) {
|
|
78
105
|
// По спеке если инпут помечен как invalid при фокусе содержимое должно очищаться
|
|
79
106
|
updateValue('');
|
|
107
|
+
setCaretPosition(0);
|
|
80
108
|
}
|
|
81
109
|
}
|
|
82
110
|
}, onBlur: (event) => {
|
|
@@ -90,73 +118,64 @@ const PincodeInput = forwardRef(({ digitsCount: _digitsCount = null, size = 'lar
|
|
|
90
118
|
return;
|
|
91
119
|
}
|
|
92
120
|
const isLeft = keyboardMatch(event, keyboardKeys.ArrowLeft);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// перемещении курсо просто встанет перед выделенной цифрой, а refineCaretPosition yсова её
|
|
96
|
-
// выделит и поставит каретку в исходное положение. Т.е. без этого кода не получится
|
|
97
|
-
// передвинуть каретку влево, если выделена цифра
|
|
98
|
-
if (isLeft && selectionRef.current !== null) {
|
|
121
|
+
const isRight = keyboardMatch(event, keyboardKeys.ArrowRight);
|
|
122
|
+
if (isLeft || isRight) {
|
|
99
123
|
event.preventDefault();
|
|
100
|
-
|
|
124
|
+
setCaretPosition((caretPosition) => clamp(caretPosition + (isRight ? 1 : -1), 0, value.length));
|
|
101
125
|
return;
|
|
102
126
|
}
|
|
103
|
-
if (
|
|
127
|
+
if (keyboardMatches(event, keyboardKeys.DigitKeys)) {
|
|
128
|
+
updateValue(`${value.slice(0, caretPos)}${event.key}${value.slice(caretPos + 1)}`);
|
|
129
|
+
setCaretPosition((caretPosition) => clamp(caretPosition + 1, 0, digitsCount - 1));
|
|
130
|
+
event.preventDefault();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const isBackspace = keyboardMatch(event, keyboardKeys.Backspace);
|
|
134
|
+
const isDelete = keyboardMatch(event, keyboardKeys.Delete);
|
|
135
|
+
if (isBackspace || isDelete) {
|
|
104
136
|
// При нажатии backspace действует следующая логика:
|
|
105
137
|
// Если каретка находится в позиции последней введенной цифры, то удаляем её
|
|
106
138
|
// и оставляем каретку на той же позиции
|
|
107
139
|
// Если каретка находится в другой позиции, то удаляем цифру слева от каретки и
|
|
108
140
|
// сдвигаем каретку влево
|
|
141
|
+
const isLastDigit = caretPos === value.length - 1;
|
|
142
|
+
const deleteIndex = clamp(caretPos - (isDelete || isLastDigit ? 0 : 1), 0, digitsCount - 1);
|
|
143
|
+
updateValue(`${value.slice(0, deleteIndex)}${value.slice(deleteIndex + 1)}`);
|
|
144
|
+
setCaretPosition((caretPosition) => {
|
|
145
|
+
const newPosition = isLastDigit || isDelete ? caretPosition : Math.max(caretPosition - 1, 0);
|
|
146
|
+
return newPosition;
|
|
147
|
+
});
|
|
109
148
|
event.preventDefault();
|
|
110
|
-
const deleteIndex = selectionRef.current === value.length - 1
|
|
111
|
-
? selectionRef.current
|
|
112
|
-
: Math.max(selectionRef.current - 1, 0);
|
|
113
|
-
const newValue = `${value.slice(0, deleteIndex)}${value.slice(deleteIndex + 1)}`;
|
|
114
|
-
updateValue(newValue);
|
|
115
|
-
if (selectionRef.current !== value.length - 1) {
|
|
116
|
-
moveCaretTo(inputRef, Math.max(selectionRef.current - 1, 0), refineCaretPosition);
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
refineCaretPosition();
|
|
121
|
-
}, onChange: (event) => {
|
|
122
|
-
updateValue(event.target.value);
|
|
123
|
-
if (selectionRef.current !== null) {
|
|
124
|
-
// При изменении, если была выделена цифра, то каретку надо сдвинуть на следующую
|
|
125
|
-
// позицию вручную
|
|
126
|
-
moveCaretTo(inputRef, selectionRef.current + 1, refineCaretPosition);
|
|
127
|
-
return;
|
|
128
149
|
}
|
|
129
|
-
refineCaretPosition();
|
|
130
150
|
}, onPaste: (event) => {
|
|
131
151
|
event.preventDefault();
|
|
132
152
|
const pasteValue = event.clipboardData.getData('text').replace(/\D/g, '');
|
|
133
153
|
if (pasteValue.length === digitsCount) {
|
|
134
154
|
updateValue(pasteValue);
|
|
135
|
-
|
|
155
|
+
setCaretPosition(digitsCount - 1);
|
|
136
156
|
return;
|
|
137
157
|
}
|
|
138
|
-
const
|
|
139
|
-
const newValue = `${value.slice(0, caretPosition)}${pasteValue}${value.slice(caretPosition + pasteValue.length)}`.slice(0, digitsCount);
|
|
158
|
+
const newValue = `${value.slice(0, caretPos)}${pasteValue}${value.slice(caretPos + pasteValue.length)}`.slice(0, digitsCount);
|
|
140
159
|
updateValue(newValue);
|
|
141
|
-
|
|
142
|
-
} }), jsx("div", { className: styles.digitBoxContainer, children: Array.from({ length: digitsCount }).map((_, index) => (jsx("div", { className: classnames(styles.digitBox), "data-digit-index": index, children: jsx("span", { className: classnames(styles.valueContainer, {
|
|
160
|
+
setCaretPosition(Math.min(digitsCount - 1, caretPos + pasteValue.length));
|
|
161
|
+
} }, inputKey), jsx("div", { className: styles.digitBoxContainer, children: Array.from({ length: digitsCount }).map((_, index) => (jsx("div", { className: classnames(styles.digitBox), "data-digit-index": index, children: jsx("span", { className: classnames(styles.valueContainer, {
|
|
143
162
|
// в позицию ничего не введено, и каретка не находится в этой позиции
|
|
144
163
|
[styles.placeholder]: index >= value.length &&
|
|
145
164
|
(!focused ||
|
|
146
|
-
(index !==
|
|
147
|
-
!(
|
|
165
|
+
(index !== caretPos &&
|
|
166
|
+
!(caretPos === digitsCount && index === digitsCount - 1))),
|
|
148
167
|
// в позицию введена цифра, каретка находится в этой позиции, фокус
|
|
149
168
|
// установлен в инпут, это не крайняя справа цифра из введенных
|
|
150
|
-
[styles.caretLeft]: index ===
|
|
169
|
+
[styles.caretLeft]: index === caretPos && index !== value.length - 1 && focused,
|
|
151
170
|
// в позицию введена цифра, каретка находится в этой позиции, либо в следующей
|
|
152
171
|
// если введены все цифры (потому что при вводе последней цифры каретка
|
|
153
172
|
// должна уходить за нее), фокус установлен в инпут, это крайняя справа цифра
|
|
154
173
|
// из введенных
|
|
155
|
-
[styles.caretRight]: (index ===
|
|
156
|
-
(index === digitsCount - 1 && index ===
|
|
174
|
+
[styles.caretRight]: (index === caretPos ||
|
|
175
|
+
(index === digitsCount - 1 && index === caretPos - 1)) &&
|
|
157
176
|
index === value.length - 1 &&
|
|
158
177
|
focused,
|
|
159
|
-
}), "data-qa": `magritte-pincode-input-digit-${index}`, children: value[index] || (
|
|
178
|
+
}), "data-qa": `magritte-pincode-input-digit-${index}`, children: value[index] || (caretPos === index && focused ? '' : '•') }) }, index))) }), jsx("div", { className: styles.ghostValue, ref: ghostLastDigitRef, children: value.slice(caretPos, caretPos + 1) })] }), jsx(FormHelper, { invalid: invalid, description: description, errorMessage: errorMessage, "data-qa": "magritte-pincode-input-message" })] }));
|
|
160
179
|
});
|
|
161
180
|
PincodeInput.displayName = 'PincodeInput';
|
|
162
181
|
|
package/PincodeInput.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PincodeInput.js","sources":["../src/PincodeInput.tsx"],"sourcesContent":["import { type CSSProperties, type FocusEventHandler, useRef, useState, RefObject, forwardRef } from 'react';\nimport classnames from 'classnames';\n\nimport { keyboardKeys, keyboardMatch } from '@hh.ru/magritte-common-keyboard';\nimport { Selection } from '@hh.ru/magritte-common-text-selection';\nimport { FormHelper } from '@hh.ru/magritte-ui-form-helper';\n\nimport styles from './pincodeInput.less';\n\nconst moveCaretTo = (ref: RefObject<HTMLInputElement>, position: number, callback?: VoidFunction) =>\n // Каретка сдвигается при изменении содержимого инпута автоматически\n // Поэтому двигаем её асинхронно, чтобы сдвиг происходил после нативного, иначе она окажется не в нужной позиции\n requestAnimationFrame(() => {\n if (ref.current) {\n Selection.setCaretPosition(ref.current, position);\n callback?.();\n }\n });\n\nconst getCaretPosition = (ref: RefObject<HTMLInputElement>) =>\n ref.current ? (Selection.getCaretPosition(ref.current) ?? 0) : 0;\n\nexport type PincodeInputSize = 'medium' | 'large';\n\nexport interface PincodeInputProps {\n /**\n * Количество цифр в пин-коде, если не указано будет выбрано автоматически\n * в зависимости от размера инпута ( medium - 6, large - 4)\n */\n digitsCount?: number | null;\n /** Размер инпута */\n size?: PincodeInputSize;\n /** Обработчик изменений */\n onChange?: (value: string) => void;\n /** Обработчик фокуса */\n onFocus?: FocusEventHandler<HTMLInputElement>;\n /** Обработчик потери фокуса */\n onBlur?: FocusEventHandler<HTMLInputElement>;\n /** Флаг наличия ошибки */\n invalid?: boolean;\n /** Текст описания */\n description?: string;\n /** Текст сообщения об ошибке */\n errorMessage?: string;\n 'data-qa'?: string;\n}\n\nexport const PincodeInput = forwardRef<HTMLDivElement, PincodeInputProps>(\n (\n {\n digitsCount: _digitsCount = null,\n size = 'large',\n invalid = false,\n onFocus,\n onBlur,\n onChange,\n description,\n errorMessage,\n 'data-qa': dataQa = 'magritte-pincode-input',\n },\n ref\n ) => {\n const digitsCount = _digitsCount ?? (size === 'medium' ? 6 : 4);\n const [value, setValue] = useState<string>('');\n const [caretPosition, setCaretPosition] = useState<number>(0);\n const [focused, setFocused] = useState(false);\n const [focusVisible, setFocusVisible] = useState(false);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const selectionRef = useRef<number | null>(null);\n\n const updateValue = (newValue: string) => {\n setValue(newValue);\n onChange?.(newValue);\n };\n\n const refineCaretPosition = () => {\n // если попытаться получить позицию каретки синхронно, то получим позицию до изменения\n requestAnimationFrame(() => {\n if (!inputRef.current) {\n return;\n }\n // Когда перемещаемся между цифрами если в позиции введена цифра, то ввод должен заменят именно её\n // а не сдвигать весь текст после нее врпаво, как в обычном инпуте.\n // Поэтому здесь проверяем если в позиции что-то введено, то не просто устанавливаем каретку, а выделяем\n // символ, чтобы при вводе он заменился\n const caretPosition = getCaretPosition(inputRef);\n const value = inputRef.current?.value ?? '';\n const digitNumberToSelect = Math.min(caretPosition, Math.min(value.length, digitsCount - 1));\n const selection = Selection.get(inputRef.current);\n if (selectionRef.current && selection.start !== selection.end) {\n return;\n }\n selectionRef.current = null;\n if (value.length > digitNumberToSelect) {\n Selection.set(inputRef.current, digitNumberToSelect, digitNumberToSelect + 1, 'forward');\n selectionRef.current = digitNumberToSelect;\n }\n setCaretPosition(caretPosition);\n });\n };\n\n return (\n <div ref={ref} data-qa={dataQa}>\n <div\n data-qa=\"magritte-pincode-input-wrapper\"\n className={classnames(styles.pincodeInput, {\n [styles.focus]: focused,\n [styles.focusVisible]: focusVisible,\n [styles.medium]: size === 'medium',\n [styles.invalid]: invalid,\n })}\n onClick={(event) => {\n const caretPosition = Math.min(\n Number((event.target as HTMLElement)?.dataset?.digitIndex ?? value.length),\n value.length\n );\n inputRef.current?.focus();\n moveCaretTo(inputRef, caretPosition);\n refineCaretPosition();\n }}\n // Этот обработчик нужен для того, чтобы не сбрасывался фокус при клике на дочерние элементы\n // Визуально это проявляется как моргание цвета рамки если клик происходит когда инпут в фокусе\n onMouseDown={(event) => event.preventDefault()}\n style={{ '--magritte-pincode-digits-count': digitsCount } as CSSProperties}\n >\n <input\n ref={inputRef}\n autoComplete=\"one-time-code\"\n pattern={`\\\\d{${digitsCount}}`}\n data-qa=\"magritte-pincode-input-field\"\n className={styles.digitsInput}\n type=\"text\"\n inputMode=\"numeric\"\n maxLength={digitsCount}\n value={value}\n onFocus={(event) => {\n onFocus?.(event);\n refineCaretPosition();\n if (!event.defaultPrevented) {\n setFocused(true);\n setFocusVisible(!!inputRef.current?.classList.contains('focus-visible'));\n if (invalid) {\n // По спеке если инпут помечен как invalid при фокусе содержимое должно очищаться\n updateValue('');\n }\n }\n }}\n onBlur={(event) => {\n onBlur?.(event);\n if (!event.defaultPrevented) {\n setFocused(false);\n setFocusVisible(false);\n }\n }}\n onKeyDown={(event) => {\n if (!inputRef.current) {\n return;\n }\n\n const isLeft = keyboardMatch(event, keyboardKeys.ArrowLeft);\n\n // Если одна из цифр выделена, и мы пытаемся передвинут каретку влево с помощью клавиатуры,\n // нужно делать это вручную, так как цифры мы выделяем справо на лево, и при нативном\n // перемещении курсо просто встанет перед выделенной цифрой, а refineCaretPosition yсова её\n // выделит и поставит каретку в исходное положение. Т.е. без этого кода не получится\n // передвинуть каретку влево, если выделена цифра\n if (isLeft && selectionRef.current !== null) {\n event.preventDefault();\n moveCaretTo(\n inputRef,\n Math.max(0, Math.min(selectionRef.current - 1, digitsCount - 1)),\n refineCaretPosition\n );\n return;\n }\n\n if (keyboardMatch(event, keyboardKeys.Backspace) && selectionRef.current !== null) {\n // При нажатии backspace действует следующая логика:\n // Если каретка находится в позиции последней введенной цифры, то удаляем её\n // и оставляем каретку на той же позиции\n // Если каретка находится в другой позиции, то удаляем цифру слева от каретки и\n // сдвигаем каретку влево\n event.preventDefault();\n const deleteIndex =\n selectionRef.current === value.length - 1\n ? selectionRef.current\n : Math.max(selectionRef.current - 1, 0);\n const newValue = `${value.slice(0, deleteIndex)}${value.slice(deleteIndex + 1)}`;\n updateValue(newValue);\n if (selectionRef.current !== value.length - 1) {\n moveCaretTo(inputRef, Math.max(selectionRef.current - 1, 0), refineCaretPosition);\n return;\n }\n }\n\n refineCaretPosition();\n }}\n onChange={(event) => {\n updateValue(event.target.value);\n if (selectionRef.current !== null) {\n // При изменении, если была выделена цифра, то каретку надо сдвинуть на следующую\n // позицию вручную\n moveCaretTo(inputRef, selectionRef.current + 1, refineCaretPosition);\n return;\n }\n refineCaretPosition();\n }}\n onPaste={(event) => {\n event.preventDefault();\n const pasteValue = event.clipboardData.getData('text').replace(/\\D/g, '');\n if (pasteValue.length === digitsCount) {\n updateValue(pasteValue);\n moveCaretTo(inputRef, digitsCount, refineCaretPosition);\n return;\n }\n\n const caretPosition = Math.min(\n getCaretPosition(inputRef),\n selectionRef.current ?? value.length\n );\n const newValue =\n `${value.slice(0, caretPosition)}${pasteValue}${value.slice(caretPosition + pasteValue.length)}`.slice(\n 0,\n digitsCount\n );\n updateValue(newValue);\n moveCaretTo(inputRef, caretPosition + pasteValue.length, refineCaretPosition);\n }}\n />\n <div className={styles.digitBoxContainer}>\n {/* eslint-disable-next-line no-restricted-properties */}\n {Array.from({ length: digitsCount }).map((_, index) => (\n <div key={index} className={classnames(styles.digitBox)} data-digit-index={index}>\n <span\n className={classnames(styles.valueContainer, {\n // в позицию ничего не введено, и каретка не находится в этой позиции\n [styles.placeholder]:\n index >= value.length &&\n (!focused ||\n (index !== caretPosition &&\n !(caretPosition === digitsCount && index === digitsCount - 1))),\n // в позицию введена цифра, каретка находится в этой позиции, фокус\n // установлен в инпут, это не крайняя справа цифра из введенных\n [styles.caretLeft]:\n index === caretPosition && index !== value.length - 1 && focused,\n // в позицию введена цифра, каретка находится в этой позиции, либо в следующей\n // если введены все цифры (потому что при вводе последней цифры каретка\n // должна уходить за нее), фокус установлен в инпут, это крайняя справа цифра\n // из введенных\n [styles.caretRight]:\n (index === caretPosition ||\n (index === digitsCount - 1 && index === caretPosition - 1)) &&\n index === value.length - 1 &&\n focused,\n })}\n data-qa={`magritte-pincode-input-digit-${index}`}\n >\n {value[index] || (caretPosition === index && focused ? '' : '•')}\n </span>\n </div>\n ))}\n </div>\n </div>\n <FormHelper\n invalid={invalid}\n description={description}\n errorMessage={errorMessage}\n data-qa=\"magritte-pincode-input-message\"\n />\n </div>\n );\n }\n);\n\nPincodeInput.displayName = 'PincodeInput';\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PincodeInput.js","sources":["../src/PincodeInput.tsx"],"sourcesContent":["import { type CSSProperties, type FocusEventHandler, useRef, useState, forwardRef, useLayoutEffect } from 'react';\nimport classnames from 'classnames';\n\nimport { keyboardKeys, keyboardMatch, keyboardMatches } from '@hh.ru/magritte-common-keyboard';\nimport { useMultipleRefs } from '@hh.ru/magritte-common-use-multiple-refs';\nimport { FormHelper } from '@hh.ru/magritte-ui-form-helper';\nimport { useOTP } from '@hh.ru/magritte-ui-pincode-input/useOTP';\n\nimport styles from './pincodeInput.less';\n\nexport type PincodeInputSize = 'medium' | 'large';\n\nexport interface PincodeInputProps {\n /**\n * Количество цифр в пин-коде, если не указано будет выбрано автоматически\n * в зависимости от размера инпута ( medium - 6, large - 4)\n */\n digitsCount?: number | null;\n /** Размер инпута */\n size?: PincodeInputSize;\n /** Обработчик изменений */\n onChange?: (value: string) => void;\n /** Обработчик фокуса */\n onFocus?: FocusEventHandler<HTMLInputElement>;\n /** Обработчик потери фокуса */\n onBlur?: FocusEventHandler<HTMLInputElement>;\n /** Флаг наличия ошибки */\n invalid?: boolean;\n /** Текст описания */\n description?: string;\n /** Текст сообщения об ошибке */\n errorMessage?: string;\n /**\n * Если код предполагается отправлять посредством sms то для автозаполнения на Android требуется получение от\n * пользователя разрешения на доступ к sms. Для этого используется WebOTP API. Часть этого API находится в\n * статусе экспериментального и поддерживается далеко не всеми браузерами. В браузерах которые поддерживают это\n * API у пользователя будет запрошено разрешение, поэтому используйте этот флаг только в случае отправки OTP\n * через sms.\n */\n requestCredentials?: boolean;\n 'data-qa'?: string;\n}\n\nconst clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max);\n\nexport const PincodeInput = forwardRef<HTMLDivElement, PincodeInputProps>(\n (\n {\n digitsCount: _digitsCount = null,\n size = 'large',\n invalid = false,\n onFocus,\n onBlur,\n onChange,\n description,\n errorMessage,\n requestCredentials = false,\n 'data-qa': dataQa = 'magritte-pincode-input',\n },\n ref\n ) => {\n const digitsCount = _digitsCount ?? (size === 'medium' ? 6 : 4);\n const [value, setValue] = useState<string>('');\n const [caretPosition, setCaretPosition] = useState<number>(0);\n const [focused, setFocused] = useState(false);\n const [focusVisible, setFocusVisible] = useState(false);\n const [inputKey, setInputKey] = useState(0);\n\n const ghostLastDigitRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const autofillValueRef = useRef('');\n\n const updateValue = (newValue: string) => {\n setValue(newValue);\n onChange?.(newValue);\n };\n\n useOTP((OPTCode) => {\n if (!OPTCode) {\n return;\n }\n updateValue(OPTCode);\n setCaretPosition(OPTCode.length - 1);\n setInputKey((key) => key + 1);\n }, requestCredentials);\n\n // внутри этого хука безопасно не мемоизировать рефколлбек, коллбек который будет возвращен будет мемоизирован\n // и не будет меняться при изменении ссылок на аргументы\n const inputRefMultiplexer = useMultipleRefs(inputRef, (element) => {\n if (element && focused) {\n element.focus();\n }\n });\n\n useLayoutEffect(() => {\n if (!containerRef.current || !ghostLastDigitRef.current) {\n return;\n }\n\n const lastWidth = ghostLastDigitRef.current.getBoundingClientRect().width;\n containerRef.current.style.setProperty('--magritte-pincode-last-digit-width', `${lastWidth}px`);\n }, [caretPosition, value]);\n\n const caretPos = clamp(caretPosition, 0, value.length);\n\n return (\n <div ref={ref} data-qa={dataQa} tabIndex={0} onFocus={() => inputRef.current?.focus()}>\n <div\n data-qa=\"magritte-pincode-input-wrapper\"\n ref={containerRef}\n className={classnames(styles.pincodeInput, {\n [styles.focus]: focused,\n [styles.focusVisible]: focusVisible,\n [styles.medium]: size === 'medium',\n [styles.invalid]: invalid,\n })}\n onMouseDown={(event) => {\n if (event.target === inputRef.current) {\n return;\n }\n event.preventDefault();\n\n const caretPosition = Math.min(\n Number((event.target as HTMLElement)?.dataset?.digitIndex ?? value.length),\n value.length\n );\n\n if (!focused) {\n inputRef.current?.focus();\n }\n\n setCaretPosition(caretPosition);\n }}\n style={\n {\n '--magritte-pincode-digits-count': digitsCount,\n '--magritte-pincode-caret-position': caretPos,\n } as CSSProperties\n }\n >\n <input\n key={inputKey}\n ref={inputRefMultiplexer}\n tabIndex={-1}\n autoComplete=\"one-time-code\"\n pattern={`\\\\d{${digitsCount}}`}\n data-qa=\"magritte-pincode-input-field\"\n className={styles.digitsInput}\n type=\"text\"\n inputMode=\"numeric\"\n maxLength={digitsCount}\n // оставляем инпут всегда пустым, т.к. он нам нужен только для показа контекстного меню вставки,\n // обработки события вставки и обработки фокуса\n value=\"\"\n onChange={(event) => {\n if (!/\\d/.test(event.target.value)) {\n return;\n }\n\n // Вставку в инпут и нажатия клавиш цифр мы перехватываем\n // поэтому если попали сюда, значит использовался autofill (например из sms).\n // На iOS autofill генерит несколько событий, по количеству заполняемых символов.\n // Мы не меняем значение самого инпута, поэтому каждое событие будет содержать только\n // один символ. Сохраняем их и когда их количество достигнет ожидаемого (digitsCount)\n // подставляем в значение\n autofillValueRef.current += event.target.value;\n\n if (autofillValueRef.current.length >= digitsCount) {\n updateValue(autofillValueRef.current.slice(0, digitsCount));\n autofillValueRef.current = '';\n setCaretPosition(digitsCount - 1);\n\n // Кроме того при автозаполнении инпут красится в желтый цвет, что в нашем случае\n // недопустимо т.к. инпут располагается поверх цифр и должен быть прозрачным. Это нельзя\n // переопределить с помощью css, есть только хак когда задают transition для цвета фона\n // это замедлит смену цвета, но в конечном итоге инпут все же поменяет цвет фона.\n // Поэтому меняем key у инпута, чтобы он перемонтировался и браузер не определял его как\n // автозаполненный инпут. Важно после перемонтирования не забыть вернуть в него фокус,\n // иначе для вызова меню вставки потребуется два тапа вместо одного.\n setInputKey((key) => key + 1);\n }\n }}\n onFocus={(event) => {\n if (focused) {\n return;\n }\n onFocus?.(event);\n if (!event.defaultPrevented) {\n setFocused(true);\n setFocusVisible(!!inputRef.current?.classList.contains('focus-visible'));\n if (invalid) {\n // По спеке если инпут помечен как invalid при фокусе содержимое должно очищаться\n updateValue('');\n setCaretPosition(0);\n }\n }\n }}\n onBlur={(event) => {\n onBlur?.(event);\n if (!event.defaultPrevented) {\n setFocused(false);\n setFocusVisible(false);\n }\n }}\n onKeyDown={(event) => {\n if (!inputRef.current) {\n return;\n }\n\n const isLeft = keyboardMatch(event, keyboardKeys.ArrowLeft);\n const isRight = keyboardMatch(event, keyboardKeys.ArrowRight);\n if (isLeft || isRight) {\n event.preventDefault();\n setCaretPosition((caretPosition) =>\n clamp(caretPosition + (isRight ? 1 : -1), 0, value.length)\n );\n return;\n }\n\n if (keyboardMatches(event, keyboardKeys.DigitKeys)) {\n updateValue(`${value.slice(0, caretPos)}${event.key}${value.slice(caretPos + 1)}`);\n setCaretPosition((caretPosition) => clamp(caretPosition + 1, 0, digitsCount - 1));\n event.preventDefault();\n return;\n }\n\n const isBackspace = keyboardMatch(event, keyboardKeys.Backspace);\n const isDelete = keyboardMatch(event, keyboardKeys.Delete);\n\n if (isBackspace || isDelete) {\n // При нажатии backspace действует следующая логика:\n // Если каретка находится в позиции последней введенной цифры, то удаляем её\n // и оставляем каретку на той же позиции\n // Если каретка находится в другой позиции, то удаляем цифру слева от каретки и\n // сдвигаем каретку влево\n\n const isLastDigit = caretPos === value.length - 1;\n const deleteIndex = clamp(\n caretPos - (isDelete || isLastDigit ? 0 : 1),\n 0,\n digitsCount - 1\n );\n updateValue(`${value.slice(0, deleteIndex)}${value.slice(deleteIndex + 1)}`);\n setCaretPosition((caretPosition) => {\n const newPosition =\n isLastDigit || isDelete ? caretPosition : Math.max(caretPosition - 1, 0);\n\n return newPosition;\n });\n\n event.preventDefault();\n }\n }}\n onPaste={(event) => {\n event.preventDefault();\n const pasteValue = event.clipboardData.getData('text').replace(/\\D/g, '');\n if (pasteValue.length === digitsCount) {\n updateValue(pasteValue);\n setCaretPosition(digitsCount - 1);\n return;\n }\n\n const newValue =\n `${value.slice(0, caretPos)}${pasteValue}${value.slice(caretPos + pasteValue.length)}`.slice(\n 0,\n digitsCount\n );\n updateValue(newValue);\n setCaretPosition(Math.min(digitsCount - 1, caretPos + pasteValue.length));\n }}\n />\n <div className={styles.digitBoxContainer}>\n {/* eslint-disable-next-line no-restricted-properties */}\n {Array.from({ length: digitsCount }).map((_, index) => (\n <div key={index} className={classnames(styles.digitBox)} data-digit-index={index}>\n <span\n className={classnames(styles.valueContainer, {\n // в позицию ничего не введено, и каретка не находится в этой позиции\n [styles.placeholder]:\n index >= value.length &&\n (!focused ||\n (index !== caretPos &&\n !(caretPos === digitsCount && index === digitsCount - 1))),\n // в позицию введена цифра, каретка находится в этой позиции, фокус\n // установлен в инпут, это не крайняя справа цифра из введенных\n [styles.caretLeft]: index === caretPos && index !== value.length - 1 && focused,\n // в позицию введена цифра, каретка находится в этой позиции, либо в следующей\n // если введены все цифры (потому что при вводе последней цифры каретка\n // должна уходить за нее), фокус установлен в инпут, это крайняя справа цифра\n // из введенных\n [styles.caretRight]:\n (index === caretPos ||\n (index === digitsCount - 1 && index === caretPos - 1)) &&\n index === value.length - 1 &&\n focused,\n })}\n data-qa={`magritte-pincode-input-digit-${index}`}\n >\n {value[index] || (caretPos === index && focused ? '' : '•')}\n </span>\n </div>\n ))}\n </div>\n <div className={styles.ghostValue} ref={ghostLastDigitRef}>\n {value.slice(caretPos, caretPos + 1)}\n </div>\n </div>\n <FormHelper\n invalid={invalid}\n description={description}\n errorMessage={errorMessage}\n data-qa=\"magritte-pincode-input-message\"\n />\n </div>\n );\n }\n);\n\nPincodeInput.displayName = 'PincodeInput';\n"],"names":["_jsxs","_jsx"],"mappings":";;;;;;;;;;AA2CA,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;MAElF,YAAY,GAAG,UAAU,CAClC,CACI,EACI,WAAW,EAAE,YAAY,GAAG,IAAI,EAChC,IAAI,GAAG,OAAO,EACd,OAAO,GAAG,KAAK,EACf,OAAO,EACP,MAAM,EACN,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,kBAAkB,GAAG,KAAK,EAC1B,SAAS,EAAE,MAAM,GAAG,wBAAwB,GAC/C,EACD,GAAG,KACH;AACA,IAAA,MAAM,WAAW,GAAG,YAAY,KAAK,IAAI,KAAK,QAAQ,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAS,EAAE,CAAC,CAAC;IAC/C,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAS,CAAC,CAAC,CAAC;IAC9D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;AAE5C,IAAA,MAAM,iBAAiB,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;AACvD,IAAA,MAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;AAClD,IAAA,MAAM,QAAQ,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAC;AAChD,IAAA,MAAM,gBAAgB,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;AAEpC,IAAA,MAAM,WAAW,GAAG,CAAC,QAAgB,KAAI;QACrC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACnB,QAAA,QAAQ,GAAG,QAAQ,CAAC,CAAC;AACzB,KAAC,CAAC;AAEF,IAAA,MAAM,CAAC,CAAC,OAAO,KAAI;QACf,IAAI,CAAC,OAAO,EAAE;YACV,OAAO;SACV;QACD,WAAW,CAAC,OAAO,CAAC,CAAC;AACrB,QAAA,gBAAgB,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACrC,WAAW,CAAC,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC;KACjC,EAAE,kBAAkB,CAAC,CAAC;;;IAIvB,MAAM,mBAAmB,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,OAAO,KAAI;AAC9D,QAAA,IAAI,OAAO,IAAI,OAAO,EAAE;YACpB,OAAO,CAAC,KAAK,EAAE,CAAC;SACnB;AACL,KAAC,CAAC,CAAC;IAEH,eAAe,CAAC,MAAK;QACjB,IAAI,CAAC,YAAY,CAAC,OAAO,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE;YACrD,OAAO;SACV;QAED,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC,KAAK,CAAC;AAC1E,QAAA,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,qCAAqC,EAAE,CAAA,EAAG,SAAS,CAAA,EAAA,CAAI,CAAC,CAAC;AACpG,KAAC,EAAE,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC;AAE3B,IAAA,MAAM,QAAQ,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;AAEvD,IAAA,QACIA,IAAK,CAAA,KAAA,EAAA,EAAA,GAAG,EAAE,GAAG,aAAW,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,EAAA,QAAA,EAAA,CACjFA,IACY,CAAA,KAAA,EAAA,EAAA,SAAA,EAAA,gCAAgC,EACxC,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,UAAU,CAAC,MAAM,CAAC,YAAY,EAAE;AACvC,oBAAA,CAAC,MAAM,CAAC,KAAK,GAAG,OAAO;AACvB,oBAAA,CAAC,MAAM,CAAC,YAAY,GAAG,YAAY;AACnC,oBAAA,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,KAAK,QAAQ;AAClC,oBAAA,CAAC,MAAM,CAAC,OAAO,GAAG,OAAO;AAC5B,iBAAA,CAAC,EACF,WAAW,EAAE,CAAC,KAAK,KAAI;oBACnB,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,OAAO,EAAE;wBACnC,OAAO;qBACV;oBACD,KAAK,CAAC,cAAc,EAAE,CAAC;oBAEvB,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAC1B,MAAM,CAAE,KAAK,CAAC,MAAsB,EAAE,OAAO,EAAE,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC,EAC1E,KAAK,CAAC,MAAM,CACf,CAAC;oBAEF,IAAI,CAAC,OAAO,EAAE;AACV,wBAAA,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;qBAC7B;oBAED,gBAAgB,CAAC,aAAa,CAAC,CAAC;iBACnC,EACD,KAAK,EACD;AACI,oBAAA,iCAAiC,EAAE,WAAW;AAC9C,oBAAA,mCAAmC,EAAE,QAAQ;AAC/B,iBAAA,EAAA,QAAA,EAAA,CAGtBC,eAEI,GAAG,EAAE,mBAAmB,EACxB,QAAQ,EAAE,CAAC,CAAC,EACZ,YAAY,EAAC,eAAe,EAC5B,OAAO,EAAE,OAAO,WAAW,CAAA,CAAA,CAAG,EACtB,SAAA,EAAA,8BAA8B,EACtC,SAAS,EAAE,MAAM,CAAC,WAAW,EAC7B,IAAI,EAAC,MAAM,EACX,SAAS,EAAC,SAAS,EACnB,SAAS,EAAE,WAAW;;;wBAGtB,KAAK,EAAC,EAAE,EACR,QAAQ,EAAE,CAAC,KAAK,KAAI;AAChB,4BAAA,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;gCAChC,OAAO;6BACV;;;;;;;4BAQD,gBAAgB,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC;4BAE/C,IAAI,gBAAgB,CAAC,OAAO,CAAC,MAAM,IAAI,WAAW,EAAE;AAChD,gCAAA,WAAW,CAAC,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC;AAC5D,gCAAA,gBAAgB,CAAC,OAAO,GAAG,EAAE,CAAC;AAC9B,gCAAA,gBAAgB,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;;;;;;;;gCASlC,WAAW,CAAC,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC;6BACjC;AACL,yBAAC,EACD,OAAO,EAAE,CAAC,KAAK,KAAI;4BACf,IAAI,OAAO,EAAE;gCACT,OAAO;6BACV;AACD,4BAAA,OAAO,GAAG,KAAK,CAAC,CAAC;AACjB,4BAAA,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE;gCACzB,UAAU,CAAC,IAAI,CAAC,CAAC;AACjB,gCAAA,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC;gCACzE,IAAI,OAAO,EAAE;;oCAET,WAAW,CAAC,EAAE,CAAC,CAAC;oCAChB,gBAAgB,CAAC,CAAC,CAAC,CAAC;iCACvB;6BACJ;AACL,yBAAC,EACD,MAAM,EAAE,CAAC,KAAK,KAAI;AACd,4BAAA,MAAM,GAAG,KAAK,CAAC,CAAC;AAChB,4BAAA,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE;gCACzB,UAAU,CAAC,KAAK,CAAC,CAAC;gCAClB,eAAe,CAAC,KAAK,CAAC,CAAC;6BAC1B;AACL,yBAAC,EACD,SAAS,EAAE,CAAC,KAAK,KAAI;AACjB,4BAAA,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE;gCACnB,OAAO;6BACV;4BAED,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;4BAC5D,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;AAC9D,4BAAA,IAAI,MAAM,IAAI,OAAO,EAAE;gCACnB,KAAK,CAAC,cAAc,EAAE,CAAC;AACvB,gCAAA,gBAAgB,CAAC,CAAC,aAAa,KAC3B,KAAK,CAAC,aAAa,IAAI,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAC7D,CAAC;gCACF,OAAO;6BACV;4BAED,IAAI,eAAe,CAAC,KAAK,EAAE,YAAY,CAAC,SAAS,CAAC,EAAE;gCAChD,WAAW,CAAC,CAAG,EAAA,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAG,EAAA,KAAK,CAAC,GAAG,CAAA,EAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAE,CAAA,CAAC,CAAC;AACnF,gCAAA,gBAAgB,CAAC,CAAC,aAAa,KAAK,KAAK,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC;gCAClF,KAAK,CAAC,cAAc,EAAE,CAAC;gCACvB,OAAO;6BACV;4BAED,MAAM,WAAW,GAAG,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;4BACjE,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;AAE3D,4BAAA,IAAI,WAAW,IAAI,QAAQ,EAAE;;;;;;gCAOzB,MAAM,WAAW,GAAG,QAAQ,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;gCAClD,MAAM,WAAW,GAAG,KAAK,CACrB,QAAQ,IAAI,QAAQ,IAAI,WAAW,GAAG,CAAC,GAAG,CAAC,CAAC,EAC5C,CAAC,EACD,WAAW,GAAG,CAAC,CAClB,CAAC;gCACF,WAAW,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC,CAAE,CAAA,CAAC,CAAC;AAC7E,gCAAA,gBAAgB,CAAC,CAAC,aAAa,KAAI;oCAC/B,MAAM,WAAW,GACb,WAAW,IAAI,QAAQ,GAAG,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AAE7E,oCAAA,OAAO,WAAW,CAAC;AACvB,iCAAC,CAAC,CAAC;gCAEH,KAAK,CAAC,cAAc,EAAE,CAAC;6BAC1B;AACL,yBAAC,EACD,OAAO,EAAE,CAAC,KAAK,KAAI;4BACf,KAAK,CAAC,cAAc,EAAE,CAAC;AACvB,4BAAA,MAAM,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC1E,4BAAA,IAAI,UAAU,CAAC,MAAM,KAAK,WAAW,EAAE;gCACnC,WAAW,CAAC,UAAU,CAAC,CAAC;AACxB,gCAAA,gBAAgB,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;gCAClC,OAAO;6BACV;AAED,4BAAA,MAAM,QAAQ,GACV,CAAG,EAAA,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAG,EAAA,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,CAAA,CAAE,CAAC,KAAK,CACxF,CAAC,EACD,WAAW,CACd,CAAC;4BACN,WAAW,CAAC,QAAQ,CAAC,CAAC;AACtB,4BAAA,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;yBAC7E,EAAA,EAhII,QAAQ,CAiIf,EACFA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,MAAM,CAAC,iBAAiB,EAEnC,QAAA,EAAA,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,MAC9CA,GAAA,CAAA,KAAA,EAAA,EAAiB,SAAS,EAAE,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAoB,kBAAA,EAAA,KAAK,EAC5E,QAAA,EAAAA,GAAA,CAAA,MAAA,EAAA,EACI,SAAS,EAAE,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE;;oCAEzC,CAAC,MAAM,CAAC,WAAW,GACf,KAAK,IAAI,KAAK,CAAC,MAAM;AACrB,yCAAC,CAAC,OAAO;6CACJ,KAAK,KAAK,QAAQ;AACf,gDAAA,EAAE,QAAQ,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC;;;AAGtE,oCAAA,CAAC,MAAM,CAAC,SAAS,GAAG,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO;;;;;oCAK/E,CAAC,MAAM,CAAC,UAAU,GACd,CAAC,KAAK,KAAK,QAAQ;AACf,yCAAC,KAAK,KAAK,WAAW,GAAG,CAAC,IAAI,KAAK,KAAK,QAAQ,GAAG,CAAC,CAAC;AACzD,wCAAA,KAAK,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC;wCAC1B,OAAO;AACd,iCAAA,CAAC,aACO,CAAgC,6BAAA,EAAA,KAAK,CAAE,CAAA,EAAA,QAAA,EAE/C,KAAK,CAAC,KAAK,CAAC,KAAK,QAAQ,KAAK,KAAK,IAAI,OAAO,GAAG,EAAE,GAAG,GAAG,CAAC,GACxD,EAzBD,EAAA,KAAK,CA0BT,CACT,CAAC,EACA,CAAA,EACNA,GAAK,CAAA,KAAA,EAAA,EAAA,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,EAAE,iBAAiB,EACpD,QAAA,EAAA,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,GAAG,CAAC,CAAC,EAAA,CAClC,IACJ,EACNA,GAAA,CAAC,UAAU,EAAA,EACP,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,YAAY,EAClB,SAAA,EAAA,gCAAgC,EAC1C,CAAA,CAAA,EAAA,CACA,EACR;AACN,CAAC,EACH;AAEF,YAAY,CAAC,WAAW,GAAG,cAAc;;;;"}
|
package/index.css
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
--magritte-color-component-input-stroke-state-field-focused-v21-1-0:#0070ff;
|
|
5
5
|
--magritte-color-component-input-stroke-state-accent-focused-accessible-v21-1-0:#0070ff7a;
|
|
6
6
|
--magritte-color-component-input-stroke-state-field-invalid-v21-1-0:#ff4d3a;
|
|
7
|
+
--magritte-color-component-input-stroke-state-negative-focused-accessible-v21-1-0:#ff4d3a7a;
|
|
7
8
|
--magritte-color-component-input-background-field-v21-1-0:#ffffff;
|
|
8
9
|
--magritte-color-component-input-background-state-field-hovered-v21-1-0:#ffffff;
|
|
9
10
|
--magritte-color-component-input-background-state-field-focused-v21-1-0:#ffffff;
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
--magritte-color-component-input-stroke-state-field-focused-v21-1-0:#0070ff;
|
|
29
30
|
--magritte-color-component-input-stroke-state-accent-focused-accessible-v21-1-0:#0070ff7a;
|
|
30
31
|
--magritte-color-component-input-stroke-state-field-invalid-v21-1-0:#ff4d3a;
|
|
32
|
+
--magritte-color-component-input-stroke-state-negative-focused-accessible-v21-1-0:#ff4d3a7a;
|
|
31
33
|
--magritte-color-component-input-background-field-v21-1-0:#1B1B1B;
|
|
32
34
|
--magritte-color-component-input-background-state-field-hovered-v21-1-0:#1B1B1B;
|
|
33
35
|
--magritte-color-component-input-background-state-field-focused-v21-1-0:#1B1B1B;
|
|
@@ -39,17 +41,18 @@
|
|
|
39
41
|
--magritte-color-component-input-text-state-placeholder-focused-v21-1-0:#535353;
|
|
40
42
|
--magritte-color-component-input-text-state-placeholder-invalid-v21-1-0:#535353;
|
|
41
43
|
}
|
|
42
|
-
.magritte-pincode-input___RpWyq_1-0
|
|
44
|
+
.magritte-pincode-input___RpWyq_1-1-0{
|
|
43
45
|
--magritte-pincode-input-padding-horizontal:20px;
|
|
44
46
|
--magritte-pincode-input-padding-vertical:8px;
|
|
45
47
|
--magritte-pincode-input-gap:6px;
|
|
48
|
+
--magritte-digit-box-width:calc((100% - (var(--magritte-pincode-input-gap) * (var(--magritte-pincode-digits-count) - 1)) - (var(--magritte-pincode-input-padding-horizontal) * 2)) / var(--magritte-pincode-digits-count));
|
|
46
49
|
box-sizing:border-box;
|
|
47
50
|
height:88px;
|
|
48
|
-
|
|
51
|
+
box-shadow:inset 0 0 0 var(--magritte-semantic-border-width-default-v21-1-0) var(--magritte-color-component-input-stroke-field-v21-1-0);
|
|
49
52
|
border-radius:var(--magritte-static-border-radius-450-v21-1-0);
|
|
50
53
|
background-color:var(--magritte-color-component-input-background-field-v21-1-0);
|
|
51
|
-
padding:var(--magritte-pincode-input-padding-vertical) var(--magritte-pincode-input-padding-horizontal);
|
|
52
54
|
position:relative;
|
|
55
|
+
overflow:clip;
|
|
53
56
|
cursor:text;
|
|
54
57
|
font-family:var(--magritte-font-families-body-v21-1-0);
|
|
55
58
|
font-size:32px;
|
|
@@ -58,7 +61,7 @@
|
|
|
58
61
|
line-height:40px;
|
|
59
62
|
color:var(--magritte-color-component-input-text-state-content-focused-v21-1-0);
|
|
60
63
|
}
|
|
61
|
-
.magritte-pincode-input___RpWyq_1-0
|
|
64
|
+
.magritte-pincode-input___RpWyq_1-1-0.magritte-medium___wfRSc_1-1-0{
|
|
62
65
|
font-size:28px;
|
|
63
66
|
--magritte-pincode-input-padding-horizontal:16px;
|
|
64
67
|
--magritte-pincode-input-padding-vertical:6px;
|
|
@@ -66,78 +69,102 @@
|
|
|
66
69
|
border-radius:var(--magritte-static-border-radius-300-v21-1-0);
|
|
67
70
|
height:76px;
|
|
68
71
|
}
|
|
69
|
-
.magritte-pincode-input___RpWyq_1-0
|
|
70
|
-
|
|
72
|
+
.magritte-pincode-input___RpWyq_1-1-0:hover{
|
|
73
|
+
box-shadow:inset 0 0 0 var(--magritte-semantic-border-width-default-v21-1-0) var(--magritte-color-component-input-stroke-state-field-hovered-v21-1-0);
|
|
71
74
|
background-color:var(--magritte-color-component-input-background-state-field-hovered-v21-1-0);
|
|
72
75
|
}
|
|
73
|
-
.magritte-pincode-input___RpWyq_1-0
|
|
76
|
+
.magritte-pincode-input___RpWyq_1-1-0:hover .magritte-placeholder___10kQ0_1-1-0{
|
|
74
77
|
color:var(--magritte-color-component-input-text-state-placeholder-hovered-v21-1-0);
|
|
75
78
|
}
|
|
76
|
-
.magritte-pincode-input___RpWyq_1-0
|
|
77
|
-
|
|
79
|
+
.magritte-pincode-input___RpWyq_1-1-0.magritte-focus___IuhXb_1-1-0{
|
|
80
|
+
box-shadow:inset 0 0 0 var(--magritte-semantic-border-width-focused-v21-1-0) var(--magritte-color-component-input-stroke-state-field-focused-v21-1-0);
|
|
78
81
|
background-color:var(--magritte-color-component-input-background-state-field-focused-v21-1-0);
|
|
79
|
-
padding:var(--magritte-pincode-input-padding-vertical) calc(var(--magritte-pincode-input-padding-horizontal) - 1px);
|
|
80
82
|
}
|
|
81
|
-
.magritte-pincode-input___RpWyq_1-0
|
|
83
|
+
.magritte-pincode-input___RpWyq_1-1-0.magritte-focus___IuhXb_1-1-0 .magritte-placeholder___10kQ0_1-1-0{
|
|
82
84
|
color:var(--magritte-color-component-input-text-state-placeholder-focused-v21-1-0);
|
|
83
85
|
}
|
|
84
|
-
.magritte-pincode-input___RpWyq_1-0
|
|
85
|
-
|
|
86
|
+
.magritte-pincode-input___RpWyq_1-1-0.magritte-invalid___hB8h2_1-1-0{
|
|
87
|
+
box-shadow:inset 0 0 0 var(--magritte-semantic-border-width-default-v21-1-0) var(--magritte-color-component-input-stroke-state-field-invalid-v21-1-0);
|
|
86
88
|
background-color:var(--magritte-color-component-input-background-state-field-invalid-v21-1-0);
|
|
87
89
|
}
|
|
88
|
-
.magritte-pincode-input___RpWyq_1-0
|
|
90
|
+
.magritte-pincode-input___RpWyq_1-1-0.magritte-invalid___hB8h2_1-1-0 .magritte-placeholder___10kQ0_1-1-0{
|
|
89
91
|
color:var(--magritte-color-component-input-text-state-placeholder-invalid-v21-1-0);
|
|
90
92
|
}
|
|
91
|
-
.magritte-pincode-input___RpWyq_1-0
|
|
93
|
+
.magritte-pincode-input___RpWyq_1-1-0.magritte-focus-visible___BN1eD_1-1-0{
|
|
92
94
|
outline:var(--magritte-color-component-input-stroke-state-accent-focused-accessible-v21-1-0) solid 4px;
|
|
93
95
|
}
|
|
94
|
-
.magritte-
|
|
96
|
+
.magritte-pincode-input___RpWyq_1-1-0.magritte-focus-visible___BN1eD_1-1-0.magritte-invalid___hB8h2_1-1-0{
|
|
97
|
+
outline-color:var(--magritte-color-component-input-stroke-state-negative-focused-accessible-v21-1-0);
|
|
98
|
+
}
|
|
99
|
+
.magritte-digit-box-container___BqMWw_1-1-0{
|
|
95
100
|
display:flex;
|
|
96
101
|
gap:var(--magritte-pincode-input-gap);
|
|
97
|
-
position:
|
|
98
|
-
|
|
102
|
+
position:absolute;
|
|
103
|
+
inset:0;
|
|
104
|
+
justify-content:center;
|
|
99
105
|
align-items:stretch;
|
|
100
106
|
height:100%;
|
|
107
|
+
pointer-events:none;
|
|
108
|
+
z-index:0;
|
|
101
109
|
}
|
|
102
|
-
.magritte-digit-box___6J0Q2_1-0
|
|
103
|
-
flex:0 0
|
|
110
|
+
.magritte-digit-box___6J0Q2_1-1-0{
|
|
111
|
+
flex:0 0 var(--magritte-digit-box-width);
|
|
112
|
+
pointer-events:auto;
|
|
104
113
|
display:flex;
|
|
105
114
|
align-items:center;
|
|
106
115
|
justify-content:center;
|
|
116
|
+
user-select:none;
|
|
117
|
+
}
|
|
118
|
+
.magritte-digit-box___6J0Q2_1-1-0::selection{
|
|
119
|
+
background-color:transparent;
|
|
107
120
|
}
|
|
108
|
-
.magritte-
|
|
121
|
+
.magritte-digit-box___6J0Q2_1-1-0::-moz-selection{
|
|
122
|
+
background-color:transparent;
|
|
123
|
+
}
|
|
124
|
+
.magritte-digits-input___4QeLj_1-1-0{
|
|
109
125
|
display:block;
|
|
110
126
|
position:absolute;
|
|
111
|
-
|
|
127
|
+
top:0;
|
|
128
|
+
bottom:0;
|
|
129
|
+
--last-digit-width:max(var(--magritte-pincode-last-digit-width), 10px);
|
|
130
|
+
padding:0 calc(var(--last-digit-width) / 2);
|
|
131
|
+
left:calc(var(--magritte-pincode-input-padding-horizontal) + (var(--magritte-pincode-caret-position) * var(--magritte-digit-box-width)) + (var(--magritte-pincode-caret-position) * var(--magritte-pincode-input-gap)) + (var(--magritte-digit-box-width) / 2) - var(--last-digit-width) / 2);
|
|
132
|
+
height:100%;
|
|
133
|
+
width:var(--last-digit-width);
|
|
134
|
+
z-index:1;
|
|
112
135
|
border-width:0;
|
|
113
136
|
background:none;
|
|
114
|
-
|
|
137
|
+
color:transparent;
|
|
115
138
|
outline:none;
|
|
116
139
|
appearance:none;
|
|
117
140
|
caret-color:transparent;
|
|
118
|
-
z-index:0;
|
|
119
|
-
pointer-events:none;
|
|
120
141
|
}
|
|
121
|
-
.magritte-digits-input___4QeLj_1-0
|
|
142
|
+
.magritte-digits-input___4QeLj_1-1-0::selection{
|
|
122
143
|
background-color:transparent;
|
|
144
|
+
appearance:none;
|
|
123
145
|
}
|
|
124
|
-
.magritte-digits-input___4QeLj_1-0
|
|
146
|
+
.magritte-digits-input___4QeLj_1-1-0::-moz-selection{
|
|
125
147
|
background-color:transparent;
|
|
148
|
+
appearance:none;
|
|
126
149
|
}
|
|
127
|
-
.magritte-caret-left___iZMuA_1-0
|
|
128
|
-
.magritte-caret-right___u8wNk_1-0
|
|
150
|
+
.magritte-caret-left___iZMuA_1-1-0::before,
|
|
151
|
+
.magritte-caret-right___u8wNk_1-1-0::after{
|
|
129
152
|
content:'';
|
|
130
153
|
block-size:1em;
|
|
131
154
|
border-left:1px solid var(--magritte-color-text-primary-v21-1-0);
|
|
132
|
-
animation:magritte-caret-blink___-bhg7_1-0
|
|
155
|
+
animation:magritte-caret-blink___-bhg7_1-1-0 1s steps(1) infinite;
|
|
133
156
|
}
|
|
134
|
-
.magritte-placeholder___10kQ0_1-0
|
|
157
|
+
.magritte-placeholder___10kQ0_1-1-0{
|
|
135
158
|
color:var(--magritte-color-component-input-text-placeholder-v21-1-0);
|
|
136
159
|
}
|
|
137
|
-
.magritte-value-container___9JR3e_1-0
|
|
160
|
+
.magritte-value-container___9JR3e_1-1-0{
|
|
138
161
|
pointer-events:none;
|
|
139
162
|
}
|
|
140
|
-
|
|
163
|
+
.magritte-ghost-value___jPQwY_1-1-0{
|
|
164
|
+
display:inline-block;
|
|
165
|
+
visibility:hidden;
|
|
166
|
+
}
|
|
167
|
+
@keyframes magritte-caret-blink___-bhg7_1-1-0{
|
|
141
168
|
50%{
|
|
142
169
|
opacity:0;
|
|
143
170
|
}
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import './index.css';
|
|
2
2
|
export { PincodeInput } from './PincodeInput.js';
|
|
3
|
+
export * from '@hh.ru/magritte-ui-theme-provider';
|
|
3
4
|
import 'react/jsx-runtime';
|
|
4
5
|
import 'react';
|
|
5
6
|
import 'classnames';
|
|
6
7
|
import '@hh.ru/magritte-common-keyboard';
|
|
7
|
-
import '@hh.ru/magritte-common-
|
|
8
|
+
import '@hh.ru/magritte-common-use-multiple-refs';
|
|
8
9
|
import '@hh.ru/magritte-ui-form-helper';
|
|
10
|
+
import './useOTP.js';
|
|
9
11
|
//# sourceMappingURL=index.js.map
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hh.ru/magritte-ui-pincode-input",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"types": "index.d.ts",
|
|
6
6
|
"sideEffects": [
|
|
@@ -23,13 +23,14 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@hh.ru/magritte-common-keyboard": "4.0
|
|
27
|
-
"@hh.ru/magritte-common-
|
|
28
|
-
"@hh.ru/magritte-ui-form-helper": "1.0.22"
|
|
26
|
+
"@hh.ru/magritte-common-keyboard": "4.1.0",
|
|
27
|
+
"@hh.ru/magritte-common-use-multiple-refs": "1.1.5",
|
|
28
|
+
"@hh.ru/magritte-ui-form-helper": "1.0.22",
|
|
29
|
+
"@hh.ru/magritte-ui-theme-provider": "1.1.37"
|
|
29
30
|
},
|
|
30
31
|
"peerDependencies": {
|
|
31
32
|
"classnames": ">=2.3.2",
|
|
32
33
|
"react": ">=18.2.0"
|
|
33
34
|
},
|
|
34
|
-
"gitHead": "
|
|
35
|
+
"gitHead": "68b593ee06af120092b98cf659bd9800e496de23"
|
|
35
36
|
}
|
package/useOTP.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useOTP: (callback: (OTPValue: string | null) => void, requestCredentials?: boolean) => void;
|
package/useOTP.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import './index.css';
|
|
2
|
+
import { useRef, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
const useOTP = (callback, requestCredentials = true) => {
|
|
5
|
+
const callbackRef = useRef(callback);
|
|
6
|
+
callbackRef.current = callback;
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!('OTPCredential' in window) || !requestCredentials) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
const ac = new AbortController();
|
|
12
|
+
navigator.credentials
|
|
13
|
+
.get({
|
|
14
|
+
otp: { transport: ['sms'] },
|
|
15
|
+
signal: ac.signal,
|
|
16
|
+
})
|
|
17
|
+
// ts не знает о типе OTPCredentials
|
|
18
|
+
.then((otp) => callbackRef.current(otp?.code))
|
|
19
|
+
.catch(() => {
|
|
20
|
+
/* WebOTP fail, do nothing */
|
|
21
|
+
});
|
|
22
|
+
return () => {
|
|
23
|
+
ac.abort();
|
|
24
|
+
};
|
|
25
|
+
}, [requestCredentials]);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export { useOTP };
|
|
29
|
+
//# sourceMappingURL=useOTP.js.map
|
package/useOTP.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOTP.js","sources":["../src/useOTP.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\nexport const useOTP = (callback: (OTPValue: string | null) => void, requestCredentials: boolean = true): void => {\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n if (!('OTPCredential' in window) || !requestCredentials) {\n return undefined;\n }\n\n const ac = new AbortController();\n\n navigator.credentials\n .get({\n otp: { transport: ['sms'] },\n signal: ac.signal,\n } as CredentialRequestOptions)\n // ts не знает о типе OTPCredentials\n .then((otp) => callbackRef.current((otp as unknown as { code: string })?.code))\n .catch(() => {\n /* WebOTP fail, do nothing */\n });\n\n return () => {\n ac.abort();\n };\n }, [requestCredentials]);\n};\n"],"names":[],"mappings":";;AAEa,MAAA,MAAM,GAAG,CAAC,QAA2C,EAAE,kBAAA,GAA8B,IAAI,KAAU;AAC5G,IAAA,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;AACrC,IAAA,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IAE/B,SAAS,CAAC,MAAK;QACX,IAAI,EAAE,eAAe,IAAI,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE;AACrD,YAAA,OAAO,SAAS,CAAC;SACpB;AAED,QAAA,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,CAAC;AAEjC,QAAA,SAAS,CAAC,WAAW;AAChB,aAAA,GAAG,CAAC;AACD,YAAA,GAAG,EAAE,EAAE,SAAS,EAAE,CAAC,KAAK,CAAC,EAAE;YAC3B,MAAM,EAAE,EAAE,CAAC,MAAM;SACQ,CAAC;;AAE7B,aAAA,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,CAAC,OAAO,CAAE,GAAmC,EAAE,IAAI,CAAC,CAAC;aAC9E,KAAK,CAAC,MAAK;;AAEZ,SAAC,CAAC,CAAC;AAEP,QAAA,OAAO,MAAK;YACR,EAAE,CAAC,KAAK,EAAE,CAAC;AACf,SAAC,CAAC;AACN,KAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAC7B;;;;"}
|