Skip to content

Commit 03dbf70

Browse files
committed
feat: factorize listitem, phone number input and modalcontent
1 parent 5fde1da commit 03dbf70

21 files changed

Lines changed: 3109 additions & 410 deletions

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"license": "MIT",
3737
"dependencies": {
3838
"@expo/vector-icons": "9.0.0",
39+
"countries-list": "2.4.2",
3940
"deepmerge": "^3.0.0",
4041
"exenv": "1.2.2",
4142
"focus-trap": "4.0.2",

src/components/Avatar/Avatar.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import Avatar from './Avatar';
2525
### Using image source
2626

2727
<Playground>
28-
<Avatar source="https://picsum.photos/200/200" />
28+
<Avatar source={{ uri: 'https://picsum.photos/200/200' }} />
2929
</Playground>
3030

3131
### Avatar sizes

src/components/Avatar/Avatar.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { Image, View } from 'react-native';
2+
import { Image, ImageSourcePropType, View } from 'react-native';
33
import { DeepPartial } from 'ts-essentials';
44

55
import { Theme, withTheme } from '../../theme';
@@ -32,7 +32,7 @@ export interface AvatarProps {
3232
* The source attribute of the image.
3333
* When it's not available, render initials instead.
3434
*/
35-
source?: string;
35+
source?: ImageSourcePropType;
3636

3737
/**
3838
* The size of the avatar.
@@ -91,7 +91,7 @@ export const AvatarBase = (props: AvatarProps) => {
9191
theme,
9292

9393
source,
94-
size = 40,
94+
size = 48,
9595
name,
9696
isSolid = false,
9797
hashValue,
@@ -142,13 +142,8 @@ export const AvatarBase = (props: AvatarProps) => {
142142
{initials}
143143
</Text>
144144
)}
145-
{!imageUnavailable && (
146-
<Image
147-
source={{
148-
uri: source,
149-
}}
150-
style={imageStyle}
151-
/>
145+
{!imageUnavailable && !!source && (
146+
<Image source={source} style={imageStyle} />
152147
)}
153148
</View>
154149
);

src/components/Button/Button.styles.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export const getButtonVariables = (theme: Theme): ButtonVariables => {
124124
default: {
125125
backgroundColor: theme.colors.background.plain,
126126
borderColor: theme.colors.text.default,
127+
borderWidth: 4,
127128
color: theme.colors.text.default,
128129

129130
focusColor: theme.colors.background.tint2,
@@ -132,6 +133,7 @@ export const getButtonVariables = (theme: Theme): ButtonVariables => {
132133
danger: {
133134
backgroundColor: theme.colors.background.plain,
134135
borderColor: theme.colors.border.danger,
136+
borderWidth: 4,
135137
color: theme.colors.text.danger,
136138

137139
focusColor: theme.colors.background.danger.focusLight,
@@ -140,6 +142,7 @@ export const getButtonVariables = (theme: Theme): ButtonVariables => {
140142
primary: {
141143
backgroundColor: theme.colors.background.plain,
142144
borderColor: theme.colors.border.primary,
145+
borderWidth: 4,
143146
color: theme.colors.text.primary,
144147

145148
focusColor: theme.colors.background.primary.focusLight,
@@ -148,6 +151,7 @@ export const getButtonVariables = (theme: Theme): ButtonVariables => {
148151
secondary: {
149152
backgroundColor: theme.colors.background.plain,
150153
borderColor: theme.colors.border.secondary,
154+
borderWidth: 4,
151155
color: theme.colors.text.secondary,
152156

153157
focusColor: theme.colors.background.secondary.focusLight,
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
name: PhoneNumberInput
3+
menu: Components
4+
---
5+
6+
import { Playground, PropsTable } from 'docz';
7+
import PhoneNumberInput from './PhoneNumberInput';
8+
import { State } from 'react-powerplug';
9+
10+
## Usage
11+
12+
### Default
13+
14+
<Playground>
15+
<State initial={{ countryCode: 'US', phoneNumber: '' }}>
16+
{({ state, setState }) => (
17+
<PhoneNumberInput
18+
label="Select your country"
19+
onChangeCountryCode={countryCode => setState({ countryCode })}
20+
onChangePhoneNumber={phoneNumber => setState({ phoneNumber })}
21+
phoneNumber={state.phoneNumber}
22+
countryCode={state.countryCode}
23+
placeholder="Enter your phone number"
24+
/>
25+
)}
26+
</State>
27+
</Playground>
28+
29+
## Props
30+
31+
<PropsTable of={PhoneNumberInput} />
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { TextStyle, ViewStyle } from 'react-native';
2+
3+
import { Theme } from '../../theme/ThemeInterface';
4+
5+
export interface PhoneNumberInputSizes {
6+
small: TextStyle;
7+
medium: TextStyle;
8+
large: TextStyle;
9+
}
10+
11+
export type PhoneNumberInputSize = keyof PhoneNumberInputSizes;
12+
13+
export interface PhoneNumberInputStyles {
14+
containerStyle: ViewStyle;
15+
}
16+
17+
export interface PhoneNumberInputStylesProps {
18+
size: PhoneNumberInputSize;
19+
}
20+
21+
export type GetPhoneNumberInputStyles = (
22+
textInputStylesProps: PhoneNumberInputStylesProps,
23+
theme: Theme,
24+
) => PhoneNumberInputStyles;
25+
26+
export const getPhoneNumberInputStyles: GetPhoneNumberInputStyles = (
27+
{ size },
28+
theme,
29+
) => {
30+
return {
31+
containerStyle: {
32+
flexDirection: 'row',
33+
},
34+
};
35+
};
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { countries as countryList } from 'countries-list';
2+
import * as React from 'react';
3+
import { FlatList, View } from 'react-native';
4+
import { Toggle } from 'react-powerplug';
5+
import { DeepPartial } from 'ts-essentials';
6+
7+
import { Icon } from '../../icons';
8+
import { Theme, withTheme } from '../../theme';
9+
import { mergeStyles, ReplaceReturnType } from '../../utils/mergeStyles';
10+
import { Button } from '../Button';
11+
import { Spacing } from '../Layout';
12+
import { ListItem } from '../ListItem';
13+
import { Modal } from '../Modal';
14+
import ModalContent from '../Modal/ModalContent';
15+
import { Heading } from '../Typography';
16+
import {
17+
GetPhoneNumberInputStyles,
18+
getPhoneNumberInputStyles,
19+
PhoneNumberInputStyles,
20+
} from './PhoneNumberInput.styles';
21+
import TextInput from './TextInput';
22+
23+
export interface PhoneNumberInputProps {
24+
countryCode?: string;
25+
onChangeCountryCode?: (countryCode: string) => void;
26+
phoneNumber?: string;
27+
onChangePhoneNumber?: (phoneNumber: string) => void;
28+
theme: Theme;
29+
/** Label displayed when showing country selection */
30+
label?: string;
31+
placeholder?: string;
32+
getStyles?: ReplaceReturnType<
33+
GetPhoneNumberInputStyles,
34+
DeepPartial<PhoneNumberInputStyles>
35+
>;
36+
}
37+
38+
const countries = (() => {
39+
return Object.keys(countryList).map(countryCode => ({
40+
countryCode,
41+
key: countryCode,
42+
...countryList[countryCode],
43+
}));
44+
})();
45+
46+
const PhoneNumberInputBase = (props: PhoneNumberInputProps) => {
47+
const {
48+
countryCode = 'US',
49+
phoneNumber,
50+
onChangeCountryCode,
51+
onChangePhoneNumber,
52+
placeholder,
53+
label,
54+
theme,
55+
getStyles,
56+
} = props;
57+
58+
const { containerStyle } = mergeStyles(getPhoneNumberInputStyles, getStyles)(
59+
{},
60+
theme,
61+
);
62+
63+
return (
64+
<View style={containerStyle}>
65+
<Toggle initial={false}>
66+
{({ on, set }) => {
67+
return (
68+
<>
69+
<Button
70+
onPress={() => set(true)}
71+
appearance="outline"
72+
getStyles={() => ({
73+
buttonStyle: {
74+
borderBottomRightRadius: 0,
75+
borderColor: theme.colors.border.muted,
76+
borderTopRightRadius: 0,
77+
borderWidth: 1,
78+
},
79+
})}
80+
iconAfter={
81+
<Icon
82+
size={20}
83+
color={theme.colors.text.default}
84+
name="chevron-down"
85+
/>
86+
}
87+
title={`+${countryList[countryCode].phone}`}
88+
/>
89+
<Modal visible={on}>
90+
<ModalContent onClose={() => set(false)}>
91+
<FlatList
92+
ListHeaderComponent={
93+
label ? (
94+
<Spacing marginBottom={3}>
95+
<Heading size="xxxlarge">{label}</Heading>
96+
</Spacing>
97+
) : null
98+
}
99+
keyExtractor={item => item.key}
100+
getItemLayout={(data, index) => ({
101+
index,
102+
length: theme.controlHeights.medium,
103+
offset: theme.controlHeights.medium * index,
104+
})}
105+
data={countries}
106+
renderItem={({ item: country }) => {
107+
return (
108+
<ListItem
109+
key={country.countryCode}
110+
label={country.name}
111+
onPress={() => {
112+
if (onChangeCountryCode) {
113+
onChangeCountryCode(country.countryCode);
114+
}
115+
set(false);
116+
}}
117+
/>
118+
);
119+
}}
120+
/>
121+
</ModalContent>
122+
</Modal>
123+
</>
124+
);
125+
}}
126+
</Toggle>
127+
<TextInput
128+
getStyles={() => ({
129+
containerStyle: {
130+
flex: 1,
131+
},
132+
inputStyle: {
133+
borderBottomLeftRadius: 0,
134+
borderTopLeftRadius: 0,
135+
},
136+
})}
137+
keyboardType="number-pad"
138+
value={phoneNumber}
139+
onChangeText={onChangePhoneNumber}
140+
placeholder={placeholder}
141+
/>
142+
</View>
143+
);
144+
};
145+
146+
export const PhoneNumberInput = withTheme(PhoneNumberInputBase);
147+
export default PhoneNumberInput;

src/components/Inputs/TextInput.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ class TextInputBase extends React.Component<TextInputProps> {
100100
size = 'medium',
101101
theme,
102102
value,
103+
getStyles,
103104
...textInputProps
104105
} = this.props;
105106

src/components/Layout/Box.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Box is a primitive component for convenient layout of components. All style prop
2020
paddingHorizontal={40}
2121
height={200}
2222
width={200}
23-
backgroundColor="blue"
23+
backgroundColor="#67c6bb"
2424
/>
2525
</Playground>
2626

@@ -40,7 +40,7 @@ Box is a primitive component for convenient layout of components. All style prop
4040
paddingHorizontal={10}
4141
height={33}
4242
width={33}
43-
backgroundColor="blue"
43+
backgroundColor="#67c6bb"
4444
/>
4545
</Playground>
4646

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
name: ListItem
3+
menu: Components
4+
---
5+
6+
import { Playground, PropsTable } from 'docz';
7+
import ListItem from './ListItem';
8+
import { Icon } from '../../icons';
9+
10+
## Usage
11+
12+
### All useful props
13+
14+
For image, it composes `Avatar`, you can pass `avatarProps` provide more specific customizations
15+
16+
<Playground>
17+
<ListItem
18+
onPress={() => null}
19+
imageSource={{ uri: 'https://picsum.photos/200/200' }}
20+
rightIcon={<Icon color="#7e7e7e" size={24} name="chevron-right" />}
21+
label="Some label"
22+
description="Some description"
23+
/>
24+
</Playground>
25+
26+
## Props
27+
28+
Extends `TouchableHighlightProps`. Read from `activeOpacity` and above for component specific props
29+
30+
<PropsTable of={ListItem} />

0 commit comments

Comments
 (0)