@widergy/mobile-ui 2.9.7 → 2.11.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/CHANGELOG.md +14 -0
- package/lib/components/UTCoupon/README.md +69 -0
- package/lib/components/UTCoupon/constants.js +8 -0
- package/lib/components/UTCoupon/index.js +159 -0
- package/lib/components/UTCoupon/proptypes.js +29 -0
- package/lib/components/UTCoupon/theme.js +71 -0
- package/lib/components/UTCoupon/utils.js +40 -0
- package/lib/components/UTPhoneInput/index.js +18 -13
- package/lib/index.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [2.11.0](https://github.com/widergy/mobile-ui/compare/v2.10.0...v2.11.0) (2026-04-13)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* [UGGC-95] UTCoupon ([#493](https://github.com/widergy/mobile-ui/issues/493)) ([11a3367](https://github.com/widergy/mobile-ui/commit/11a336717acfe31e146288981360481b5ad73268))
|
|
7
|
+
|
|
8
|
+
# [2.10.0](https://github.com/widergy/mobile-ui/compare/v2.9.7...v2.10.0) (2026-04-13)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* [PROMI-221] UTPhoneInput soporta valor string u objeto con prefix editable ([#496](https://github.com/widergy/mobile-ui/issues/496)) ([07a8a8b](https://github.com/widergy/mobile-ui/commit/07a8a8bf4101efcd89176119de7a8aa62252f79e))
|
|
14
|
+
|
|
1
15
|
## [2.9.7](https://github.com/widergy/mobile-ui/compare/v2.9.6...v2.9.7) (2026-04-10)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# UTCoupon
|
|
2
|
+
|
|
3
|
+
Tarjeta de cupón seleccionable usada en flujos de pedidos. Muestra imagen de marca, nombre del producto, condiciones de uso, fecha de vencimiento y precio. Soporta tres estados: default, activo y agotado.
|
|
4
|
+
|
|
5
|
+
La forma de ticket (recortes elípticos en los bordes laterales) se implementa con SVG.
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
|
|
9
|
+
| Prop | Tipo | Default | Descripción |
|
|
10
|
+
| ------------ | --------------------------- | ------- | ------------------------------------------------------------------------------------------ |
|
|
11
|
+
| `image` | `number \| { uri: string }` | — | Fuente de imagen para la sección izquierda (misma forma que `Image` source de RN) |
|
|
12
|
+
| `detail` | `string` | — | Texto pequeño superior (ej. municipio, código de producto) |
|
|
13
|
+
| `title` | `string` | — | Texto principal central (ej. "Carga 11Kg") |
|
|
14
|
+
| `subtitle` | `string` | — | Texto secundario inferior (ej. "1 uso mensualmente") |
|
|
15
|
+
| `value` | `string` | — | Texto de la sección derecha (ej. "$2.500") |
|
|
16
|
+
| `active` | `bool` | `false` | Estado seleccionado: borde azul. Mutuamente excluyente con `disabled`. |
|
|
17
|
+
| `disabled` | `bool` | `false` | Estado agotado: overlay gris, `onPress` deshabilitado. Mutuamente excluyente con `active`. |
|
|
18
|
+
| `onPress` | `func` | — | Callback de selección. Se ignora si `disabled` es `true`. |
|
|
19
|
+
| `style` | `shape` | `{}` | Overrides de estilo por subelemento (ver tabla de llaves). |
|
|
20
|
+
| `dataTestId` | `string` | — | ID raíz; los subelementos añaden sufijos automáticamente. |
|
|
21
|
+
|
|
22
|
+
## Uso
|
|
23
|
+
|
|
24
|
+
```jsx
|
|
25
|
+
import { UTCoupon } from '@widergy/mobile-ui';
|
|
26
|
+
|
|
27
|
+
// Default
|
|
28
|
+
<UTCoupon
|
|
29
|
+
image={require('./assets/logo.png')}
|
|
30
|
+
detail="Mun. Las Condes"
|
|
31
|
+
title="Carga 11Kg"
|
|
32
|
+
subtitle="1 uso mensualmente"
|
|
33
|
+
value="$2.500"
|
|
34
|
+
onPress={() => handleSelect(coupon)}
|
|
35
|
+
/>
|
|
36
|
+
|
|
37
|
+
// Activo (seleccionado)
|
|
38
|
+
<UTCoupon
|
|
39
|
+
image={{ uri: 'https://example.com/logo.png' }}
|
|
40
|
+
detail="ANM51-1000220101-3"
|
|
41
|
+
title="Carga 11Kg"
|
|
42
|
+
subtitle="Vence el 16/08/2025"
|
|
43
|
+
value="$2.500"
|
|
44
|
+
active
|
|
45
|
+
onPress={() => handleSelect(coupon)}
|
|
46
|
+
/>
|
|
47
|
+
|
|
48
|
+
// Agotado
|
|
49
|
+
<UTCoupon
|
|
50
|
+
image={require('./assets/logo.png')}
|
|
51
|
+
title="Carga 11Kg"
|
|
52
|
+
subtitle="Sin stock"
|
|
53
|
+
value="$2.500"
|
|
54
|
+
disabled
|
|
55
|
+
/>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Test IDs
|
|
59
|
+
|
|
60
|
+
Dado `dataTestId="coupon"`, los subelementos reciben:
|
|
61
|
+
|
|
62
|
+
| Subelemento | testID |
|
|
63
|
+
| ----------- | ---------------------- |
|
|
64
|
+
| Raíz | `"coupon"` |
|
|
65
|
+
| Imagen | `"coupon.icon"` |
|
|
66
|
+
| Detail | `"coupon.description"` |
|
|
67
|
+
| Title | `"coupon.titleText"` |
|
|
68
|
+
| Subtitle | `"coupon.subtitle"` |
|
|
69
|
+
| Value | `"coupon.label"` |
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const BORDER_RADIUS = 12;
|
|
2
|
+
|
|
3
|
+
export const ICON_SECTION_WIDTH = 80;
|
|
4
|
+
|
|
5
|
+
// Notch (elliptical cutout at top/bottom edges, centered near the icon section right edge)
|
|
6
|
+
export const NOTCH_RADIUS_X = 26; // horizontal semi-axis (notch width = 52)
|
|
7
|
+
export const NOTCH_RADIUS_Y = 8; // vertical semi-axis (notch depth from edge = 8)
|
|
8
|
+
export const NOTCH_CENTER_X = 104; // fixed x position of the notch center
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import React, { memo, useRef, useState } from 'react';
|
|
2
|
+
import { Image, TouchableOpacity, View } from 'react-native';
|
|
3
|
+
import { ClipPath, Defs, Path, Rect, Svg } from 'react-native-svg';
|
|
4
|
+
|
|
5
|
+
import UTLabel from '../UTLabel';
|
|
6
|
+
import { useTheme } from '../../theming';
|
|
7
|
+
import { TEST_ID_CONSTANTS } from '../../constants/testIds';
|
|
8
|
+
|
|
9
|
+
import { defaultProps, propTypes } from './proptypes';
|
|
10
|
+
import { retrieveStyle } from './theme';
|
|
11
|
+
import { ICON_SECTION_WIDTH } from './constants';
|
|
12
|
+
import { buildCouponPath } from './utils';
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
description: detailTestId,
|
|
16
|
+
icon: iconTestId,
|
|
17
|
+
label: valueTestId,
|
|
18
|
+
subtitle: subtitleTestId,
|
|
19
|
+
titleText: titleTestId
|
|
20
|
+
} = TEST_ID_CONSTANTS;
|
|
21
|
+
|
|
22
|
+
const UTCoupon = ({
|
|
23
|
+
active = false,
|
|
24
|
+
dataTestId,
|
|
25
|
+
detail,
|
|
26
|
+
disabled = false,
|
|
27
|
+
image,
|
|
28
|
+
onPress,
|
|
29
|
+
style,
|
|
30
|
+
subtitle,
|
|
31
|
+
title,
|
|
32
|
+
value
|
|
33
|
+
}) => {
|
|
34
|
+
const theme = useTheme();
|
|
35
|
+
const themedStyles = retrieveStyle({ theme, style });
|
|
36
|
+
const [cardLayout, setCardLayout] = useState({ width: 0, height: 0 });
|
|
37
|
+
const clipId = useRef(`clip${Math.random()}`).current;
|
|
38
|
+
|
|
39
|
+
const handleLayout = ({ nativeEvent: { layout } }) => {
|
|
40
|
+
if (layout.width !== cardLayout.width || layout.height !== cardLayout.height) {
|
|
41
|
+
setCardLayout({ width: layout.width, height: layout.height });
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const couponPath = cardLayout.width > 0 ? buildCouponPath(cardLayout.width, cardLayout.height) : null;
|
|
46
|
+
|
|
47
|
+
const couponBorderPath =
|
|
48
|
+
active && cardLayout.width > 0 ? buildCouponPath(cardLayout.width, cardLayout.height, 1) : null; // Border path inset by 1px (= strokeWidth/2) so the full 2px stroke renders
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<View testID={dataTestId} style={themedStyles.root} onLayout={handleLayout}>
|
|
52
|
+
{couponPath && (
|
|
53
|
+
<Svg width={cardLayout.width} height={cardLayout.height} style={themedStyles.absoluteFill}>
|
|
54
|
+
<Defs>
|
|
55
|
+
<ClipPath id={clipId}>
|
|
56
|
+
<Path d={couponPath} />
|
|
57
|
+
</ClipPath>
|
|
58
|
+
</Defs>
|
|
59
|
+
<Path d={couponPath} fill={theme.Palette.light['01']} />
|
|
60
|
+
<Rect
|
|
61
|
+
x={0}
|
|
62
|
+
y={0}
|
|
63
|
+
width={ICON_SECTION_WIDTH}
|
|
64
|
+
height={cardLayout.height}
|
|
65
|
+
fill={theme.Palette.information['01']}
|
|
66
|
+
clipPath={`url(#${clipId})`}
|
|
67
|
+
/>
|
|
68
|
+
</Svg>
|
|
69
|
+
)}
|
|
70
|
+
|
|
71
|
+
<TouchableOpacity
|
|
72
|
+
style={themedStyles.contentRow}
|
|
73
|
+
onPress={disabled ? undefined : onPress}
|
|
74
|
+
activeOpacity={disabled ? 1 : 0.7}
|
|
75
|
+
>
|
|
76
|
+
<View style={themedStyles.iconContainer}>
|
|
77
|
+
{image && (
|
|
78
|
+
<Image
|
|
79
|
+
testID={dataTestId ? `${dataTestId}.${iconTestId}` : undefined}
|
|
80
|
+
source={image}
|
|
81
|
+
style={[themedStyles.image, { height: cardLayout.height }]}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
</View>
|
|
85
|
+
|
|
86
|
+
<View style={themedStyles.rightSection}>
|
|
87
|
+
<View style={themedStyles.textContainer}>
|
|
88
|
+
{detail && (
|
|
89
|
+
<UTLabel
|
|
90
|
+
dataTestId={dataTestId ? `${dataTestId}.${detailTestId}` : undefined}
|
|
91
|
+
colorTheme="gray"
|
|
92
|
+
variant="xsmall"
|
|
93
|
+
style={themedStyles.detail}
|
|
94
|
+
>
|
|
95
|
+
{detail}
|
|
96
|
+
</UTLabel>
|
|
97
|
+
)}
|
|
98
|
+
{title && (
|
|
99
|
+
<UTLabel
|
|
100
|
+
dataTestId={dataTestId ? `${dataTestId}.${titleTestId}` : undefined}
|
|
101
|
+
colorTheme="dark"
|
|
102
|
+
variant="title2"
|
|
103
|
+
weight="medium"
|
|
104
|
+
style={themedStyles.title}
|
|
105
|
+
>
|
|
106
|
+
{title}
|
|
107
|
+
</UTLabel>
|
|
108
|
+
)}
|
|
109
|
+
{subtitle && (
|
|
110
|
+
<UTLabel
|
|
111
|
+
dataTestId={dataTestId ? `${dataTestId}.${subtitleTestId}` : undefined}
|
|
112
|
+
colorTheme="gray"
|
|
113
|
+
variant="small"
|
|
114
|
+
style={themedStyles.subtitle}
|
|
115
|
+
>
|
|
116
|
+
{subtitle}
|
|
117
|
+
</UTLabel>
|
|
118
|
+
)}
|
|
119
|
+
</View>
|
|
120
|
+
|
|
121
|
+
{value && (
|
|
122
|
+
<UTLabel
|
|
123
|
+
dataTestId={dataTestId ? `${dataTestId}.${valueTestId}` : undefined}
|
|
124
|
+
colorTheme="accent"
|
|
125
|
+
variant="title2"
|
|
126
|
+
weight="medium"
|
|
127
|
+
style={themedStyles.value}
|
|
128
|
+
>
|
|
129
|
+
{value}
|
|
130
|
+
</UTLabel>
|
|
131
|
+
)}
|
|
132
|
+
</View>
|
|
133
|
+
</TouchableOpacity>
|
|
134
|
+
|
|
135
|
+
{disabled && (
|
|
136
|
+
<View style={[themedStyles.absoluteFill, themedStyles.disabledOverlay]} pointerEvents="none" />
|
|
137
|
+
)}
|
|
138
|
+
|
|
139
|
+
{couponBorderPath && (
|
|
140
|
+
<View style={themedStyles.absoluteFill} pointerEvents="none">
|
|
141
|
+
<Svg width={cardLayout.width} height={cardLayout.height} style={themedStyles.absoluteFill}>
|
|
142
|
+
<Path
|
|
143
|
+
d={couponBorderPath}
|
|
144
|
+
fill="none"
|
|
145
|
+
stroke={theme.Palette.accent['05']}
|
|
146
|
+
strokeWidth={2}
|
|
147
|
+
strokeLinejoin="round"
|
|
148
|
+
/>
|
|
149
|
+
</Svg>
|
|
150
|
+
</View>
|
|
151
|
+
)}
|
|
152
|
+
</View>
|
|
153
|
+
);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
UTCoupon.propTypes = propTypes;
|
|
157
|
+
UTCoupon.defaultProps = defaultProps;
|
|
158
|
+
|
|
159
|
+
export default memo(UTCoupon);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { bool, func, number, object, oneOfType, shape, string } from 'prop-types';
|
|
2
|
+
|
|
3
|
+
export const propTypes = {
|
|
4
|
+
image: oneOfType([number, shape({ uri: string })]),
|
|
5
|
+
detail: string,
|
|
6
|
+
title: string,
|
|
7
|
+
subtitle: string,
|
|
8
|
+
value: string,
|
|
9
|
+
active: bool,
|
|
10
|
+
disabled: bool,
|
|
11
|
+
onPress: func,
|
|
12
|
+
dataTestId: string,
|
|
13
|
+
style: shape({
|
|
14
|
+
root: object,
|
|
15
|
+
iconContainer: object,
|
|
16
|
+
image: object,
|
|
17
|
+
textContainer: object,
|
|
18
|
+
detail: object,
|
|
19
|
+
title: object,
|
|
20
|
+
subtitle: object,
|
|
21
|
+
value: object
|
|
22
|
+
})
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const defaultProps = {
|
|
26
|
+
active: false,
|
|
27
|
+
disabled: false,
|
|
28
|
+
style: {}
|
|
29
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { getShadow, mergeMultipleStyles } from '../../utils/styleUtils';
|
|
2
|
+
|
|
3
|
+
import { BORDER_RADIUS, ICON_SECTION_WIDTH } from './constants';
|
|
4
|
+
|
|
5
|
+
const ownStyles = {
|
|
6
|
+
root: {
|
|
7
|
+
width: '100%'
|
|
8
|
+
},
|
|
9
|
+
absoluteFill: {
|
|
10
|
+
position: 'absolute',
|
|
11
|
+
left: 0,
|
|
12
|
+
right: 0,
|
|
13
|
+
top: 0,
|
|
14
|
+
bottom: 0
|
|
15
|
+
},
|
|
16
|
+
contentRow: {
|
|
17
|
+
flexDirection: 'row',
|
|
18
|
+
alignItems: 'stretch'
|
|
19
|
+
},
|
|
20
|
+
iconContainer: {
|
|
21
|
+
width: ICON_SECTION_WIDTH,
|
|
22
|
+
alignSelf: 'stretch',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
justifyContent: 'center',
|
|
25
|
+
borderTopLeftRadius: BORDER_RADIUS,
|
|
26
|
+
borderBottomLeftRadius: BORDER_RADIUS,
|
|
27
|
+
overflow: 'hidden'
|
|
28
|
+
},
|
|
29
|
+
image: {
|
|
30
|
+
position: 'absolute',
|
|
31
|
+
top: 0,
|
|
32
|
+
left: 0,
|
|
33
|
+
width: ICON_SECTION_WIDTH,
|
|
34
|
+
resizeMode: 'cover'
|
|
35
|
+
},
|
|
36
|
+
rightSection: {
|
|
37
|
+
flex: 1,
|
|
38
|
+
flexDirection: 'row',
|
|
39
|
+
alignItems: 'center',
|
|
40
|
+
paddingVertical: 18,
|
|
41
|
+
paddingLeft: 14,
|
|
42
|
+
paddingRight: 14
|
|
43
|
+
},
|
|
44
|
+
textContainer: {
|
|
45
|
+
flex: 1,
|
|
46
|
+
gap: 4
|
|
47
|
+
},
|
|
48
|
+
detail: {},
|
|
49
|
+
title: {},
|
|
50
|
+
subtitle: {},
|
|
51
|
+
value: {
|
|
52
|
+
marginLeft: 8,
|
|
53
|
+
textAlign: 'right'
|
|
54
|
+
},
|
|
55
|
+
disabledOverlay: {
|
|
56
|
+
borderRadius: BORDER_RADIUS
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const getThemeStyles = theme => ({
|
|
61
|
+
root: {
|
|
62
|
+
borderRadius: BORDER_RADIUS * 2,
|
|
63
|
+
...getShadow({ level: 1, theme })
|
|
64
|
+
},
|
|
65
|
+
disabledOverlay: {
|
|
66
|
+
backgroundColor: theme.colors.effect
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export const retrieveStyle = ({ theme, style = {} }) =>
|
|
71
|
+
mergeMultipleStyles(ownStyles, getThemeStyles(theme), theme?.UTCoupon, style);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { BORDER_RADIUS, NOTCH_CENTER_X, NOTCH_RADIUS_X, NOTCH_RADIUS_Y } from './constants';
|
|
2
|
+
|
|
3
|
+
// Traces a rounded rectangle with two concave half-ellipse notches on the top and bottom edges, centered at x = NOTCH_CENTER_X.
|
|
4
|
+
// `inset` shifts all edges inward by this many pixels — use strokeWidth/2 so the full stroke renders within the SVG viewport without clipping.
|
|
5
|
+
export const buildCouponPath = (width, height, inset = 0) => {
|
|
6
|
+
// KAPPA = 4*(√2−1)/3 ≈ 0.5523: the control-point ratio that best fits a cubic bezier to a quarter-ellipse arc (minimises radial error at t=0.5).
|
|
7
|
+
const KAPPA = 0.5523;
|
|
8
|
+
|
|
9
|
+
const cornerR = BORDER_RADIUS - inset; // effective corner radius after inset
|
|
10
|
+
const notchX = NOTCH_CENTER_X; // x position of the notch center
|
|
11
|
+
const notchRx = NOTCH_RADIUS_X; // notch horizontal semi-axis (width)
|
|
12
|
+
const notchRy = NOTCH_RADIUS_Y; // notch vertical semi-axis (depth)
|
|
13
|
+
const notchCp = notchRx * KAPPA; // bezier control-point offset for notch curves
|
|
14
|
+
const cornerCp = cornerR * KAPPA; // bezier control-point offset for corner curves
|
|
15
|
+
|
|
16
|
+
return [
|
|
17
|
+
`M ${cornerR + inset} ${inset}`,
|
|
18
|
+
`L ${notchX - notchRx} ${inset}`,
|
|
19
|
+
// Top notch: two beziers forming a half-ellipse curving DOWNWARD into the card.
|
|
20
|
+
`C ${notchX - notchRx + notchCp} ${inset} ${notchX - notchCp} ${inset + notchRy} ${notchX} ${inset + notchRy}`,
|
|
21
|
+
`C ${notchX + notchCp} ${inset + notchRy} ${notchX + notchRx - notchCp} ${inset} ${notchX + notchRx} ${inset}`,
|
|
22
|
+
`L ${width - cornerR - inset} ${inset}`,
|
|
23
|
+
// Top-right corner
|
|
24
|
+
`C ${width - cornerR - inset + cornerCp} ${inset} ${width - inset} ${inset + cornerCp} ${width - inset} ${cornerR + inset}`,
|
|
25
|
+
`L ${width - inset} ${height - cornerR - inset}`,
|
|
26
|
+
// Bottom-right corner
|
|
27
|
+
`C ${width - inset} ${height - cornerR - inset + cornerCp} ${width - cornerR - inset + cornerCp} ${height - inset} ${width - cornerR - inset} ${height - inset}`,
|
|
28
|
+
`L ${notchX + notchRx} ${height - inset}`,
|
|
29
|
+
// Bottom notch: two beziers forming a half-ellipse curving UPWARD into the card.
|
|
30
|
+
`C ${notchX + notchRx - notchCp} ${height - inset} ${notchX + notchCp} ${height - inset - notchRy} ${notchX} ${height - inset - notchRy}`,
|
|
31
|
+
`C ${notchX - notchCp} ${height - inset - notchRy} ${notchX - notchRx + notchCp} ${height - inset} ${notchX - notchRx} ${height - inset}`,
|
|
32
|
+
`L ${cornerR + inset} ${height - inset}`,
|
|
33
|
+
// Bottom-left corner
|
|
34
|
+
`C ${cornerR + inset - cornerCp} ${height - inset} ${inset} ${height - cornerR - inset + cornerCp} ${inset} ${height - cornerR - inset}`,
|
|
35
|
+
`L ${inset} ${cornerR + inset}`,
|
|
36
|
+
// Top-left corner
|
|
37
|
+
`C ${inset} ${cornerR + inset - cornerCp} ${cornerR + inset - cornerCp} ${inset} ${cornerR + inset} ${inset}`,
|
|
38
|
+
'Z'
|
|
39
|
+
].join(' ');
|
|
40
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { PureComponent, createRef } from 'react';
|
|
2
2
|
import { View } from 'react-native';
|
|
3
|
-
import { bool, elementType, func, number, shape, string } from 'prop-types';
|
|
3
|
+
import { bool, elementType, func, number, oneOfType, shape, string } from 'prop-types';
|
|
4
4
|
|
|
5
5
|
import { COMPONENT_KEYS } from '../UTBaseInputField/constants';
|
|
6
6
|
import { formatErrorToValidation } from '../UTValidation/utils';
|
|
@@ -53,9 +53,11 @@ class UTPhoneInput extends PureComponent {
|
|
|
53
53
|
|
|
54
54
|
changeText = updateFunction => {
|
|
55
55
|
const { areaCode, phoneNumber } = this.state;
|
|
56
|
-
const { withAreaCode } = this.props;
|
|
56
|
+
const { withAreaCode, prefix, editablePrefix } = this.props;
|
|
57
57
|
|
|
58
|
-
if (
|
|
58
|
+
if (prefix !== undefined && editablePrefix) {
|
|
59
|
+
updateFunction({ number: phoneNumber, prefix });
|
|
60
|
+
} else if (withAreaCode && (areaCode || phoneNumber)) {
|
|
59
61
|
updateFunction(`${areaCode}-${phoneNumber}`);
|
|
60
62
|
} else if (!withAreaCode) {
|
|
61
63
|
updateFunction(phoneNumber);
|
|
@@ -137,10 +139,14 @@ class UTPhoneInput extends PureComponent {
|
|
|
137
139
|
};
|
|
138
140
|
|
|
139
141
|
updateValue = () => {
|
|
140
|
-
const { value, withAreaCode } = this.props;
|
|
142
|
+
const { value, withAreaCode, prefix, editablePrefix, onChange } = this.props;
|
|
141
143
|
if (value) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
const valueNumber = typeof value === 'object' ? value.number : value;
|
|
145
|
+
if (prefix !== undefined && editablePrefix) {
|
|
146
|
+
this.setState({ phoneNumber: valueNumber });
|
|
147
|
+
if (typeof value !== 'object') onChange({ number: valueNumber, prefix });
|
|
148
|
+
} else if (withAreaCode) {
|
|
149
|
+
const phone = valueNumber.split('-');
|
|
144
150
|
if (phone.length === 2) {
|
|
145
151
|
const newAreaCodeState = this.getAreaCodeState(phone[0]);
|
|
146
152
|
this.setState({
|
|
@@ -149,7 +155,7 @@ class UTPhoneInput extends PureComponent {
|
|
|
149
155
|
});
|
|
150
156
|
}
|
|
151
157
|
} else {
|
|
152
|
-
this.setState({ phoneNumber:
|
|
158
|
+
this.setState({ phoneNumber: valueNumber });
|
|
153
159
|
}
|
|
154
160
|
} else {
|
|
155
161
|
this.setState({
|
|
@@ -164,7 +170,7 @@ class UTPhoneInput extends PureComponent {
|
|
|
164
170
|
render() {
|
|
165
171
|
const {
|
|
166
172
|
areaCodePlaceholder,
|
|
167
|
-
|
|
173
|
+
prefix,
|
|
168
174
|
dataTestId,
|
|
169
175
|
disabled,
|
|
170
176
|
error,
|
|
@@ -235,9 +241,7 @@ class UTPhoneInput extends PureComponent {
|
|
|
235
241
|
disabled={(withAreaCode && !isValidCode) || disabled || readOnly}
|
|
236
242
|
error={hasError && !areaCodeError}
|
|
237
243
|
leftAdornments={
|
|
238
|
-
|
|
239
|
-
? [{ name: COMPONENT_KEYS.PREFIX, props: { text: countryCode } }]
|
|
240
|
-
: []
|
|
244
|
+
prefix && !withAreaCode ? [{ name: COMPONENT_KEYS.PREFIX, props: { text: prefix } }] : []
|
|
241
245
|
}
|
|
242
246
|
maxLength={withAreaCode ? maxLength - (areaCode?.length || 0) : maxLength}
|
|
243
247
|
onBlur={this.handleSecondBlur}
|
|
@@ -288,7 +292,6 @@ UTPhoneInput.defaultProps = {
|
|
|
288
292
|
|
|
289
293
|
UTPhoneInput.propTypes = {
|
|
290
294
|
areaCodePlaceholder: string,
|
|
291
|
-
countryCode: string,
|
|
292
295
|
dataTestId: string,
|
|
293
296
|
disabled: bool,
|
|
294
297
|
error: string,
|
|
@@ -300,6 +303,8 @@ UTPhoneInput.propTypes = {
|
|
|
300
303
|
onFocus: func,
|
|
301
304
|
onSubmitEditing: func,
|
|
302
305
|
placeholder: string,
|
|
306
|
+
prefix: string,
|
|
307
|
+
editablePrefix: bool,
|
|
303
308
|
readOnly: bool,
|
|
304
309
|
required: bool,
|
|
305
310
|
RightIcon: elementType,
|
|
@@ -310,7 +315,7 @@ UTPhoneInput.propTypes = {
|
|
|
310
315
|
invalidAreaCodeError: string
|
|
311
316
|
}),
|
|
312
317
|
validations: validationDataProptypes,
|
|
313
|
-
value: string,
|
|
318
|
+
value: oneOfType([string, shape({ number: string, prefix: string })]),
|
|
314
319
|
withAreaCode: bool
|
|
315
320
|
};
|
|
316
321
|
|
package/lib/index.js
CHANGED
|
@@ -44,6 +44,7 @@ export { default as UTButton } from './components/UTButton';
|
|
|
44
44
|
export { default as UTButtonGroup } from './components/UTButtonGroup';
|
|
45
45
|
export { default as UTCBUInput } from './components/UTCBUInput';
|
|
46
46
|
export { default as UTCheckBox } from './components/UTCheckBox';
|
|
47
|
+
export { default as UTCoupon } from './components/UTCoupon';
|
|
47
48
|
export { default as UTCuit } from './components/UTCuit';
|
|
48
49
|
export { default as UTCheckList } from './components/UTCheckList';
|
|
49
50
|
export { default as UTDataCategory } from './components/UTDataCategory';
|
package/package.json
CHANGED