Skip to content

Commit 5eb70be

Browse files
author
amoreno
committed
Start project
0 parents  commit 5eb70be

File tree

11 files changed

+388
-0
lines changed

11 files changed

+388
-0
lines changed

.eslintrc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"extends": ["eslint:recommended", "prettier"],
3+
"env": {
4+
"browser": true,
5+
"node": true
6+
},
7+
"plugins": ["prettier"],
8+
"rules": {
9+
"semi": 1,
10+
"prettier/prettier": ["error", { "singleQuote": true }]
11+
}
12+
}

.flowconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[ignore]
2+
3+
[include]
4+
5+
[libs]
6+
7+
[lints]
8+
9+
[options]

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
*.log
3+
jsconfig.json

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Example/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 InterfaceKit
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# react-native-image-picker-form
2+
3+
<p>
4+
<img src="https://img.shields.io/npm/dm/react-native-image-picker-form.svg" />
5+
<img src="https://img.shields.io/npm/dt/react-native-image-picker-form.svg" />
6+
</p>
7+
8+
A React Native component factory to use with [`tcomb-form-native`](https://github.com/gcanti/tcomb-form-native) library. Currently using [`react-native-image-crop-picker`](https://github.com/ivpusic/react-native-image-crop-picker) to provide image selection.
9+
10+
At the moment only supported iOS platform.
11+
12+
<p align="center">
13+
<img src="./example.gif" alt="Image factory" width="400">
14+
</p>
15+
16+
## Getting started
17+
18+
```sh
19+
$ yarn add react-native-image-picker-form
20+
```
21+
22+
After that, follow the instructions on: https://github.com/ivpusic/react-native-image-crop-picker#install
23+
24+
## Usage
25+
26+
When configuring your `tcomb-form-native` form, use the `factory` option to set as `SelectImageFactory`.
27+
You can change the text displayed on ActionSheet setting a options value or change the title with title option on `config`.
28+
29+
Default locale is `en-US`
30+
31+
```js
32+
import React from 'react-native'
33+
import t from 'tcomb-form-native'
34+
import ImageFactory from 'react-native-image-picker-form'
35+
36+
const Form = t.form.Form
37+
const DocumentFormStruct = t.struct({
38+
image: t.String
39+
})
40+
41+
type Props = {}
42+
type State = {
43+
value: Object,
44+
options: Object
45+
}
46+
47+
class App extends React.Component<Props, State> {
48+
constructor(props) {
49+
super(props)
50+
this.state = {
51+
value: {},
52+
options: {
53+
fields: {
54+
image: {
55+
config: {
56+
title: 'Select image',
57+
options: ['Open camera', 'Select from gallery', 'Cancel']
58+
},
59+
error: 'No image provided',
60+
factory: ImageFactory
61+
}
62+
}
63+
}
64+
}
65+
}
66+
67+
render() {
68+
return (
69+
<Form
70+
ref={(ref: any) => {
71+
this.form = ref
72+
}}
73+
type={DocumentFormStruct}
74+
value={this.state.value}
75+
options={this.state.options}
76+
/>
77+
)
78+
}
79+
}
80+
```
81+
82+
## License
83+
84+
MIT License
85+
86+
Copyright (c) 2018 InterfaceKit
87+
88+
## Author
89+
90+
Antonio Moreno Valls `<amoreno at apsl.net>`
91+
92+
Built with 💛 by [APSL](https://github.com/apsl).

__tests__/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import 'react-native'
2+
import React from 'react'
3+
import Stepper from '../lib/Stepper'
4+
5+
// Note: test renderer must be required after react-native.
6+
import renderer from 'react-test-renderer'
7+
8+
it('renders correctly', () => {
9+
const tree = renderer.create(<Stepper />)
10+
})

example.gif

7.49 MB
Loading

index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/* @flow */
2+
3+
import Stepper from './lib/Stepper'
4+
5+
export default Stepper

lib/ImageFactory.js

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/* @flow */
2+
3+
import React from 'react'
4+
import {
5+
ActionSheetIOS,
6+
View,
7+
Text,
8+
Button,
9+
Animated,
10+
StyleSheet
11+
} from 'react-native'
12+
import ImagePicker from 'react-native-image-crop-picker'
13+
import SimpleLineIcons from 'react-native-vector-icons/SimpleLineIcons'
14+
import t from 'tcomb-form-native'
15+
16+
type Props = {
17+
title: string
18+
}
19+
type State = {
20+
image: ?string
21+
}
22+
23+
const Component = t.form.Component
24+
25+
class ImageFactory extends Component<Props, State> {
26+
constructor(props: Props) {
27+
super(props)
28+
this.state = {
29+
image: undefined,
30+
height: new Animated.Value(0),
31+
overflow: 'visible'
32+
}
33+
}
34+
35+
_onPressImage = () => {
36+
const options = this.props.options.config.options || [
37+
'Open camera',
38+
'Select from the gallery',
39+
'Cancel'
40+
]
41+
ActionSheetIOS.showActionSheetWithOptions(
42+
{
43+
options,
44+
cancelButtonIndex: 2
45+
},
46+
(buttonIndex: number) => {
47+
if (buttonIndex === 0) {
48+
ImagePicker.openCamera({}).then((image: Object) =>
49+
this._getImageFromStorage(image.path)
50+
)
51+
} else if (buttonIndex === 1) {
52+
ImagePicker.openPicker({}).then((image: Object) =>
53+
this._getImageFromStorage(image.path)
54+
)
55+
}
56+
}
57+
)
58+
}
59+
60+
shouldComponentUpdate(nextProps: Props, nextState: State) {
61+
return true
62+
}
63+
64+
_startAnimation = () => {
65+
Animated.sequence([
66+
Animated.timing(this.state.height, {
67+
toValue: 0,
68+
duration: 250
69+
}),
70+
Animated.timing(this.state.height, {
71+
toValue: 150,
72+
duration: 500,
73+
delay: 75
74+
})
75+
]).start()
76+
}
77+
78+
_getImageFromStorage = (path: string) => {
79+
this.setState({ image: path, overflow: 'hidden' }, () =>
80+
this._startAnimation()
81+
)
82+
super.getLocals().onChange(path)
83+
}
84+
85+
getTemplate() {
86+
return (locals: Object) => {
87+
const stylesheet = locals.stylesheet
88+
let formGroupStyle = stylesheet.formGroup.normal
89+
let controlLabelStyle = stylesheet.controlLabel.normal
90+
let textboxStyle = stylesheet.textbox.normal
91+
let helpBlockStyle = stylesheet.helpBlock.normal
92+
let errorBlockStyle = stylesheet.errorBlock
93+
94+
if (locals.hasError) {
95+
controlLabelStyle = stylesheet.controlLabel.error
96+
formGroupStyle = stylesheet.formGroup.error
97+
textboxStyle = stylesheet.textbox.error
98+
helpBlockStyle = stylesheet.helpBlock.error
99+
}
100+
101+
return (
102+
<View>
103+
{locals.label ? (
104+
<Text
105+
style={[
106+
controlLabelStyle,
107+
{
108+
color: locals.error ? '#a94442' : 'black'
109+
}
110+
]}>
111+
{locals.label}
112+
</Text>
113+
) : null}
114+
<View
115+
style={[
116+
styles.topContainer,
117+
{
118+
borderColor: locals.hasError ? '#a94442' : 'grey'
119+
}
120+
]}>
121+
<Animated.Image
122+
resizeMode="cover"
123+
source={{
124+
uri: this.state.image
125+
}}
126+
style={[
127+
styles.image,
128+
{
129+
height: this.state.height
130+
}
131+
]}
132+
/>
133+
<View
134+
style={[
135+
styles.container,
136+
{
137+
overflow: this.state.overflow,
138+
backgroundColor: locals.hasError ? '#E28E8E' : '#e6e6e6'
139+
}
140+
]}>
141+
<SimpleLineIcons name={'camera'} size={28} style={styles.icon} />
142+
</View>
143+
</View>
144+
<Button onPress={this._onPressImage} title={locals.config.title} />
145+
{locals.help || locals.config.help ? (
146+
<Text style={helpBlockStyle}>
147+
{locals.help || locals.config.help}
148+
</Text>
149+
) : null}
150+
</View>
151+
)
152+
}
153+
}
154+
}
155+
156+
const styles = StyleSheet.create({
157+
topContainer: {
158+
overflow: 'hidden',
159+
borderRadius: 4,
160+
marginBottom: 12,
161+
height: 150,
162+
borderColor: 'grey',
163+
borderWidth: 1
164+
},
165+
container: {
166+
flex: 1,
167+
alignItems: 'center',
168+
justifyContent: 'center',
169+
backgroundColor: '#e6e6e6',
170+
171+
height: 100,
172+
borderRadius: 4
173+
},
174+
icon: {
175+
textAlign: 'center',
176+
textAlignVertical: 'center'
177+
},
178+
image: {
179+
height: 150
180+
}
181+
})
182+
183+
export default ImageFactory

0 commit comments

Comments
 (0)