@xsolla/xui-input-pin 0.64.0-pr56.1768440195

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/web/index.mjs ADDED
@@ -0,0 +1,688 @@
1
+ // src/InputPin.tsx
2
+ import { useState, useRef, useEffect, useCallback } from "react";
3
+
4
+ // ../primitives-web/src/Box.tsx
5
+ import React from "react";
6
+ import styled from "styled-components";
7
+ import { jsx } from "react/jsx-runtime";
8
+ var StyledBox = styled.div`
9
+ display: flex;
10
+ box-sizing: border-box;
11
+ background-color: ${(props) => props.backgroundColor || "transparent"};
12
+ border-color: ${(props) => props.borderColor || "transparent"};
13
+ border-width: ${(props) => typeof props.borderWidth === "number" ? `${props.borderWidth}px` : props.borderWidth || 0};
14
+
15
+ ${(props) => props.borderBottomWidth !== void 0 && `
16
+ border-bottom-width: ${typeof props.borderBottomWidth === "number" ? `${props.borderBottomWidth}px` : props.borderBottomWidth};
17
+ border-bottom-color: ${props.borderBottomColor || props.borderColor || "transparent"};
18
+ border-bottom-style: solid;
19
+ `}
20
+ ${(props) => props.borderTopWidth !== void 0 && `
21
+ border-top-width: ${typeof props.borderTopWidth === "number" ? `${props.borderTopWidth}px` : props.borderTopWidth};
22
+ border-top-color: ${props.borderTopColor || props.borderColor || "transparent"};
23
+ border-top-style: solid;
24
+ `}
25
+ ${(props) => props.borderLeftWidth !== void 0 && `
26
+ border-left-width: ${typeof props.borderLeftWidth === "number" ? `${props.borderLeftWidth}px` : props.borderLeftWidth};
27
+ border-left-color: ${props.borderLeftColor || props.borderColor || "transparent"};
28
+ border-left-style: solid;
29
+ `}
30
+ ${(props) => props.borderRightWidth !== void 0 && `
31
+ border-right-width: ${typeof props.borderRightWidth === "number" ? `${props.borderRightWidth}px` : props.borderRightWidth};
32
+ border-right-color: ${props.borderRightColor || props.borderColor || "transparent"};
33
+ border-right-style: solid;
34
+ `}
35
+
36
+ border-style: ${(props) => props.borderStyle || (props.borderWidth || props.borderBottomWidth || props.borderTopWidth || props.borderLeftWidth || props.borderRightWidth ? "solid" : "none")};
37
+ border-radius: ${(props) => typeof props.borderRadius === "number" ? `${props.borderRadius}px` : props.borderRadius || 0};
38
+ height: ${(props) => typeof props.height === "number" ? `${props.height}px` : props.height || "auto"};
39
+ width: ${(props) => typeof props.width === "number" ? `${props.width}px` : props.width || "auto"};
40
+ min-width: ${(props) => typeof props.minWidth === "number" ? `${props.minWidth}px` : props.minWidth || "auto"};
41
+ min-height: ${(props) => typeof props.minHeight === "number" ? `${props.minHeight}px` : props.minHeight || "auto"};
42
+
43
+ padding: ${(props) => typeof props.padding === "number" ? `${props.padding}px` : props.padding || 0};
44
+ ${(props) => props.paddingHorizontal && `
45
+ padding-left: ${typeof props.paddingHorizontal === "number" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};
46
+ padding-right: ${typeof props.paddingHorizontal === "number" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};
47
+ `}
48
+ ${(props) => props.paddingVertical && `
49
+ padding-top: ${typeof props.paddingVertical === "number" ? `${props.paddingVertical}px` : props.paddingVertical};
50
+ padding-bottom: ${typeof props.paddingVertical === "number" ? `${props.paddingVertical}px` : props.paddingVertical};
51
+ `}
52
+ ${(props) => props.paddingTop !== void 0 && `padding-top: ${typeof props.paddingTop === "number" ? `${props.paddingTop}px` : props.paddingTop};`}
53
+ ${(props) => props.paddingBottom !== void 0 && `padding-bottom: ${typeof props.paddingBottom === "number" ? `${props.paddingBottom}px` : props.paddingBottom};`}
54
+ ${(props) => props.paddingLeft !== void 0 && `padding-left: ${typeof props.paddingLeft === "number" ? `${props.paddingLeft}px` : props.paddingLeft};`}
55
+ ${(props) => props.paddingRight !== void 0 && `padding-right: ${typeof props.paddingRight === "number" ? `${props.paddingRight}px` : props.paddingRight};`}
56
+
57
+ margin: ${(props) => typeof props.margin === "number" ? `${props.margin}px` : props.margin || 0};
58
+ ${(props) => props.marginTop !== void 0 && `margin-top: ${typeof props.marginTop === "number" ? `${props.marginTop}px` : props.marginTop};`}
59
+ ${(props) => props.marginBottom !== void 0 && `margin-bottom: ${typeof props.marginBottom === "number" ? `${props.marginBottom}px` : props.marginBottom};`}
60
+ ${(props) => props.marginLeft !== void 0 && `margin-left: ${typeof props.marginLeft === "number" ? `${props.marginLeft}px` : props.marginLeft};`}
61
+ ${(props) => props.marginRight !== void 0 && `margin-right: ${typeof props.marginRight === "number" ? `${props.marginRight}px` : props.marginRight};`}
62
+
63
+ flex-direction: ${(props) => props.flexDirection || "column"};
64
+ flex-wrap: ${(props) => props.flexWrap || "nowrap"};
65
+ align-items: ${(props) => props.alignItems || "stretch"};
66
+ justify-content: ${(props) => props.justifyContent || "flex-start"};
67
+ cursor: ${(props) => props.cursor ? props.cursor : props.onClick || props.onPress ? "pointer" : "inherit"};
68
+ position: ${(props) => props.position || "static"};
69
+ top: ${(props) => typeof props.top === "number" ? `${props.top}px` : props.top};
70
+ bottom: ${(props) => typeof props.bottom === "number" ? `${props.bottom}px` : props.bottom};
71
+ left: ${(props) => typeof props.left === "number" ? `${props.left}px` : props.left};
72
+ right: ${(props) => typeof props.right === "number" ? `${props.right}px` : props.right};
73
+ flex: ${(props) => props.flex};
74
+ flex-shrink: ${(props) => props.flexShrink ?? 1};
75
+ gap: ${(props) => typeof props.gap === "number" ? `${props.gap}px` : props.gap || 0};
76
+ align-self: ${(props) => props.alignSelf || "auto"};
77
+ overflow: ${(props) => props.overflow || "visible"};
78
+ overflow-x: ${(props) => props.overflowX || "visible"};
79
+ overflow-y: ${(props) => props.overflowY || "visible"};
80
+ z-index: ${(props) => props.zIndex};
81
+ opacity: ${(props) => props.disabled ? 0.5 : 1};
82
+ pointer-events: ${(props) => props.disabled ? "none" : "auto"};
83
+
84
+ &:hover {
85
+ ${(props) => props.hoverStyle?.backgroundColor && `background-color: ${props.hoverStyle.backgroundColor};`}
86
+ ${(props) => props.hoverStyle?.borderColor && `border-color: ${props.hoverStyle.borderColor};`}
87
+ }
88
+
89
+ &:active {
90
+ ${(props) => props.pressStyle?.backgroundColor && `background-color: ${props.pressStyle.backgroundColor};`}
91
+ }
92
+ `;
93
+ var Box = React.forwardRef(
94
+ ({
95
+ children,
96
+ onPress,
97
+ onKeyDown,
98
+ onKeyUp,
99
+ role,
100
+ "aria-label": ariaLabel,
101
+ "aria-labelledby": ariaLabelledBy,
102
+ "aria-current": ariaCurrent,
103
+ "aria-disabled": ariaDisabled,
104
+ "aria-live": ariaLive,
105
+ "aria-busy": ariaBusy,
106
+ "aria-describedby": ariaDescribedBy,
107
+ "aria-expanded": ariaExpanded,
108
+ "aria-haspopup": ariaHasPopup,
109
+ "aria-pressed": ariaPressed,
110
+ "aria-controls": ariaControls,
111
+ tabIndex,
112
+ as,
113
+ src,
114
+ alt,
115
+ type,
116
+ disabled,
117
+ id,
118
+ ...props
119
+ }, ref) => {
120
+ if (as === "img" && src) {
121
+ return /* @__PURE__ */ jsx(
122
+ "img",
123
+ {
124
+ src,
125
+ alt: alt || "",
126
+ style: {
127
+ display: "block",
128
+ objectFit: "cover",
129
+ width: typeof props.width === "number" ? `${props.width}px` : props.width,
130
+ height: typeof props.height === "number" ? `${props.height}px` : props.height,
131
+ borderRadius: typeof props.borderRadius === "number" ? `${props.borderRadius}px` : props.borderRadius,
132
+ position: props.position,
133
+ top: typeof props.top === "number" ? `${props.top}px` : props.top,
134
+ left: typeof props.left === "number" ? `${props.left}px` : props.left,
135
+ right: typeof props.right === "number" ? `${props.right}px` : props.right,
136
+ bottom: typeof props.bottom === "number" ? `${props.bottom}px` : props.bottom
137
+ }
138
+ }
139
+ );
140
+ }
141
+ return /* @__PURE__ */ jsx(
142
+ StyledBox,
143
+ {
144
+ ref,
145
+ as,
146
+ id,
147
+ type: as === "button" ? type || "button" : void 0,
148
+ disabled: as === "button" ? disabled : void 0,
149
+ onClick: onPress,
150
+ onKeyDown,
151
+ onKeyUp,
152
+ role,
153
+ "aria-label": ariaLabel,
154
+ "aria-labelledby": ariaLabelledBy,
155
+ "aria-current": ariaCurrent,
156
+ "aria-disabled": ariaDisabled,
157
+ "aria-busy": ariaBusy,
158
+ "aria-describedby": ariaDescribedBy,
159
+ "aria-expanded": ariaExpanded,
160
+ "aria-haspopup": ariaHasPopup,
161
+ "aria-pressed": ariaPressed,
162
+ "aria-controls": ariaControls,
163
+ "aria-live": ariaLive,
164
+ tabIndex: tabIndex !== void 0 ? tabIndex : void 0,
165
+ ...props,
166
+ children
167
+ }
168
+ );
169
+ }
170
+ );
171
+ Box.displayName = "Box";
172
+
173
+ // ../primitives-web/src/Text.tsx
174
+ import styled2 from "styled-components";
175
+ import { jsx as jsx2 } from "react/jsx-runtime";
176
+ var StyledText = styled2.span`
177
+ color: ${(props) => props.color || "inherit"};
178
+ font-size: ${(props) => typeof props.fontSize === "number" ? `${props.fontSize}px` : props.fontSize || "inherit"};
179
+ font-weight: ${(props) => props.fontWeight || "normal"};
180
+ font-family: ${(props) => props.fontFamily || '"Pilat Wide Bold", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important'};
181
+ line-height: ${(props) => typeof props.lineHeight === "number" ? `${props.lineHeight}px` : props.lineHeight || "inherit"};
182
+ white-space: ${(props) => props.whiteSpace || "normal"};
183
+ text-align: ${(props) => props.textAlign || "inherit"};
184
+ text-decoration: ${(props) => props.textDecoration || "none"};
185
+ `;
186
+ var Text = ({
187
+ style,
188
+ className,
189
+ id,
190
+ role,
191
+ ...props
192
+ }) => {
193
+ return /* @__PURE__ */ jsx2(
194
+ StyledText,
195
+ {
196
+ ...props,
197
+ style,
198
+ className,
199
+ id,
200
+ role
201
+ }
202
+ );
203
+ };
204
+
205
+ // ../primitives-web/src/Spinner.tsx
206
+ import styled3, { keyframes } from "styled-components";
207
+ import { jsx as jsx3 } from "react/jsx-runtime";
208
+ var rotate = keyframes`
209
+ from {
210
+ transform: rotate(0deg);
211
+ }
212
+ to {
213
+ transform: rotate(360deg);
214
+ }
215
+ `;
216
+ var StyledSpinner = styled3.div`
217
+ width: ${(props) => typeof props.size === "number" ? `${props.size}px` : props.size || "24px"};
218
+ height: ${(props) => typeof props.size === "number" ? `${props.size}px` : props.size || "24px"};
219
+ border: ${(props) => props.strokeWidth || 2}px solid
220
+ ${(props) => props.color || "currentColor"};
221
+ border-bottom-color: transparent;
222
+ border-radius: 50%;
223
+ display: inline-block;
224
+ box-sizing: border-box;
225
+ animation: ${rotate} 1s linear infinite;
226
+ `;
227
+ var Spinner = ({
228
+ role = "status",
229
+ "aria-label": ariaLabel,
230
+ "aria-live": ariaLive = "polite",
231
+ "aria-describedby": ariaDescribedBy,
232
+ testID,
233
+ ...props
234
+ }) => {
235
+ return /* @__PURE__ */ jsx3(
236
+ StyledSpinner,
237
+ {
238
+ role,
239
+ "aria-label": ariaLabel,
240
+ "aria-live": ariaLive,
241
+ "aria-describedby": ariaDescribedBy,
242
+ "data-testid": testID,
243
+ ...props
244
+ }
245
+ );
246
+ };
247
+ Spinner.displayName = "Spinner";
248
+
249
+ // ../primitives-web/src/Icon.tsx
250
+ import styled4 from "styled-components";
251
+ import { jsx as jsx4 } from "react/jsx-runtime";
252
+ var StyledIcon = styled4.div`
253
+ display: flex;
254
+ align-items: center;
255
+ justify-content: center;
256
+ width: ${(props) => typeof props.size === "number" ? `${props.size}px` : props.size || "24px"};
257
+ height: ${(props) => typeof props.size === "number" ? `${props.size}px` : props.size || "24px"};
258
+ color: ${(props) => props.color || "currentColor"};
259
+
260
+ svg {
261
+ width: 100%;
262
+ height: 100%;
263
+ fill: none;
264
+ stroke: currentColor;
265
+ }
266
+ `;
267
+
268
+ // ../primitives-web/src/Divider.tsx
269
+ import styled5 from "styled-components";
270
+ import { jsx as jsx5 } from "react/jsx-runtime";
271
+ var StyledDivider = styled5.div`
272
+ background-color: ${(props) => props.dashStroke ? "transparent" : props.color || "rgba(255, 255, 255, 0.15)"};
273
+ width: ${(props) => props.vertical ? typeof props.width === "number" ? `${props.width}px` : props.width || "1px" : "100%"};
274
+ height: ${(props) => props.vertical ? "100%" : typeof props.height === "number" ? `${props.height}px` : props.height || "1px"};
275
+
276
+ ${(props) => props.dashStroke && `
277
+ border-style: dashed;
278
+ border-color: ${props.color || "rgba(255, 255, 255, 0.15)"};
279
+ border-width: 0;
280
+ ${props.vertical ? `
281
+ border-left-width: ${typeof props.width === "number" ? `${props.width}px` : props.width || "1px"};
282
+ height: 100%;
283
+ ` : `
284
+ border-top-width: ${typeof props.height === "number" ? `${props.height}px` : props.height || "1px"};
285
+ width: 100%;
286
+ `}
287
+ `}
288
+ `;
289
+
290
+ // ../primitives-web/src/Input.tsx
291
+ import { forwardRef } from "react";
292
+ import styled6 from "styled-components";
293
+ import { jsx as jsx6 } from "react/jsx-runtime";
294
+ var StyledInput = styled6.input`
295
+ background: transparent;
296
+ border: none;
297
+ outline: none;
298
+ width: 100%;
299
+ height: 100%;
300
+ padding: 0;
301
+ margin: 0;
302
+ color: ${(props) => props.color || "inherit"};
303
+ font-size: ${(props) => typeof props.fontSize === "number" ? `${props.fontSize}px` : props.fontSize || "inherit"};
304
+ font-family: inherit;
305
+ text-align: inherit;
306
+
307
+ &::placeholder {
308
+ color: ${(props) => props.placeholderTextColor || "rgba(255, 255, 255, 0.5)"};
309
+ }
310
+
311
+ &:disabled {
312
+ cursor: not-allowed;
313
+ }
314
+ `;
315
+ var InputPrimitive = forwardRef(
316
+ ({
317
+ value,
318
+ placeholder,
319
+ onChange,
320
+ onChangeText,
321
+ onFocus,
322
+ onBlur,
323
+ onKeyDown,
324
+ disabled,
325
+ secureTextEntry,
326
+ style,
327
+ color,
328
+ fontSize,
329
+ placeholderTextColor,
330
+ maxLength,
331
+ name,
332
+ type,
333
+ inputMode,
334
+ autoComplete,
335
+ id,
336
+ "aria-invalid": ariaInvalid,
337
+ "aria-describedby": ariaDescribedBy,
338
+ "aria-labelledby": ariaLabelledBy,
339
+ "aria-label": ariaLabel,
340
+ "aria-disabled": ariaDisabled,
341
+ "data-testid": dataTestId,
342
+ ...rest
343
+ }, ref) => {
344
+ const handleChange = (e) => {
345
+ if (onChange) {
346
+ onChange(e);
347
+ }
348
+ if (onChangeText) {
349
+ onChangeText(e.target.value);
350
+ }
351
+ };
352
+ const inputValue = value !== void 0 ? value : "";
353
+ return /* @__PURE__ */ jsx6(
354
+ StyledInput,
355
+ {
356
+ ref,
357
+ id,
358
+ value: inputValue,
359
+ name,
360
+ placeholder,
361
+ onChange: handleChange,
362
+ onFocus,
363
+ onBlur,
364
+ onKeyDown,
365
+ disabled,
366
+ type: secureTextEntry ? "password" : type || "text",
367
+ inputMode,
368
+ autoComplete,
369
+ style,
370
+ color,
371
+ fontSize,
372
+ placeholderTextColor,
373
+ maxLength,
374
+ "aria-invalid": ariaInvalid,
375
+ "aria-describedby": ariaDescribedBy,
376
+ "aria-labelledby": ariaLabelledBy,
377
+ "aria-label": ariaLabel,
378
+ "aria-disabled": ariaDisabled,
379
+ "data-testid": dataTestId,
380
+ ...rest
381
+ }
382
+ );
383
+ }
384
+ );
385
+ InputPrimitive.displayName = "InputPrimitive";
386
+
387
+ // ../primitives-web/src/TextArea.tsx
388
+ import { forwardRef as forwardRef2 } from "react";
389
+ import styled7 from "styled-components";
390
+ import { jsx as jsx7 } from "react/jsx-runtime";
391
+ var StyledTextArea = styled7.textarea`
392
+ background: transparent;
393
+ border: none;
394
+ outline: none;
395
+ width: 100%;
396
+ height: 100%;
397
+ padding: 0;
398
+ margin: 0;
399
+ color: ${(props) => props.color || "inherit"};
400
+ font-size: ${(props) => typeof props.fontSize === "number" ? `${props.fontSize}px` : props.fontSize || "inherit"};
401
+ font-family: inherit;
402
+ text-align: inherit;
403
+ resize: none;
404
+
405
+ &::placeholder {
406
+ color: ${(props) => props.placeholderTextColor || "rgba(255, 255, 255, 0.5)"};
407
+ }
408
+
409
+ &:disabled {
410
+ cursor: not-allowed;
411
+ }
412
+ `;
413
+ var TextAreaPrimitive = forwardRef2(
414
+ ({
415
+ value,
416
+ placeholder,
417
+ onChangeText,
418
+ onFocus,
419
+ onBlur,
420
+ onKeyDown,
421
+ disabled,
422
+ style,
423
+ color,
424
+ fontSize,
425
+ placeholderTextColor,
426
+ maxLength,
427
+ rows
428
+ }, ref) => {
429
+ return /* @__PURE__ */ jsx7(
430
+ StyledTextArea,
431
+ {
432
+ ref,
433
+ value,
434
+ placeholder,
435
+ onChange: (e) => onChangeText?.(e.target.value),
436
+ onFocus,
437
+ onBlur,
438
+ onKeyDown,
439
+ disabled,
440
+ style,
441
+ color,
442
+ fontSize,
443
+ placeholderTextColor,
444
+ maxLength,
445
+ rows
446
+ }
447
+ );
448
+ }
449
+ );
450
+ TextAreaPrimitive.displayName = "TextAreaPrimitive";
451
+
452
+ // src/InputPin.tsx
453
+ import { useDesignSystem, useId } from "@xsolla/xui-core";
454
+ import { jsx as jsx8, jsxs } from "react/jsx-runtime";
455
+ var InputPin = ({
456
+ value = "",
457
+ onChange,
458
+ onComplete,
459
+ codeLength,
460
+ length = 4,
461
+ size = "m",
462
+ state: externalState,
463
+ disabled: disabledProp,
464
+ error: errorProp,
465
+ label,
466
+ errorMessage,
467
+ errorLabel,
468
+ secureTextEntry = false,
469
+ showPlaceholderDots = true,
470
+ flexibleWidth = false,
471
+ isComplete: isCompleteProp,
472
+ currentFocus,
473
+ addRef,
474
+ testID,
475
+ "aria-label": ariaLabel
476
+ }) => {
477
+ const { theme } = useDesignSystem();
478
+ const uniqueId = useId();
479
+ const [focusedIndex, setFocusedIndex] = useState(null);
480
+ const [internalValue, setInternalValue] = useState(value);
481
+ const actualLength = codeLength ?? length;
482
+ const errorText = errorMessage ?? errorLabel;
483
+ const isError = errorProp ?? (!!errorText || externalState === "error");
484
+ const isDisabled = disabledProp ?? externalState === "disable";
485
+ const isHover = externalState === "hover";
486
+ const sizeStyles = theme.sizing.inputPin(size);
487
+ const inputColors = theme.colors.control.input;
488
+ const inputs = useRef([]);
489
+ useEffect(() => {
490
+ setInternalValue(value);
491
+ }, [value]);
492
+ useEffect(() => {
493
+ if (currentFocus !== void 0 && currentFocus >= 0 && currentFocus < actualLength) {
494
+ inputs.current[currentFocus]?.focus();
495
+ }
496
+ }, [currentFocus, actualLength]);
497
+ const checkIsComplete = useCallback(
498
+ (val) => val.length >= actualLength,
499
+ [actualLength]
500
+ );
501
+ const currentIsComplete = isCompleteProp ?? checkIsComplete(internalValue);
502
+ const labelId = `${uniqueId}-label`;
503
+ const errorId = `${uniqueId}-error`;
504
+ const handleTextChange = (text, index) => {
505
+ const sanitizedText = text.replace(/[^0-9a-zA-Z]/g, "");
506
+ if (sanitizedText.length > 1) {
507
+ const pastedData = sanitizedText.slice(0, actualLength - index);
508
+ const newValue2 = internalValue.split("");
509
+ while (newValue2.length < actualLength) {
510
+ newValue2.push("");
511
+ }
512
+ for (let i = 0; i < pastedData.length && index + i < actualLength; i++) {
513
+ newValue2[index + i] = pastedData[i];
514
+ }
515
+ const updatedValue2 = newValue2.slice(0, actualLength).join("");
516
+ setInternalValue(updatedValue2);
517
+ const complete2 = checkIsComplete(updatedValue2);
518
+ const changeProps2 = { isComplete: complete2, value: updatedValue2 };
519
+ onChange?.(changeProps2);
520
+ if (complete2) {
521
+ onComplete?.(changeProps2);
522
+ }
523
+ const nextIndex = Math.min(index + pastedData.length, actualLength - 1);
524
+ inputs.current[nextIndex]?.focus();
525
+ return;
526
+ }
527
+ const char = sanitizedText.slice(-1);
528
+ const newValue = internalValue.split("");
529
+ while (newValue.length < actualLength) {
530
+ newValue.push("");
531
+ }
532
+ newValue[index] = char;
533
+ const updatedValue = newValue.slice(0, actualLength).join("");
534
+ setInternalValue(updatedValue);
535
+ const complete = checkIsComplete(updatedValue);
536
+ const changeProps = { isComplete: complete, value: updatedValue };
537
+ onChange?.(changeProps);
538
+ if (complete) {
539
+ onComplete?.(changeProps);
540
+ }
541
+ if (char && index < actualLength - 1) {
542
+ inputs.current[index + 1]?.focus();
543
+ }
544
+ };
545
+ const handleKeyDown = (e, index) => {
546
+ if (e.key === "Backspace") {
547
+ if (!internalValue[index] && index > 0) {
548
+ inputs.current[index - 1]?.focus();
549
+ }
550
+ } else if (e.key === "ArrowLeft" && index > 0) {
551
+ inputs.current[index - 1]?.focus();
552
+ } else if (e.key === "ArrowRight" && index < actualLength - 1) {
553
+ inputs.current[index + 1]?.focus();
554
+ }
555
+ };
556
+ const handlePaste = (e) => {
557
+ e.preventDefault();
558
+ const pastedData = e.clipboardData.getData("text").replace(/[^0-9a-zA-Z]/g, "").slice(0, actualLength);
559
+ setInternalValue(pastedData);
560
+ const complete = checkIsComplete(pastedData);
561
+ const changeProps = { isComplete: complete, value: pastedData };
562
+ onChange?.(changeProps);
563
+ if (complete) {
564
+ onComplete?.(changeProps);
565
+ }
566
+ const nextIndex = Math.min(pastedData.length, actualLength - 1);
567
+ inputs.current[nextIndex]?.focus();
568
+ };
569
+ const handleRef = (index) => (element) => {
570
+ inputs.current[index] = element;
571
+ addRef?.(index)(element);
572
+ };
573
+ const pinItems = Array.from({ length: actualLength }).map((_, index) => {
574
+ const char = internalValue[index] || "";
575
+ const hasValue = !!char;
576
+ const isFocused = focusedIndex === index;
577
+ let backgroundColor = inputColors.bg;
578
+ let borderColor = inputColors.border;
579
+ if (isDisabled) {
580
+ backgroundColor = inputColors.bgDisable;
581
+ borderColor = inputColors.borderDisable;
582
+ } else if (isError) {
583
+ borderColor = theme.colors.border.alert;
584
+ backgroundColor = isFocused ? theme.colors.control.focus.bg : inputColors.bg;
585
+ } else if (isFocused) {
586
+ backgroundColor = theme.colors.control.focus.bg;
587
+ borderColor = theme.colors.content.brand.secondary;
588
+ } else if (isHover) {
589
+ backgroundColor = inputColors.bgHover;
590
+ borderColor = inputColors.borderHover;
591
+ } else {
592
+ backgroundColor = inputColors.bg;
593
+ borderColor = inputColors.border;
594
+ }
595
+ const textColor = isDisabled ? inputColors.textDisable : inputColors.text;
596
+ return /* @__PURE__ */ jsx8(
597
+ Box,
598
+ {
599
+ width: flexibleWidth ? void 0 : sizeStyles.size,
600
+ height: sizeStyles.size,
601
+ flex: flexibleWidth ? 1 : void 0,
602
+ backgroundColor,
603
+ borderColor,
604
+ borderWidth: sizeStyles.borderWidth,
605
+ borderRadius: sizeStyles.radius,
606
+ alignItems: "center",
607
+ justifyContent: "center",
608
+ hoverStyle: !isDisabled && !isFocused && !isError ? {
609
+ backgroundColor: inputColors.bgHover,
610
+ borderColor: inputColors.borderHover
611
+ } : void 0,
612
+ children: /* @__PURE__ */ jsx8(
613
+ InputPrimitive,
614
+ {
615
+ ref: addRef ? handleRef(index) : (el) => inputs.current[index] = el,
616
+ value: char,
617
+ placeholder: showPlaceholderDots && !isFocused ? "\u2022" : void 0,
618
+ onChangeText: (text) => handleTextChange(text, index),
619
+ onFocus: () => setFocusedIndex(index),
620
+ onBlur: () => setFocusedIndex(null),
621
+ onKeyDown: (e) => handleKeyDown(e, index),
622
+ disabled: isDisabled,
623
+ secureTextEntry,
624
+ color: textColor,
625
+ placeholderTextColor: inputColors.placeholder,
626
+ fontSize: sizeStyles.fontSize,
627
+ style: { textAlign: "center" },
628
+ maxLength: 1,
629
+ inputMode: "numeric",
630
+ autoComplete: "one-time-code",
631
+ "aria-label": ariaLabel ? `${ariaLabel} digit ${index + 1}` : `PIN digit ${index + 1}`,
632
+ "aria-invalid": isError,
633
+ "aria-describedby": isError && errorText ? errorId : void 0,
634
+ "data-testid": testID ? `${testID}-input-${index}` : `input-pin-input-${index}`
635
+ }
636
+ )
637
+ },
638
+ index
639
+ );
640
+ });
641
+ return /* @__PURE__ */ jsxs(
642
+ Box,
643
+ {
644
+ flexDirection: "column",
645
+ gap: 8,
646
+ "data-testid": testID || "input-pin",
647
+ role: "group",
648
+ "aria-labelledby": label ? labelId : void 0,
649
+ "aria-label": !label && ariaLabel ? ariaLabel : void 0,
650
+ children: [
651
+ label && /* @__PURE__ */ jsx8(
652
+ Text,
653
+ {
654
+ id: labelId,
655
+ color: theme.colors.content.secondary,
656
+ fontSize: sizeStyles.fontSize - 2,
657
+ fontWeight: "500",
658
+ children: label
659
+ }
660
+ ),
661
+ /* @__PURE__ */ jsx8(
662
+ Box,
663
+ {
664
+ flexDirection: "row",
665
+ gap: sizeStyles.gap,
666
+ onPaste: handlePaste,
667
+ width: flexibleWidth ? "100%" : void 0,
668
+ children: pinItems
669
+ }
670
+ ),
671
+ isError && errorText && /* @__PURE__ */ jsx8(
672
+ Text,
673
+ {
674
+ id: errorId,
675
+ role: "alert",
676
+ color: theme.colors.content.alert.primary,
677
+ fontSize: sizeStyles.fontSize - 2,
678
+ children: errorText
679
+ }
680
+ )
681
+ ]
682
+ }
683
+ );
684
+ };
685
+ export {
686
+ InputPin
687
+ };
688
+ //# sourceMappingURL=index.mjs.map