@xsolla/xui-input-payment 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,928 @@
1
+ // src/InputPayment.tsx
2
+ import React4, { useState, forwardRef as forwardRef3, useRef, useEffect } 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/InputPayment.tsx
453
+ import { useDesignSystem, useId, isWeb } from "@xsolla/xui-core";
454
+ import {
455
+ Visa,
456
+ Mastercard,
457
+ Maestro,
458
+ AmericanExpress,
459
+ Dinersclub,
460
+ Discover,
461
+ Jcb,
462
+ Unionpay,
463
+ Aura,
464
+ CartesBancaires,
465
+ Cirrus,
466
+ Dankort,
467
+ Elo,
468
+ Hipercard,
469
+ Mir,
470
+ Naranja,
471
+ Paypal,
472
+ Sodexo,
473
+ Uatp
474
+ } from "@xsolla/xui-icons-payment";
475
+ import { jsx as jsx8, jsxs } from "react/jsx-runtime";
476
+ var DEFAULT_POSSIBLE_PAYMENTS = [
477
+ "mastercard",
478
+ "visa",
479
+ "maestro",
480
+ "diners",
481
+ "amex",
482
+ "discover",
483
+ "jcb",
484
+ "unionpay"
485
+ ];
486
+ var paymentIconComponents = {
487
+ visa: Visa,
488
+ mastercard: Mastercard,
489
+ maestro: Maestro,
490
+ amex: AmericanExpress,
491
+ diners: Dinersclub,
492
+ discover: Discover,
493
+ jcb: Jcb,
494
+ unionpay: Unionpay,
495
+ aura: Aura,
496
+ cartesbancaires: CartesBancaires,
497
+ cirrus: Cirrus,
498
+ dankort: Dankort,
499
+ elo: Elo,
500
+ hipercard: Hipercard,
501
+ mir: Mir,
502
+ naranja: Naranja,
503
+ paypal: Paypal,
504
+ sodexo: Sodexo,
505
+ uatp: Uatp
506
+ };
507
+ var detectPaymentSystem = (cardNumber) => {
508
+ const cleanNumber = cardNumber.replace(/\s/g, "");
509
+ if (!cleanNumber) return null;
510
+ if (/^1/.test(cleanNumber)) return "uatp";
511
+ if (/^220[0-4]/.test(cleanNumber)) return "mir";
512
+ if (/^3[47]/.test(cleanNumber)) return "amex";
513
+ if (/^3(?:0[0-5]|[68])/.test(cleanNumber)) return "diners";
514
+ if (/^35(?:2[89]|[3-8])/.test(cleanNumber)) return "jcb";
515
+ if (/^(401178|401179|431274|438935|451416|457393|457631|457632|504175|506699|5067[0-9]{2}|509[0-9]{3}|627780|636297|636368|650[0-9]{3}|651[0-9]{3}|655[0-9]{3})/.test(
516
+ cleanNumber
517
+ ))
518
+ return "elo";
519
+ if (/^4/.test(cleanNumber)) return "visa";
520
+ if (/^(606282|637095|637568|637599|637609|637612)/.test(cleanNumber))
521
+ return "hipercard";
522
+ if (/^507860/.test(cleanNumber)) return "aura";
523
+ if (/^5019/.test(cleanNumber)) return "dankort";
524
+ if (/^589562/.test(cleanNumber)) return "naranja";
525
+ if (/^5[1-5]/.test(cleanNumber)) return "mastercard";
526
+ if (cleanNumber.length >= 4 && /^(?:222[1-9]|22[3-9]\d|2[3-6]\d{2}|27[01]\d|2720)/.test(cleanNumber))
527
+ return "mastercard";
528
+ if (/^(?:6011|65|64[4-9])/.test(cleanNumber)) return "discover";
529
+ if (cleanNumber.length >= 6 && /^(?:6221(?:2[6-9]|[3-9]\d)|622[2-8]\d{2}|6229(?:[01]\d|2[0-5]))/.test(cleanNumber))
530
+ return "discover";
531
+ if (/^62/.test(cleanNumber)) return "unionpay";
532
+ if (/^(?:5[06-9]|6)/.test(cleanNumber)) return "maestro";
533
+ return null;
534
+ };
535
+ var CARD_GAP = 4;
536
+ var PaymentIcons = ({ possiblePayments, maxVisible, recognizedPayment, iconHeight }) => {
537
+ const [cyclingIndex, setCyclingIndex] = useState(0);
538
+ const [isTransitioning, setIsTransitioning] = useState(false);
539
+ const [isRecognized, setIsRecognized] = useState(false);
540
+ const scaledCardHeight = iconHeight;
541
+ const scaledCardWidth = Math.round(iconHeight * (4 / 3));
542
+ const scaledGap = CARD_GAP;
543
+ const constantCards = possiblePayments.slice(0, maxVisible - 1);
544
+ const cyclingCards = possiblePayments.slice(maxVisible - 1);
545
+ useEffect(() => {
546
+ if (recognizedPayment) {
547
+ setIsRecognized(true);
548
+ } else {
549
+ const timeout = setTimeout(() => {
550
+ setIsRecognized(false);
551
+ }, 50);
552
+ return () => clearTimeout(timeout);
553
+ }
554
+ }, [recognizedPayment]);
555
+ useEffect(() => {
556
+ if (cyclingCards.length <= 1 || recognizedPayment) return;
557
+ const interval = setInterval(() => {
558
+ setIsTransitioning(true);
559
+ setTimeout(() => {
560
+ setCyclingIndex((prev) => (prev + 1) % cyclingCards.length);
561
+ setIsTransitioning(false);
562
+ }, 150);
563
+ }, 2e3);
564
+ return () => clearInterval(interval);
565
+ }, [cyclingCards.length, recognizedPayment]);
566
+ const paymentLabels = {
567
+ mastercard: "Mastercard",
568
+ visa: "Visa",
569
+ maestro: "Maestro",
570
+ diners: "Diners Club",
571
+ amex: "American Express",
572
+ discover: "Discover",
573
+ jcb: "JCB",
574
+ unionpay: "UnionPay",
575
+ aura: "Aura",
576
+ cartesbancaires: "Cartes Bancaires",
577
+ cirrus: "Cirrus",
578
+ dankort: "Dankort",
579
+ elo: "Elo",
580
+ hipercard: "Hipercard",
581
+ mir: "Mir",
582
+ naranja: "Naranja",
583
+ paypal: "PayPal",
584
+ sodexo: "Sodexo",
585
+ uatp: "UATP"
586
+ };
587
+ const getAriaLabel = () => {
588
+ if (recognizedPayment) {
589
+ return `Recognized payment: ${paymentLabels[recognizedPayment]}`;
590
+ }
591
+ return `Accepted payment cards: ${possiblePayments.map((key) => paymentLabels[key]).join(", ")}`;
592
+ };
593
+ const totalVisibleCards = Math.min(possiblePayments.length, maxVisible);
594
+ const containerWidth = totalVisibleCards * scaledCardWidth + (totalVisibleCards - 1) * scaledGap;
595
+ return /* @__PURE__ */ jsxs(
596
+ Box,
597
+ {
598
+ flexDirection: "row",
599
+ alignItems: "center",
600
+ role: "img",
601
+ "aria-label": getAriaLabel(),
602
+ position: "relative",
603
+ width: containerWidth,
604
+ height: scaledCardHeight,
605
+ style: { overflow: "hidden" },
606
+ children: [
607
+ constantCards.map((key, index) => {
608
+ const IconComponent = paymentIconComponents[key];
609
+ const isRecognizedCard = recognizedPayment === key;
610
+ let translateX = 0;
611
+ let opacity = 1;
612
+ if (isRecognized && recognizedPayment) {
613
+ if (isRecognizedCard) {
614
+ const targetPosition = (totalVisibleCards - 1) * (scaledCardWidth + scaledGap);
615
+ const currentPosition = index * (scaledCardWidth + scaledGap);
616
+ translateX = targetPosition - currentPosition;
617
+ opacity = 1;
618
+ } else {
619
+ translateX = containerWidth;
620
+ opacity = 0;
621
+ }
622
+ }
623
+ return /* @__PURE__ */ jsx8(
624
+ Box,
625
+ {
626
+ position: "absolute",
627
+ left: index * (scaledCardWidth + scaledGap),
628
+ style: {
629
+ transform: isWeb ? `translateX(${translateX}px)` : void 0,
630
+ opacity,
631
+ ...isWeb && { transition: "transform 300ms ease-out, opacity 300ms ease-out" }
632
+ },
633
+ children: /* @__PURE__ */ jsx8(IconComponent, { size: scaledCardHeight })
634
+ },
635
+ key
636
+ );
637
+ }),
638
+ cyclingCards.length > 0 && /* @__PURE__ */ jsx8(
639
+ Box,
640
+ {
641
+ position: "absolute",
642
+ left: (maxVisible - 1) * (scaledCardWidth + scaledGap),
643
+ width: scaledCardWidth,
644
+ height: scaledCardHeight,
645
+ children: cyclingCards.map((key, index) => {
646
+ const IconComponent = paymentIconComponents[key];
647
+ const isCurrentCycling = index === cyclingIndex;
648
+ const isRecognizedCard = recognizedPayment === key;
649
+ let translateX = 0;
650
+ let opacity = isCurrentCycling && !isTransitioning ? 1 : 0;
651
+ if (isRecognized && recognizedPayment) {
652
+ if (isRecognizedCard) {
653
+ opacity = 1;
654
+ } else {
655
+ translateX = containerWidth;
656
+ opacity = 0;
657
+ }
658
+ }
659
+ return /* @__PURE__ */ jsx8(
660
+ Box,
661
+ {
662
+ position: "absolute",
663
+ top: 0,
664
+ left: 0,
665
+ style: {
666
+ transform: isWeb ? `translateX(${translateX}px)` : void 0,
667
+ opacity,
668
+ ...isWeb && {
669
+ transition: isRecognized ? "transform 300ms ease-out, opacity 300ms ease-out" : "opacity 150ms ease-in-out"
670
+ }
671
+ },
672
+ children: /* @__PURE__ */ jsx8(IconComponent, { size: scaledCardHeight })
673
+ },
674
+ key
675
+ );
676
+ })
677
+ }
678
+ )
679
+ ]
680
+ }
681
+ );
682
+ };
683
+ var InputPayment = forwardRef3(
684
+ ({
685
+ value,
686
+ icon,
687
+ placeholder = "Card number",
688
+ onChange,
689
+ onChangeText,
690
+ onKeyDown,
691
+ size = "m",
692
+ name,
693
+ disabled = false,
694
+ errorMessage,
695
+ error,
696
+ possiblePayments = DEFAULT_POSSIBLE_PAYMENTS,
697
+ maxVisiblePossiblePayments = 5,
698
+ recognizedPayment: controlledRecognizedPayment,
699
+ onRecognizedPaymentChange,
700
+ autoDetect = true,
701
+ id: providedId,
702
+ "aria-label": ariaLabel,
703
+ testID,
704
+ ...rest
705
+ }, ref) => {
706
+ const { theme } = useDesignSystem();
707
+ const [internalState, setInternalState] = useState(
708
+ "default"
709
+ );
710
+ const [passValue, setPassValue] = useState(value ?? "");
711
+ const [detectedPayment, setDetectedPayment] = useState(null);
712
+ const inputRef = useRef(null);
713
+ const rawId = useId();
714
+ const safeId = rawId.replace(/:/g, "");
715
+ const inputId = providedId || `input-payment-${safeId}`;
716
+ const errorId = `${inputId}-error`;
717
+ React4.useImperativeHandle(
718
+ ref,
719
+ () => inputRef.current,
720
+ []
721
+ );
722
+ useEffect(() => {
723
+ if (value !== void 0) {
724
+ setPassValue(value);
725
+ }
726
+ }, [value]);
727
+ useEffect(() => {
728
+ if (autoDetect && passValue) {
729
+ const detected = detectPaymentSystem(passValue);
730
+ const validDetected = detected && possiblePayments.includes(detected) ? detected : null;
731
+ setDetectedPayment(validDetected);
732
+ onRecognizedPaymentChange?.(validDetected);
733
+ } else if (!passValue) {
734
+ setDetectedPayment(null);
735
+ if (autoDetect) {
736
+ onRecognizedPaymentChange?.(null);
737
+ }
738
+ }
739
+ }, [passValue, autoDetect, onRecognizedPaymentChange, possiblePayments]);
740
+ const isDisable = disabled;
741
+ const isError = !!(errorMessage || error);
742
+ const isFocus = internalState === "focus";
743
+ const displayRecognizedPayment = controlledRecognizedPayment ?? detectedPayment;
744
+ const sizeStyles = theme.sizing.input(size);
745
+ const inputColors = theme.colors.control.input;
746
+ const handleFocus = () => {
747
+ if (!isDisable) {
748
+ setInternalState("focus");
749
+ }
750
+ };
751
+ const handleBlur = () => {
752
+ if (!isDisable) {
753
+ setInternalState("default");
754
+ }
755
+ };
756
+ const handleChange = (e) => {
757
+ const newValue = e.target.value;
758
+ if (onChange) {
759
+ onChange(e);
760
+ }
761
+ if (onChangeText) {
762
+ onChangeText(newValue);
763
+ }
764
+ setPassValue(newValue);
765
+ };
766
+ const handleKeyDown = (e) => {
767
+ if (e.key === "Escape") {
768
+ e.currentTarget.blur();
769
+ }
770
+ if (onKeyDown) {
771
+ onKeyDown(e);
772
+ }
773
+ };
774
+ let backgroundColor = inputColors.bg;
775
+ let borderColor = inputColors.border;
776
+ let outlineColor;
777
+ if (isDisable) {
778
+ backgroundColor = inputColors.bgDisable;
779
+ borderColor = inputColors.borderDisable;
780
+ } else if (isError) {
781
+ outlineColor = theme.colors.border.alert;
782
+ if (isFocus) {
783
+ backgroundColor = theme.colors.control.focus.bg;
784
+ }
785
+ } else if (isFocus) {
786
+ backgroundColor = theme.colors.control.focus.bg;
787
+ outlineColor = theme.colors.border.brand;
788
+ }
789
+ const textColor = isDisable ? inputColors.textDisable : inputColors.text;
790
+ const placeholderColor = inputColors.placeholder;
791
+ const iconColor = inputColors.placeholder;
792
+ const paddingConfig = {
793
+ xl: { vertical: 12, horizontal: 12 },
794
+ l: { vertical: 14, horizontal: 12 },
795
+ m: { vertical: 11, horizontal: 12 },
796
+ s: { vertical: 7, horizontal: 10 },
797
+ xs: { vertical: 7, horizontal: 10 }
798
+ };
799
+ const borderRadiusConfig = {
800
+ xl: 4,
801
+ l: 4,
802
+ m: 2,
803
+ s: 2,
804
+ xs: 2
805
+ };
806
+ const iconSizeConfig = {
807
+ xl: 18,
808
+ l: 18,
809
+ m: 18,
810
+ s: 16,
811
+ xs: 16
812
+ };
813
+ const focusOutlineConfig = {
814
+ xl: { width: 2, offset: -2 },
815
+ l: { width: 2, offset: -2 },
816
+ m: { width: 1, offset: -1 },
817
+ s: { width: 1, offset: -1 },
818
+ xs: { width: 1, offset: -1 }
819
+ };
820
+ const padding = paddingConfig[size];
821
+ const borderRadius = borderRadiusConfig[size];
822
+ const iconSize = iconSizeConfig[size];
823
+ const focusOutline = focusOutlineConfig[size];
824
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 8, width: "100%", testID, children: [
825
+ /* @__PURE__ */ jsxs(
826
+ Box,
827
+ {
828
+ backgroundColor,
829
+ borderColor,
830
+ borderWidth: borderColor !== "transparent" ? 1 : 0,
831
+ borderRadius,
832
+ height: sizeStyles.height,
833
+ paddingVertical: padding.vertical,
834
+ paddingHorizontal: padding.horizontal,
835
+ flexDirection: "row",
836
+ alignItems: "center",
837
+ gap: 10,
838
+ position: "relative",
839
+ style: {
840
+ ...outlineColor ? {
841
+ outline: `${focusOutline.width}px solid ${outlineColor}`,
842
+ outlineOffset: `${focusOutline.offset}px`
843
+ } : {}
844
+ },
845
+ hoverStyle: !isDisable && !isFocus && !isError ? {
846
+ backgroundColor: inputColors.bgHover,
847
+ borderColor: inputColors.borderHover
848
+ } : void 0,
849
+ children: [
850
+ icon && /* @__PURE__ */ jsx8(
851
+ Box,
852
+ {
853
+ alignItems: "center",
854
+ justifyContent: "center",
855
+ role: "img",
856
+ "aria-hidden": "true",
857
+ children: React4.isValidElement(icon) ? React4.cloneElement(icon, {
858
+ size: iconSize,
859
+ color: iconColor
860
+ }) : icon
861
+ }
862
+ ),
863
+ /* @__PURE__ */ jsx8(Box, { flex: 1, height: "100%", justifyContent: "center", children: /* @__PURE__ */ jsx8(
864
+ InputPrimitive,
865
+ {
866
+ ref: inputRef,
867
+ id: inputId,
868
+ value: passValue,
869
+ name,
870
+ placeholder,
871
+ onChange: handleChange,
872
+ onFocus: handleFocus,
873
+ onBlur: handleBlur,
874
+ onKeyDown: handleKeyDown,
875
+ disabled: isDisable,
876
+ type: "text",
877
+ inputMode: "numeric",
878
+ autoComplete: "off",
879
+ color: textColor,
880
+ fontSize: sizeStyles.fontSize,
881
+ placeholderTextColor: placeholderColor,
882
+ "aria-invalid": isError || void 0,
883
+ "aria-describedby": errorMessage ? errorId : void 0,
884
+ "aria-label": ariaLabel || "Card number",
885
+ "aria-disabled": isDisable || void 0,
886
+ "data-testid": "input-payment__field",
887
+ ...rest
888
+ }
889
+ ) }),
890
+ possiblePayments.length > 0 && /* @__PURE__ */ jsx8(
891
+ Box,
892
+ {
893
+ alignItems: "center",
894
+ justifyContent: "center",
895
+ "data-testid": "input-payment__card-icons",
896
+ style: { overflow: "hidden" },
897
+ children: /* @__PURE__ */ jsx8(
898
+ PaymentIcons,
899
+ {
900
+ possiblePayments,
901
+ maxVisible: maxVisiblePossiblePayments,
902
+ recognizedPayment: displayRecognizedPayment,
903
+ iconHeight: iconSize
904
+ }
905
+ )
906
+ }
907
+ )
908
+ ]
909
+ }
910
+ ),
911
+ errorMessage && /* @__PURE__ */ jsx8(
912
+ Text,
913
+ {
914
+ id: errorId,
915
+ role: "alert",
916
+ color: theme.colors.content.alert.primary,
917
+ fontSize: sizeStyles.fontSize - 2,
918
+ children: errorMessage
919
+ }
920
+ )
921
+ ] });
922
+ }
923
+ );
924
+ InputPayment.displayName = "InputPayment";
925
+ export {
926
+ InputPayment
927
+ };
928
+ //# sourceMappingURL=index.mjs.map