@alfalab/core-components-bank-card 5.2.25 → 5.3.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/Component.js +1 -1
- package/esm/Component.js +1 -1
- package/esm/index.css +20 -20
- package/index.css +20 -20
- package/modern/Component.js +1 -1
- package/modern/index.css +20 -20
- package/package.json +2 -2
- package/src/Component.tsx +160 -0
- package/src/index.module.css +133 -0
- package/src/index.ts +1 -0
- package/src/utils.ts +28 -0
package/Component.js
CHANGED
|
@@ -17,7 +17,7 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
|
|
|
17
17
|
var React__default = /*#__PURE__*/_interopDefaultCompat(React);
|
|
18
18
|
var cn__default = /*#__PURE__*/_interopDefaultCompat(cn);
|
|
19
19
|
|
|
20
|
-
var styles = {"component":"bank-
|
|
20
|
+
var styles = {"component":"bank-card__component_uo254","aspectRatioContainer":"bank-card__aspectRatioContainer_uo254","content":"bank-card__content_uo254","label":"bank-card__label_uo254","focused":"bank-card__focused_uo254","filled":"bank-card__filled_uo254","input":"bank-card__input_uo254","bankLogo":"bank-card__bankLogo_uo254","brandLogo":"bank-card__brandLogo_uo254","usePhoto":"bank-card__usePhoto_uo254"};
|
|
21
21
|
require('./index.css')
|
|
22
22
|
|
|
23
23
|
// prettier-ignore
|
package/esm/Component.js
CHANGED
|
@@ -8,7 +8,7 @@ import { MirXxlIcon } from '@alfalab/icons-logotype/MirXxlIcon';
|
|
|
8
8
|
import { VisaXxlIcon } from '@alfalab/icons-logotype/VisaXxlIcon';
|
|
9
9
|
import { validateCardNumber } from './utils.js';
|
|
10
10
|
|
|
11
|
-
var styles = {"component":"bank-
|
|
11
|
+
var styles = {"component":"bank-card__component_uo254","aspectRatioContainer":"bank-card__aspectRatioContainer_uo254","content":"bank-card__content_uo254","label":"bank-card__label_uo254","focused":"bank-card__focused_uo254","filled":"bank-card__filled_uo254","input":"bank-card__input_uo254","bankLogo":"bank-card__bankLogo_uo254","brandLogo":"bank-card__brandLogo_uo254","usePhoto":"bank-card__usePhoto_uo254"};
|
|
12
12
|
require('./index.css')
|
|
13
13
|
|
|
14
14
|
// prettier-ignore
|
package/esm/index.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* hash:
|
|
1
|
+
/* hash: 1u46b */
|
|
2
2
|
:root {
|
|
3
3
|
--color-black: #000;
|
|
4
4
|
--color-black-60: rgba(0, 0, 0, 0.6);
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
--font-family-styrene: 'Styrene UI', system-ui, -apple-system, 'Segoe UI', Roboto,
|
|
23
23
|
'Helvetica Neue', Helvetica, sans-serif;
|
|
24
24
|
} :root {
|
|
25
|
-
} .bank-
|
|
25
|
+
} .bank-card__component_uo254 {
|
|
26
26
|
/* TODO: как это будет собираться и работать в webView */
|
|
27
27
|
--form-control-border-radius: 0;
|
|
28
28
|
--form-control-border-bottom: 1px solid var(--color-black-60);
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
max-width: 343px;
|
|
42
42
|
|
|
43
43
|
font-family: var(--font-family-styrene);
|
|
44
|
-
} .bank-
|
|
44
|
+
} .bank-card__aspectRatioContainer_uo254 {
|
|
45
45
|
/* Эталонный размер 343x216 */
|
|
46
46
|
padding-bottom: 63%;
|
|
47
|
-
} .bank-
|
|
47
|
+
} .bank-card__content_uo254 {
|
|
48
48
|
position: absolute;
|
|
49
49
|
top: 0;
|
|
50
50
|
right: 0;
|
|
@@ -56,26 +56,26 @@
|
|
|
56
56
|
padding-right: var(--gap-l);
|
|
57
57
|
border-radius: var(--border-radius-l);
|
|
58
58
|
box-sizing: border-box;
|
|
59
|
-
} .bank-
|
|
59
|
+
} .bank-card__label_uo254 {
|
|
60
60
|
left: 0;
|
|
61
61
|
transform: translateY(-1px);
|
|
62
62
|
color: var(--color-black-60);
|
|
63
63
|
font-size: 20px;
|
|
64
64
|
line-height: 24px
|
|
65
|
-
} @media (max-width: 360px) { .bank-
|
|
65
|
+
} @media (max-width: 360px) { .bank-card__label_uo254 {
|
|
66
66
|
font-size: 16px
|
|
67
67
|
}
|
|
68
|
-
} .bank-
|
|
69
|
-
.bank-
|
|
68
|
+
} .bank-card__focused_uo254 .bank-card__label_uo254,
|
|
69
|
+
.bank-card__filled_uo254 .bank-card__label_uo254 {
|
|
70
70
|
color: var(--color-black);
|
|
71
71
|
transform: translateY(-19px) scale(0.6)
|
|
72
|
-
} @media (max-width: 360px) { .bank-
|
|
73
|
-
.bank-
|
|
72
|
+
} @media (max-width: 360px) { .bank-card__focused_uo254 .bank-card__label_uo254,
|
|
73
|
+
.bank-card__filled_uo254 .bank-card__label_uo254 {
|
|
74
74
|
transform: translateY(-22px) scale(0.75)
|
|
75
75
|
}
|
|
76
|
-
} .bank-
|
|
76
|
+
} .bank-card__focused_uo254:before {
|
|
77
77
|
transform: scale(1) !important;
|
|
78
|
-
} .bank-
|
|
78
|
+
} .bank-card__input_uo254 {
|
|
79
79
|
font-size: 20px;
|
|
80
80
|
line-height: 24px;
|
|
81
81
|
font-weight: 400;
|
|
@@ -88,27 +88,27 @@
|
|
|
88
88
|
/* TODO: обсудить с дизайнером, почему инпут не соответствует дизайн-системе, либо уменьшить каскад в form-control */
|
|
89
89
|
padding-left: 0 !important;
|
|
90
90
|
padding-bottom: var(--gap-2xs) !important
|
|
91
|
-
} @media (max-width: 360px) { .bank-
|
|
91
|
+
} @media (max-width: 360px) { .bank-card__input_uo254 {
|
|
92
92
|
font-size: 16px
|
|
93
93
|
}
|
|
94
|
-
} .bank-
|
|
94
|
+
} .bank-card__bankLogo_uo254 {
|
|
95
95
|
position: absolute;
|
|
96
96
|
top: 20px;
|
|
97
97
|
left: 20px
|
|
98
|
-
} .bank-
|
|
98
|
+
} .bank-card__bankLogo_uo254 svg {
|
|
99
99
|
max-height: 32px;
|
|
100
100
|
width: auto;
|
|
101
101
|
display: block
|
|
102
|
-
} .bank-
|
|
102
|
+
} .bank-card__bankLogo_uo254 svg g {
|
|
103
103
|
fill: var(--color-black);
|
|
104
|
-
} .bank-
|
|
104
|
+
} .bank-card__brandLogo_uo254 {
|
|
105
105
|
position: absolute;
|
|
106
106
|
bottom: 20px;
|
|
107
107
|
right: 20px
|
|
108
|
-
} .bank-
|
|
108
|
+
} .bank-card__brandLogo_uo254 svg {
|
|
109
109
|
display: block;
|
|
110
110
|
fill: var(--color-black);
|
|
111
|
-
} .bank-
|
|
111
|
+
} .bank-card__usePhoto_uo254 {
|
|
112
112
|
display: block;
|
|
113
113
|
margin: 0;
|
|
114
114
|
padding: 0;
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
|
|
126
126
|
/* В макете иконка прилегает к правому краю поля, а по дизайн-системе там должен быть отступ */
|
|
127
127
|
margin-right: calc(var(--gap-s) * -1)
|
|
128
|
-
} .bank-
|
|
128
|
+
} .bank-card__usePhoto_uo254 svg {
|
|
129
129
|
fill: var(--color-black);
|
|
130
130
|
display: block;
|
|
131
131
|
}
|
package/index.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* hash:
|
|
1
|
+
/* hash: 1u46b */
|
|
2
2
|
:root {
|
|
3
3
|
--color-black: #000;
|
|
4
4
|
--color-black-60: rgba(0, 0, 0, 0.6);
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
--font-family-styrene: 'Styrene UI', system-ui, -apple-system, 'Segoe UI', Roboto,
|
|
23
23
|
'Helvetica Neue', Helvetica, sans-serif;
|
|
24
24
|
} :root {
|
|
25
|
-
} .bank-
|
|
25
|
+
} .bank-card__component_uo254 {
|
|
26
26
|
/* TODO: как это будет собираться и работать в webView */
|
|
27
27
|
--form-control-border-radius: 0;
|
|
28
28
|
--form-control-border-bottom: 1px solid var(--color-black-60);
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
max-width: 343px;
|
|
42
42
|
|
|
43
43
|
font-family: var(--font-family-styrene);
|
|
44
|
-
} .bank-
|
|
44
|
+
} .bank-card__aspectRatioContainer_uo254 {
|
|
45
45
|
/* Эталонный размер 343x216 */
|
|
46
46
|
padding-bottom: 63%;
|
|
47
|
-
} .bank-
|
|
47
|
+
} .bank-card__content_uo254 {
|
|
48
48
|
position: absolute;
|
|
49
49
|
top: 0;
|
|
50
50
|
right: 0;
|
|
@@ -56,26 +56,26 @@
|
|
|
56
56
|
padding-right: var(--gap-l);
|
|
57
57
|
border-radius: var(--border-radius-l);
|
|
58
58
|
box-sizing: border-box;
|
|
59
|
-
} .bank-
|
|
59
|
+
} .bank-card__label_uo254 {
|
|
60
60
|
left: 0;
|
|
61
61
|
transform: translateY(-1px);
|
|
62
62
|
color: var(--color-black-60);
|
|
63
63
|
font-size: 20px;
|
|
64
64
|
line-height: 24px
|
|
65
|
-
} @media (max-width: 360px) { .bank-
|
|
65
|
+
} @media (max-width: 360px) { .bank-card__label_uo254 {
|
|
66
66
|
font-size: 16px
|
|
67
67
|
}
|
|
68
|
-
} .bank-
|
|
69
|
-
.bank-
|
|
68
|
+
} .bank-card__focused_uo254 .bank-card__label_uo254,
|
|
69
|
+
.bank-card__filled_uo254 .bank-card__label_uo254 {
|
|
70
70
|
color: var(--color-black);
|
|
71
71
|
transform: translateY(-19px) scale(0.6)
|
|
72
|
-
} @media (max-width: 360px) { .bank-
|
|
73
|
-
.bank-
|
|
72
|
+
} @media (max-width: 360px) { .bank-card__focused_uo254 .bank-card__label_uo254,
|
|
73
|
+
.bank-card__filled_uo254 .bank-card__label_uo254 {
|
|
74
74
|
transform: translateY(-22px) scale(0.75)
|
|
75
75
|
}
|
|
76
|
-
} .bank-
|
|
76
|
+
} .bank-card__focused_uo254:before {
|
|
77
77
|
transform: scale(1) !important;
|
|
78
|
-
} .bank-
|
|
78
|
+
} .bank-card__input_uo254 {
|
|
79
79
|
font-size: 20px;
|
|
80
80
|
line-height: 24px;
|
|
81
81
|
font-weight: 400;
|
|
@@ -88,27 +88,27 @@
|
|
|
88
88
|
/* TODO: обсудить с дизайнером, почему инпут не соответствует дизайн-системе, либо уменьшить каскад в form-control */
|
|
89
89
|
padding-left: 0 !important;
|
|
90
90
|
padding-bottom: var(--gap-2xs) !important
|
|
91
|
-
} @media (max-width: 360px) { .bank-
|
|
91
|
+
} @media (max-width: 360px) { .bank-card__input_uo254 {
|
|
92
92
|
font-size: 16px
|
|
93
93
|
}
|
|
94
|
-
} .bank-
|
|
94
|
+
} .bank-card__bankLogo_uo254 {
|
|
95
95
|
position: absolute;
|
|
96
96
|
top: 20px;
|
|
97
97
|
left: 20px
|
|
98
|
-
} .bank-
|
|
98
|
+
} .bank-card__bankLogo_uo254 svg {
|
|
99
99
|
max-height: 32px;
|
|
100
100
|
width: auto;
|
|
101
101
|
display: block
|
|
102
|
-
} .bank-
|
|
102
|
+
} .bank-card__bankLogo_uo254 svg g {
|
|
103
103
|
fill: var(--color-black);
|
|
104
|
-
} .bank-
|
|
104
|
+
} .bank-card__brandLogo_uo254 {
|
|
105
105
|
position: absolute;
|
|
106
106
|
bottom: 20px;
|
|
107
107
|
right: 20px
|
|
108
|
-
} .bank-
|
|
108
|
+
} .bank-card__brandLogo_uo254 svg {
|
|
109
109
|
display: block;
|
|
110
110
|
fill: var(--color-black);
|
|
111
|
-
} .bank-
|
|
111
|
+
} .bank-card__usePhoto_uo254 {
|
|
112
112
|
display: block;
|
|
113
113
|
margin: 0;
|
|
114
114
|
padding: 0;
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
|
|
126
126
|
/* В макете иконка прилегает к правому краю поля, а по дизайн-системе там должен быть отступ */
|
|
127
127
|
margin-right: calc(var(--gap-s) * -1)
|
|
128
|
-
} .bank-
|
|
128
|
+
} .bank-card__usePhoto_uo254 svg {
|
|
129
129
|
fill: var(--color-black);
|
|
130
130
|
display: block;
|
|
131
131
|
}
|
package/modern/Component.js
CHANGED
|
@@ -8,7 +8,7 @@ import { MirXxlIcon } from '@alfalab/icons-logotype/MirXxlIcon';
|
|
|
8
8
|
import { VisaXxlIcon } from '@alfalab/icons-logotype/VisaXxlIcon';
|
|
9
9
|
import { validateCardNumber } from './utils.js';
|
|
10
10
|
|
|
11
|
-
const styles = {"component":"bank-
|
|
11
|
+
const styles = {"component":"bank-card__component_uo254","aspectRatioContainer":"bank-card__aspectRatioContainer_uo254","content":"bank-card__content_uo254","label":"bank-card__label_uo254","focused":"bank-card__focused_uo254","filled":"bank-card__filled_uo254","input":"bank-card__input_uo254","bankLogo":"bank-card__bankLogo_uo254","brandLogo":"bank-card__brandLogo_uo254","usePhoto":"bank-card__usePhoto_uo254"};
|
|
12
12
|
require('./index.css')
|
|
13
13
|
|
|
14
14
|
// prettier-ignore
|
package/modern/index.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* hash:
|
|
1
|
+
/* hash: 1u46b */
|
|
2
2
|
:root {
|
|
3
3
|
--color-black: #000;
|
|
4
4
|
--color-black-60: rgba(0, 0, 0, 0.6);
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
--font-family-styrene: 'Styrene UI', system-ui, -apple-system, 'Segoe UI', Roboto,
|
|
23
23
|
'Helvetica Neue', Helvetica, sans-serif;
|
|
24
24
|
} :root {
|
|
25
|
-
} .bank-
|
|
25
|
+
} .bank-card__component_uo254 {
|
|
26
26
|
/* TODO: как это будет собираться и работать в webView */
|
|
27
27
|
--form-control-border-radius: 0;
|
|
28
28
|
--form-control-border-bottom: 1px solid var(--color-black-60);
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
max-width: 343px;
|
|
42
42
|
|
|
43
43
|
font-family: var(--font-family-styrene);
|
|
44
|
-
} .bank-
|
|
44
|
+
} .bank-card__aspectRatioContainer_uo254 {
|
|
45
45
|
/* Эталонный размер 343x216 */
|
|
46
46
|
padding-bottom: 63%;
|
|
47
|
-
} .bank-
|
|
47
|
+
} .bank-card__content_uo254 {
|
|
48
48
|
position: absolute;
|
|
49
49
|
top: 0;
|
|
50
50
|
right: 0;
|
|
@@ -56,26 +56,26 @@
|
|
|
56
56
|
padding-right: var(--gap-l);
|
|
57
57
|
border-radius: var(--border-radius-l);
|
|
58
58
|
box-sizing: border-box;
|
|
59
|
-
} .bank-
|
|
59
|
+
} .bank-card__label_uo254 {
|
|
60
60
|
left: 0;
|
|
61
61
|
transform: translateY(-1px);
|
|
62
62
|
color: var(--color-black-60);
|
|
63
63
|
font-size: 20px;
|
|
64
64
|
line-height: 24px
|
|
65
|
-
} @media (max-width: 360px) { .bank-
|
|
65
|
+
} @media (max-width: 360px) { .bank-card__label_uo254 {
|
|
66
66
|
font-size: 16px
|
|
67
67
|
}
|
|
68
|
-
} .bank-
|
|
69
|
-
.bank-
|
|
68
|
+
} .bank-card__focused_uo254 .bank-card__label_uo254,
|
|
69
|
+
.bank-card__filled_uo254 .bank-card__label_uo254 {
|
|
70
70
|
color: var(--color-black);
|
|
71
71
|
transform: translateY(-19px) scale(0.6)
|
|
72
|
-
} @media (max-width: 360px) { .bank-
|
|
73
|
-
.bank-
|
|
72
|
+
} @media (max-width: 360px) { .bank-card__focused_uo254 .bank-card__label_uo254,
|
|
73
|
+
.bank-card__filled_uo254 .bank-card__label_uo254 {
|
|
74
74
|
transform: translateY(-22px) scale(0.75)
|
|
75
75
|
}
|
|
76
|
-
} .bank-
|
|
76
|
+
} .bank-card__focused_uo254:before {
|
|
77
77
|
transform: scale(1) !important;
|
|
78
|
-
} .bank-
|
|
78
|
+
} .bank-card__input_uo254 {
|
|
79
79
|
font-size: 20px;
|
|
80
80
|
line-height: 24px;
|
|
81
81
|
font-weight: 400;
|
|
@@ -88,27 +88,27 @@
|
|
|
88
88
|
/* TODO: обсудить с дизайнером, почему инпут не соответствует дизайн-системе, либо уменьшить каскад в form-control */
|
|
89
89
|
padding-left: 0 !important;
|
|
90
90
|
padding-bottom: var(--gap-2xs) !important
|
|
91
|
-
} @media (max-width: 360px) { .bank-
|
|
91
|
+
} @media (max-width: 360px) { .bank-card__input_uo254 {
|
|
92
92
|
font-size: 16px
|
|
93
93
|
}
|
|
94
|
-
} .bank-
|
|
94
|
+
} .bank-card__bankLogo_uo254 {
|
|
95
95
|
position: absolute;
|
|
96
96
|
top: 20px;
|
|
97
97
|
left: 20px
|
|
98
|
-
} .bank-
|
|
98
|
+
} .bank-card__bankLogo_uo254 svg {
|
|
99
99
|
max-height: 32px;
|
|
100
100
|
width: auto;
|
|
101
101
|
display: block
|
|
102
|
-
} .bank-
|
|
102
|
+
} .bank-card__bankLogo_uo254 svg g {
|
|
103
103
|
fill: var(--color-black);
|
|
104
|
-
} .bank-
|
|
104
|
+
} .bank-card__brandLogo_uo254 {
|
|
105
105
|
position: absolute;
|
|
106
106
|
bottom: 20px;
|
|
107
107
|
right: 20px
|
|
108
|
-
} .bank-
|
|
108
|
+
} .bank-card__brandLogo_uo254 svg {
|
|
109
109
|
display: block;
|
|
110
110
|
fill: var(--color-black);
|
|
111
|
-
} .bank-
|
|
111
|
+
} .bank-card__usePhoto_uo254 {
|
|
112
112
|
display: block;
|
|
113
113
|
margin: 0;
|
|
114
114
|
padding: 0;
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
|
|
126
126
|
/* В макете иконка прилегает к правому краю поля, а по дизайн-системе там должен быть отступ */
|
|
127
127
|
margin-right: calc(var(--gap-s) * -1)
|
|
128
|
-
} .bank-
|
|
128
|
+
} .bank-card__usePhoto_uo254 svg {
|
|
129
129
|
fill: var(--color-black);
|
|
130
130
|
display: block;
|
|
131
131
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfalab/core-components-bank-card",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "Bank card component",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"react-dom": "^16.9.0 || ^17.0.1 || ^18.0.0"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@alfalab/core-components-masked-input": "^6.
|
|
18
|
+
"@alfalab/core-components-masked-input": "^6.2.0",
|
|
19
19
|
"@alfalab/icons-glyph": "^2.108.0",
|
|
20
20
|
"@alfalab/icons-logotype": "^2.19.0",
|
|
21
21
|
"classnames": "^2.3.1",
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import React, { ChangeEvent, MouseEvent, ReactNode, useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
|
|
4
|
+
import { MaskedInput } from '@alfalab/core-components-masked-input';
|
|
5
|
+
import { CameraMIcon } from '@alfalab/icons-glyph/CameraMIcon';
|
|
6
|
+
import { AlfaBankLIcon } from '@alfalab/icons-logotype/AlfaBankLIcon';
|
|
7
|
+
import { MastercardLIcon } from '@alfalab/icons-logotype/MastercardLIcon';
|
|
8
|
+
import { MirXxlIcon } from '@alfalab/icons-logotype/MirXxlIcon';
|
|
9
|
+
import { VisaXxlIcon } from '@alfalab/icons-logotype/VisaXxlIcon';
|
|
10
|
+
|
|
11
|
+
import { validateCardNumber } from './utils';
|
|
12
|
+
|
|
13
|
+
import styles from './index.module.css';
|
|
14
|
+
|
|
15
|
+
export type BankCardProps = {
|
|
16
|
+
/**
|
|
17
|
+
* Дополнительный класс
|
|
18
|
+
*/
|
|
19
|
+
className?: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Цвет фона карты
|
|
23
|
+
*/
|
|
24
|
+
backgroundColor?: string;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Иконка логотипа банка (размер L)
|
|
28
|
+
*/
|
|
29
|
+
bankLogo?: ReactNode;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Лэйбл поля ввода
|
|
33
|
+
*/
|
|
34
|
+
inputLabel?: string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Значение поля ввода
|
|
38
|
+
*/
|
|
39
|
+
value?: string;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Обработчик ввода
|
|
43
|
+
*/
|
|
44
|
+
onChange?: (event: ChangeEvent<HTMLInputElement>, payload: { value: string }) => void;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Обработчик вызова камеры
|
|
48
|
+
*/
|
|
49
|
+
onUsePhoto?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Идентификатор для систем автоматизированного тестирования
|
|
53
|
+
*/
|
|
54
|
+
dataTestId?: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// prettier-ignore
|
|
58
|
+
const cardMask = [/\d/, /\d/, /\d/, /\d/, ' ', /\d/, /\d/, /\d/, /\d/, ' ', /\d/, /\d/, /\d/, /\d/, ' ', /\d/, /\d/, /\d/, /\d/];
|
|
59
|
+
// prettier-ignore
|
|
60
|
+
const accountNumberMask = [/\d/, /\d/, /\d/, /\d/, ' ', /\d/, /\d/, /\d/, /\d/, ' ', /\d/, /\d/, /\d/, /\d/, ' ', /\d/, /\d/, /\d/, /\d/, ' ', /\d/, /\d/, /\d/, /\d/];
|
|
61
|
+
|
|
62
|
+
const getBrandIcon = (value = '') => {
|
|
63
|
+
// Показываем логотип только после ввода всех цифр карты
|
|
64
|
+
if (value.replace(/\s/g, '').length === 16 && validateCardNumber(value)) {
|
|
65
|
+
if (value.startsWith('2')) return <MirXxlIcon />;
|
|
66
|
+
if (value.startsWith('4')) return <VisaXxlIcon />;
|
|
67
|
+
if (value.startsWith('5')) return <MastercardLIcon />;
|
|
68
|
+
if (value.startsWith('6')) return <MastercardLIcon />;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return null;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const BankCard = React.forwardRef<HTMLInputElement, BankCardProps>(
|
|
75
|
+
(
|
|
76
|
+
{
|
|
77
|
+
bankLogo = <AlfaBankLIcon />,
|
|
78
|
+
backgroundColor = '#EF3124',
|
|
79
|
+
inputLabel = 'Номер карты или счёта',
|
|
80
|
+
value,
|
|
81
|
+
className,
|
|
82
|
+
onUsePhoto,
|
|
83
|
+
onChange,
|
|
84
|
+
dataTestId,
|
|
85
|
+
},
|
|
86
|
+
ref,
|
|
87
|
+
) => {
|
|
88
|
+
const uncontrolled = value === undefined;
|
|
89
|
+
|
|
90
|
+
const [brandIcon, setBrandIcon] = useState<ReactNode>(getBrandIcon(value));
|
|
91
|
+
|
|
92
|
+
const getMask = useCallback(
|
|
93
|
+
(newValue: string) =>
|
|
94
|
+
newValue.length <= cardMask.length ? cardMask : accountNumberMask,
|
|
95
|
+
[],
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const handleInputChange = useCallback(
|
|
99
|
+
(event: ChangeEvent<HTMLInputElement>, payload: { value: string }) => {
|
|
100
|
+
if (uncontrolled) {
|
|
101
|
+
setBrandIcon(getBrandIcon(event.target.value));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (onChange) {
|
|
105
|
+
onChange(event, payload);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
[onChange, uncontrolled],
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const renderRightAddons = useCallback(
|
|
112
|
+
() => (
|
|
113
|
+
<button type='button' className={styles.usePhoto} onClick={onUsePhoto}>
|
|
114
|
+
<CameraMIcon />
|
|
115
|
+
</button>
|
|
116
|
+
),
|
|
117
|
+
[onUsePhoto],
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
setBrandIcon(getBrandIcon(value));
|
|
122
|
+
}, [value]);
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div className={cn(styles.component, className)}>
|
|
126
|
+
<div className={styles.aspectRatioContainer}>
|
|
127
|
+
<div className={styles.content} style={{ backgroundColor }}>
|
|
128
|
+
<div className={styles.bankLogo}>{bankLogo}</div>
|
|
129
|
+
|
|
130
|
+
<MaskedInput
|
|
131
|
+
ref={ref}
|
|
132
|
+
value={value}
|
|
133
|
+
mask={getMask}
|
|
134
|
+
block={true}
|
|
135
|
+
label={inputLabel}
|
|
136
|
+
size='m'
|
|
137
|
+
rightAddons={renderRightAddons()}
|
|
138
|
+
inputClassName={styles.input}
|
|
139
|
+
labelClassName={styles.label}
|
|
140
|
+
filledClassName={styles.filled}
|
|
141
|
+
focusedClassName={styles.focused}
|
|
142
|
+
onChange={handleInputChange}
|
|
143
|
+
dataTestId={dataTestId}
|
|
144
|
+
inputMode='numeric'
|
|
145
|
+
pattern='[0-9]*'
|
|
146
|
+
/>
|
|
147
|
+
|
|
148
|
+
{brandIcon && <div className={styles.brandLogo}>{brandIcon}</div>}
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
},
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
BankCard.defaultProps = {
|
|
157
|
+
bankLogo: <AlfaBankLIcon />,
|
|
158
|
+
backgroundColor: '#EF3124',
|
|
159
|
+
inputLabel: 'Номер карты или счёта',
|
|
160
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
@import '@alfalab/core-components-themes/src/default.css';
|
|
2
|
+
|
|
3
|
+
/* purgecss ignore */
|
|
4
|
+
.component {
|
|
5
|
+
/* TODO: как это будет собираться и работать в webView */
|
|
6
|
+
--form-control-border-radius: 0;
|
|
7
|
+
--form-control-border-bottom: 1px solid var(--color-black-60);
|
|
8
|
+
--form-control-bg-color: transparent;
|
|
9
|
+
|
|
10
|
+
/* focus */
|
|
11
|
+
--form-control-focus-border-bottom: 1px solid var(--color-black);
|
|
12
|
+
--form-control-focus-shadow: transparent;
|
|
13
|
+
|
|
14
|
+
/* hover */
|
|
15
|
+
--form-control-hover-bg-color: transparent;
|
|
16
|
+
--form-control-focus-bg-color: transparent;
|
|
17
|
+
--form-control-font-family: var(--font-family-styrene);
|
|
18
|
+
|
|
19
|
+
position: relative;
|
|
20
|
+
max-width: 343px;
|
|
21
|
+
|
|
22
|
+
font-family: var(--font-family-styrene);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.aspectRatioContainer {
|
|
26
|
+
/* Эталонный размер 343x216 */
|
|
27
|
+
padding-bottom: 63%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.content {
|
|
31
|
+
position: absolute;
|
|
32
|
+
top: 0;
|
|
33
|
+
right: 0;
|
|
34
|
+
bottom: 0;
|
|
35
|
+
left: 0;
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
padding-left: var(--gap-l);
|
|
39
|
+
padding-right: var(--gap-l);
|
|
40
|
+
border-radius: var(--border-radius-l);
|
|
41
|
+
box-sizing: border-box;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.label {
|
|
45
|
+
left: 0;
|
|
46
|
+
transform: translateY(-1px);
|
|
47
|
+
color: var(--color-black-60);
|
|
48
|
+
font-size: 20px;
|
|
49
|
+
line-height: 24px;
|
|
50
|
+
|
|
51
|
+
@media (max-width: 360px) {
|
|
52
|
+
font-size: 16px;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.focused .label,
|
|
57
|
+
.filled .label {
|
|
58
|
+
color: var(--color-black);
|
|
59
|
+
transform: translateY(-19px) scale(0.6);
|
|
60
|
+
|
|
61
|
+
@media (max-width: 360px) {
|
|
62
|
+
transform: translateY(-22px) scale(0.75);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.focused:before {
|
|
67
|
+
transform: scale(1) !important;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.input {
|
|
71
|
+
@mixin promo_xsmall;
|
|
72
|
+
|
|
73
|
+
color: var(--color-black);
|
|
74
|
+
caret-color: var(--color-black);
|
|
75
|
+
|
|
76
|
+
/* TODO: обсудить с дизайнером, почему инпут не соответствует дизайн-системе, либо уменьшить каскад в form-control */
|
|
77
|
+
padding-left: 0 !important;
|
|
78
|
+
padding-bottom: var(--gap-2xs) !important;
|
|
79
|
+
|
|
80
|
+
@media (max-width: 360px) {
|
|
81
|
+
font-size: 16px;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.bankLogo {
|
|
86
|
+
position: absolute;
|
|
87
|
+
top: 20px;
|
|
88
|
+
left: 20px;
|
|
89
|
+
|
|
90
|
+
& svg {
|
|
91
|
+
max-height: 32px;
|
|
92
|
+
width: auto;
|
|
93
|
+
display: block;
|
|
94
|
+
|
|
95
|
+
& g {
|
|
96
|
+
fill: var(--color-black);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.brandLogo {
|
|
102
|
+
position: absolute;
|
|
103
|
+
bottom: 20px;
|
|
104
|
+
right: 20px;
|
|
105
|
+
|
|
106
|
+
& svg {
|
|
107
|
+
display: block;
|
|
108
|
+
fill: var(--color-black);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.usePhoto {
|
|
113
|
+
display: block;
|
|
114
|
+
margin: 0;
|
|
115
|
+
padding: 0;
|
|
116
|
+
text-decoration: none;
|
|
117
|
+
background-color: transparent;
|
|
118
|
+
border: 0;
|
|
119
|
+
box-shadow: none;
|
|
120
|
+
user-select: none;
|
|
121
|
+
cursor: pointer;
|
|
122
|
+
outline: none;
|
|
123
|
+
margin-bottom: var(--gap-2xs);
|
|
124
|
+
align-self: flex-end;
|
|
125
|
+
|
|
126
|
+
/* В макете иконка прилегает к правому краю поля, а по дизайн-системе там должен быть отступ */
|
|
127
|
+
margin-right: calc(var(--gap-s) * -1);
|
|
128
|
+
|
|
129
|
+
& svg {
|
|
130
|
+
fill: var(--color-black);
|
|
131
|
+
display: block;
|
|
132
|
+
}
|
|
133
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Component';
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Проверяет номер карты по алгоритму Луна
|
|
3
|
+
*
|
|
4
|
+
* @param cardNumber - номер карты
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
export function validateCardNumber(cardNumber: string) {
|
|
8
|
+
const digits = cardNumber.replace(/\s+/g, '');
|
|
9
|
+
let sum = 0;
|
|
10
|
+
|
|
11
|
+
for (let i = 0; i < digits.length; i++) {
|
|
12
|
+
let cardNum = parseInt(digits[i], 10);
|
|
13
|
+
|
|
14
|
+
if (cardNum > 9) return false;
|
|
15
|
+
|
|
16
|
+
if ((digits.length - i) % 2 === 0) {
|
|
17
|
+
cardNum *= 2;
|
|
18
|
+
|
|
19
|
+
if (cardNum > 9) {
|
|
20
|
+
cardNum -= 9;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
sum += cardNum;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return sum % 10 === 0;
|
|
28
|
+
}
|