Skip to content

Commit 2e44549

Browse files
committed
feat: add new input components
1 parent 6981092 commit 2e44549

9 files changed

Lines changed: 1196 additions & 141 deletions

File tree

src/components/Form/FormField.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,11 @@ export interface FormFieldProps {
77
error?: string | null;
88
label?: string | null;
99
description?: string | null;
10+
children?: React.ReactNode;
1011
}
1112

1213
const FormField = (props: FormFieldProps) => {
13-
const {
14-
label,
15-
error,
16-
// @ts-ignore
17-
children,
18-
description,
19-
...passThroughProps
20-
} = props;
14+
const { label, error, children, description, ...passThroughProps } = props;
2115

2216
return (
2317
<Box>
@@ -27,6 +21,8 @@ const FormField = (props: FormFieldProps) => {
2721
</Box>
2822
)}
2923
<Box marginBottom={4}>
24+
{/*
25+
// @ts-ignore: TODO: Find right way to type this */}
3026
{React.cloneElement(children, passThroughProps)}
3127
</Box>
3228
{error && (

src/components/Inputs/ClearableTextInput.tsx

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,39 @@ import { TextInput as RNTextInput, TouchableOpacity } from 'react-native';
33

44
import { Icon } from '../../icons';
55
import { ThemeContext } from '../../theme';
6-
import TextInputWithIcon, { TextInputWithIconProps } from './TextInputWithIcon';
6+
import IconTextInput, { IconTextInputProps } from './IconTextInput';
77

8-
export interface ClearableTextInputProps extends TextInputWithIconProps {
8+
export interface ClearableTextInputProps extends IconTextInputProps {
99
onClear?: () => void;
1010
}
1111

1212
const ClearableTextInputBase = (props: ClearableTextInputProps) => {
13-
const { onClear, innerRef, onChangeText, ...textInputWithIconProps } = props;
13+
const {
14+
onClear,
15+
innerRef,
16+
onChangeText,
17+
value,
18+
...textInputWithIconProps
19+
} = props;
1420
const theme = React.useContext(ThemeContext);
1521

1622
return (
17-
<TextInputWithIcon
23+
<IconTextInput
1824
ref={innerRef}
1925
rightIcon={
20-
<TouchableOpacity
21-
onPress={() => {
22-
if (onChangeText) onChangeText('');
23-
if (onClear) onClear();
24-
}}
25-
>
26-
<Icon name="x" size={24} color={theme.colors.text.default} />
27-
</TouchableOpacity>
26+
value ? (
27+
<TouchableOpacity
28+
onPress={() => {
29+
if (onChangeText) onChangeText('');
30+
if (onClear) onClear();
31+
}}
32+
>
33+
<Icon name="x" size={24} color={theme.colors.text.default} />
34+
</TouchableOpacity>
35+
) : null
2836
}
2937
onChangeText={onChangeText}
38+
value={value}
3039
{...textInputWithIconProps}
3140
/>
3241
);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import * as React from 'react';
2+
import {
3+
Clipboard,
4+
TextInput as RNTextInput,
5+
TouchableOpacity,
6+
} from 'react-native';
7+
8+
import { Icon } from '../../icons';
9+
import { ThemeContext } from '../../theme';
10+
import IconTextInput, { IconTextInputProps } from './IconTextInput';
11+
12+
export interface CopyTextInputProps extends IconTextInputProps {
13+
onCopy?: (text: string) => void;
14+
}
15+
16+
const CopyTextInputBase = (props: CopyTextInputProps) => {
17+
const { onCopy, innerRef, value, ...textInputWithIconProps } = props;
18+
const theme = React.useContext(ThemeContext);
19+
20+
return (
21+
<IconTextInput
22+
ref={innerRef}
23+
rightIcon={
24+
value ? (
25+
<TouchableOpacity
26+
onPress={() => {
27+
Clipboard.setString(value);
28+
if (onCopy) {
29+
onCopy(value);
30+
}
31+
}}
32+
>
33+
<Icon name="copy" size={24} color={theme.colors.text.default} />
34+
</TouchableOpacity>
35+
) : null
36+
}
37+
value={value}
38+
{...textInputWithIconProps}
39+
/>
40+
);
41+
};
42+
43+
export const CopyTextInput = React.forwardRef<RNTextInput, CopyTextInputProps>(
44+
(props, ref) => <CopyTextInputBase {...props} innerRef={ref} />,
45+
);
46+
47+
export default CopyTextInput;

src/components/Inputs/TextInputWithIcon.styles.ts renamed to src/components/Inputs/IconTextInput.styles.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,45 @@ import { ViewStyle } from 'react-native';
22

33
import { Theme } from '../../theme/ThemeInterface';
44

5-
export interface TextInputWithIconStyles {
5+
export interface IconTextInputStyles {
6+
inputStyle: ViewStyle;
67
containerStyle: ViewStyle;
78
leftContainerStyle: ViewStyle;
89
rightContainerStyle: ViewStyle;
910
}
1011

11-
export type GetTextInputWithIconStyles = (
12+
export interface GetIconTextInputStylesProps {
13+
hasLeftIcon: boolean;
14+
hasRightIcon: boolean;
15+
}
16+
17+
export type GetIconTextInputStyles = (
18+
props: GetIconTextInputStylesProps,
1219
theme: Theme,
13-
) => TextInputWithIconStyles;
20+
) => IconTextInputStyles;
21+
22+
export const getIconTextInputStyles: GetIconTextInputStyles = (
23+
props: GetIconTextInputStylesProps,
24+
theme,
25+
) => {
26+
const { hasLeftIcon, hasRightIcon } = props;
1427

15-
export const getTextInputWithIconStyles: GetTextInputWithIconStyles = theme => {
1628
return {
1729
containerStyle: {
1830
position: 'relative',
1931
},
32+
inputStyle: {
33+
...(hasLeftIcon && { paddingLeft: 40 }),
34+
...(hasRightIcon && { paddingRight: 40 }),
35+
},
2036
leftContainerStyle: {
2137
alignItems: 'center',
2238
display: 'flex',
2339
height: '100%',
2440
justifyContent: 'center',
41+
left: 0,
2542
paddingHorizontal: 8,
2643
position: 'absolute',
27-
right: 0,
2844
},
2945
rightContainerStyle: {
3046
alignItems: 'center',

src/components/Inputs/TextInputWithIcon.tsx renamed to src/components/Inputs/IconTextInput.tsx

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,23 @@ import { DeepPartial, Omit } from 'ts-essentials';
44

55
import { ThemeContext } from '../../theme';
66
import { mergeStyles, ReplaceReturnType } from '../../utils/mergeStyles';
7+
import {
8+
getIconTextInputStyles,
9+
IconTextInputStyles,
10+
} from './IconTextInput.styles';
711
import TextInput, { TextInputProps } from './TextInput';
812
import { GetTextInputStyles, TextInputStyles } from './TextInput.styles';
9-
import {
10-
getTextInputWithIconStyles,
11-
TextInputWithIconStyles,
12-
} from './TextInputWithIcon.styles';
1313

14-
export interface TextInputWithIconProps
15-
extends Omit<TextInputProps, 'getStyles'> {
14+
export interface IconTextInputProps extends Omit<TextInputProps, 'getStyles'> {
1615
leftIcon?: React.ReactNode;
1716
rightIcon?: React.ReactNode;
1817
getStyles?: ReplaceReturnType<
1918
GetTextInputStyles,
20-
DeepPartial<TextInputStyles & TextInputWithIconStyles>
19+
DeepPartial<TextInputStyles & IconTextInputStyles>
2120
>;
2221
}
2322

24-
const TextInputWithIconBase = (props: TextInputWithIconProps) => {
23+
const IconTextInputBase = (props: IconTextInputProps) => {
2524
const {
2625
leftIcon = null,
2726
rightIcon = null,
@@ -34,19 +33,29 @@ const TextInputWithIconBase = (props: TextInputWithIconProps) => {
3433
containerStyle,
3534
leftContainerStyle,
3635
rightContainerStyle,
37-
} = mergeStyles(getTextInputWithIconStyles, getStyles)(theme);
36+
inputStyle,
37+
} = mergeStyles(getIconTextInputStyles, getStyles)(
38+
{ hasLeftIcon: !!leftIcon, hasRightIcon: !!rightIcon },
39+
theme,
40+
);
3841

3942
return (
4043
<View style={containerStyle}>
4144
<View style={leftContainerStyle}>{leftIcon}</View>
42-
<TextInput ref={innerRef} {...textInputProps} />
45+
<TextInput
46+
ref={innerRef}
47+
getStyles={() => ({
48+
inputStyle,
49+
})}
50+
{...textInputProps}
51+
/>
4352
<View style={rightContainerStyle}>{rightIcon}</View>
4453
</View>
4554
);
4655
};
4756

48-
export const TextInputWithIcon = React.forwardRef<RNTextInput, TextInputProps>(
49-
(props, ref) => <TextInputWithIconBase {...props} innerRef={ref} />,
57+
export const IconTextInput = React.forwardRef<RNTextInput, TextInputProps>(
58+
(props, ref) => <IconTextInputBase {...props} innerRef={ref} />,
5059
);
5160

52-
export default TextInputWithIcon;
61+
export default IconTextInput;

src/components/Inputs/Inputs.mdx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ menu: Components
55

66
import { Playground, PropsTable } from 'docz';
77
import { State } from 'react-powerplug';
8+
import { View } from 'react-native';
89

10+
import { Icon } from '../../icons';
911
import { Heading, Label } from '../Typography';
1012
import { Box, Spacing } from '../Layout';
1113
import TextInput from './TextInput';
14+
import IconTextInput from './IconTextInput';
15+
import CopyTextInput from './CopyTextInput';
16+
import SearchTextInput from './SearchTextInput';
1217
import ClearableTextInput from './ClearableTextInput';
1318
import TextArea from './TextArea';
1419
import PhoneNumberInput from './PhoneNumberInput';
@@ -71,6 +76,31 @@ import PhoneNumberInput from './PhoneNumberInput';
7176
</Box>
7277
</Playground>
7378

79+
## IconTextInput
80+
81+
<Playground>
82+
<Box marginBottom={24}>
83+
<State initial={{ value: '' }}>
84+
{({ state, setState }) => (
85+
<IconTextInput
86+
leftIcon={
87+
<View>
88+
<Icon name="menu" size={24} />
89+
</View>
90+
}
91+
rightIcon={
92+
<View>
93+
<Icon name="menu" size={24} />
94+
</View>
95+
}
96+
onChangeText={text => setState({ value: text })}
97+
value={state.value}
98+
/>
99+
)}
100+
</State>
101+
</Box>
102+
</Playground>
103+
74104
## ClearableTextInput
75105

76106
<Playground>
@@ -90,6 +120,44 @@ import PhoneNumberInput from './PhoneNumberInput';
90120
</Box>
91121
</Playground>
92122

123+
## SearchTextInput
124+
125+
<Playground>
126+
<Box marginBottom={24}>
127+
<State initial={{ value: '' }}>
128+
{({ state, setState }) => (
129+
<SearchTextInput
130+
onChangeText={text => setState({ value: text })}
131+
value={state.value}
132+
onClear={() => {
133+
console.log('Cleared!');
134+
}}
135+
placeholder="Text input with search icon"
136+
/>
137+
)}
138+
</State>
139+
</Box>
140+
</Playground>
141+
142+
## CopyTextInput
143+
144+
<Playground>
145+
<Box marginBottom={24}>
146+
<State initial={{ value: '' }}>
147+
{({ state, setState }) => (
148+
<CopyTextInput
149+
onChangeText={text => setState({ value: text })}
150+
value={state.value}
151+
onCopy={text => {
152+
console.log('Copied', text);
153+
}}
154+
placeholder="Text input wiht copy"
155+
/>
156+
)}
157+
</State>
158+
</Box>
159+
</Playground>
160+
93161
## PhoneNumberInput
94162

95163
### Usage
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as React from 'react';
2+
import { TextInput as RNTextInput, View } from 'react-native';
3+
4+
import { Icon } from '../../icons';
5+
import { ThemeContext } from '../../theme';
6+
import ClearableTextInput, {
7+
ClearableTextInputProps,
8+
} from './ClearableTextInput';
9+
10+
// tslint:disable-next-line
11+
export interface SearchTextInputProps extends ClearableTextInputProps {}
12+
13+
const SearchTextInputBase = (props: SearchTextInputProps) => {
14+
const { onClear, innerRef, ...textInputWithIconProps } = props;
15+
const theme = React.useContext(ThemeContext);
16+
17+
return (
18+
<ClearableTextInput
19+
ref={innerRef}
20+
leftIcon={
21+
<View>
22+
<Icon name="search" size={24} color={theme.colors.text.default} />
23+
</View>
24+
}
25+
{...textInputWithIconProps}
26+
/>
27+
);
28+
};
29+
30+
export const SearchTextInput = React.forwardRef<
31+
RNTextInput,
32+
SearchTextInputProps
33+
>((props, ref) => <SearchTextInputBase {...props} innerRef={ref} />);
34+
35+
export default SearchTextInput;

src/components/Inputs/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,13 @@ export {
1313
default as PhoneNumberInputField,
1414
PhoneNumberInputFieldProps,
1515
} from './PhoneNumberInputField';
16+
export {
17+
default as ClearableTextInput,
18+
ClearableTextInputProps,
19+
} from './ClearableTextInput';
20+
export { default as IconTextInput, IconTextInputProps } from './IconTextInput';
21+
export {
22+
default as SearchTextInput,
23+
SearchTextInputProps,
24+
} from './SearchTextInput';
25+
export { default as CopyTextInput, CopyTextInputProps } from './CopyTextInput';

0 commit comments

Comments
 (0)