diff --git a/README.md b/README.md
index cc051c3..1d768af 100644
--- a/README.md
+++ b/README.md
@@ -12,19 +12,17 @@
It implements the most common pattern of mobile form user interaction by convension over configuration. You'll never have to worry again about scrolling and focusing form fields.
-- It uses inline form fields with icons and titles
+- It uses inline form fields with icons and labels
- It displays different icons for valid and invalid field values
- It displays validation message inside the field
- When a field receives focus, it displays a keyboard (\*)
-- If it is not the last field in the form, the keyboard return key is set to `Next` (\*\*)
-- If it is the last field in the form, the keyboard return key is set to `Done` and hides keaboard on return (\*\*)
+- If it is not the last field in the form, the keyboard return key is set to `Next`
+- If it is the last field in the form, the keyboard return key is set to `Done` and hides keaboard on return
- When a field receives focus, the form scrolls to the top of the field to avoid it being hidden behind the keyboard
- When all fields lose focus, the form scrolls back to the top of the form
(\*) Unless an external keyboard is connected to the device
-(\*\*) On Android the return key button is always displayed as `Done` for now, since React Native does not support changing it yet. But the behaviour works correctly ;)
-
## What it does NOT do
- It does not implement form validation. We recommend using [validate-model](https://github.com/danielweinmann/validate-model) for that. But you can use anything you want.
@@ -33,7 +31,7 @@ It implements the most common pattern of mobile form user interaction by convens
## Support
-- React Native 0.20+
+- React Native 0.25+
- iOS
- Android (see installation below)
@@ -82,10 +80,10 @@ class Form extends Component {
backgroundColor: 'lightgray',
}}>
}
@@ -97,13 +95,13 @@ class Form extends Component {
onChangeText={(text) => { this.setState({name: text}) }}
/>
}
@@ -115,13 +113,13 @@ class Form extends Component {
onChangeText={(text) => { this.setState({email: text}) }}
/>
}
@@ -141,15 +139,16 @@ import { AppRegistry } from 'react-native'
AppRegistry.registerComponent('Form', () => Form)
```
-#### Create your own widget to keep it DRY
+#### Create your own component to keep it DRY
```js
-import React, { Component, PropTypes } from 'react-native'
+import React, { Component } from 'react-native'
+import PropTypes from 'prop-types'
import Icon from 'react-native-vector-icons/MaterialIcons'
import { StatelessForm, InlineTextInput } from 'react-native-stateless-form'
class FormInput extends Component {
- // You MUST implement focus and blur methods for your widget to work
+ // You MUST implement focus and blur methods for your component to work
focus() {
this.refs.input.focus()
}
@@ -164,7 +163,7 @@ class FormInput extends Component {
}
@@ -200,7 +199,7 @@ class Form extends Component {
return (
{ this.setState({name: text}) }}
/>
{ this.setState({email: text}) }}
/>
Form)
#### Usage with validate-model
```js
-import React, { Component, PropTypes } from 'react-native'
+import React, { Component } from 'react-native'
+import PropTypes from 'prop-types'
import Icon from 'react-native-vector-icons/MaterialIcons'
import { StatelessForm, InlineTextInput } from 'react-native-stateless-form'
import { validate } from 'validate-model'
@@ -295,7 +295,7 @@ class FormInput extends Component {
}
@@ -330,7 +330,7 @@ class Form extends Component {
Form)
#### Usage with Redux Form
```js
-import React, { Component, PropTypes } from 'react-native'
+import React, { Component } from 'react-native'
+import PropTypes from 'prop-types'
import Icon from 'react-native-vector-icons/MaterialIcons'
import { StatelessForm, InlineTextInput } from 'react-native-stateless-form'
import { validateAll } from 'validate-model'
@@ -430,7 +431,7 @@ class FormInput extends Component {
}
@@ -455,14 +456,14 @@ class Form extends Component {
Root)
## StatelessForm
-A wrapper that will manage auto-focusing and auto-scrolling for its children widgets
+A wrapper that will manage auto-focusing and auto-scrolling for its children components
| Property | Type | Default | Description |
|---------------|----------|--------------|----------------------------------------------------------------|
@@ -521,19 +522,19 @@ A wrapper that will manage auto-focusing and auto-scrolling for its children wid
\+ Any other [ScrollView](https://facebook.github.io/react-native/docs/scrollview.html#content) prop you wish to pass.
-## Widgets
+## Components
#### InlineTextInput
| Property | Type | Default | Description |
|---------------|----------|--------------|----------------------------------------------------------------|
-| title | string | 'Use title prop' | Title for the text input |
+| label | string | 'Use label prop' | Label for the text input |
| value | string | null | Value for the text input |
| valid | boolean | false | Whether the value is valid or not |
| message | string | null | Validation message to be shown |
| style | style | {} | Style changes to the main ScrollView |
| iconStyle | style | {} | Style changes to the icon View |
-| titleStyle | style | {} | Style changes to the title Text |
+| labelStyle | style | {} | Style changes to the label Text |
| inputStyle | style | {} | Style changes to the TextInput |
| messageStyle | style | {} | Style changes to the validation message Text |
| icon | element | null | Any react component to be used as icon |
@@ -542,33 +543,33 @@ A wrapper that will manage auto-focusing and auto-scrolling for its children wid
\+ Any other [TextInput](https://facebook.github.io/react-native/docs/textinput.html#content) prop you wish to pass.
-#### Other widgets
+#### Other components
-My intention is to implement most of [FaridSafi/react-native-gifted-form](https://github.com/FaridSafi/react-native-gifted-form)'s widgets. But I'll do each one only when I need it in a real project, so it might take some time.
+My intention is to implement most of [FaridSafi/react-native-gifted-form](https://github.com/FaridSafi/react-native-gifted-form)'s components. But I'll do each one only when I need it in a real project, so it might take some time.
PR's are very much welcome!
-## Creating new widgets
+## Creating new components
-Any react component can be rendered inside Stateless Form as a widget. But there is a special case below:
+Any react component can be rendered inside Stateless Form as a component. But there is a special case below:
-#### Focusable input widgets
+#### Focusable input components
-If you want your widget to receive focus when previous widget finished editing, you must implement the following pattern:
+If you want your component to receive focus when previous component finished editing, you must implement the following pattern:
-- Your widget should implement the `focus()` method.
-- Your widget should implement the `blur()` method.
-- Your widget should implement `onSubmitEditing` or equivalent and call `this.props.onNextInputFocus(this.props.nextInput, this)` so StatelessForm can focus the next input or blur the current input.
-- Your widget must have `valid` and `value` on its `propTypes`. This is how `StatelessForm` will recognize it as a focusable and/or scrollable input widget. It is important that only focusable or scrollable widgets have these props on `propTypes`.
+- Your component should implement the `focus()` method.
+- Your component should implement the `blur()` method.
+- Your component should implement `onSubmitEditing` or equivalent and call `this.props.onNextInputFocus(this.props.nextInput, this)` so StatelessForm can focus the next input or blur the current input.
+- Your component must have `valid` and `value` on its `propTypes`. This is how `StatelessForm` will recognize it as a focusable and/or scrollable input component. It is important that only focusable or scrollable components have these props on `propTypes`.
-#### Scrollable input widgets
+#### Scrollable input components
-If you want your widget to receive scroll when showing keyboard, you must implement the following pattern:
+If you want your component to receive scroll when showing keyboard, you must implement the following pattern:
-- Your widget should implement `onFocus` and call `this.props.onFocus(scrollTo)` on focus. `scrollTo` must be your widget's `y` position.
-- You can get your `y` position using `onLayout` prop. Check [InlineTextInput](https://github.com/danielweinmann/react-native-stateless-form/blob/master/widgets/InlineTextInput.js) for references on how to implement it.
-- Your widget should implement `onBlur` and call `this.props.onBlur` on blur.
-- Your widget also must have `valid` and `value` on its `propTypes`.
+- Your component should implement `onFocus` and call `this.props.onFocus(scrollTo)` on focus. `scrollTo` must be your component's `y` position.
+- You can get your `y` position using `onLayout` prop. Check [InlineTextInput](https://github.com/danielweinmann/react-native-stateless-form/blob/master/components/InlineTextInput.js) for references on how to implement it.
+- Your component should implement `onBlur` and call `this.props.onBlur` on blur.
+- Your component also must have `valid` and `value` on its `propTypes`.
## Contributing
diff --git a/StatelessForm.js b/StatelessForm.js
index adba039..eedb548 100644
--- a/StatelessForm.js
+++ b/StatelessForm.js
@@ -1,4 +1,6 @@
-import React, { Platform, Component, PropTypes, ScrollView, View } from 'react-native'
+import React, { Component } from 'react'
+import { Platform, ScrollView, View } from 'react-native'
+import PropTypes from 'prop-types'
export default class StatelessForm extends Component {
componentDidMount() {
@@ -6,6 +8,8 @@ export default class StatelessForm extends Component {
}
childrenWithProps() {
+ if (!this.props.children) return
+
let nextInput = null
let inputCount = 0
return React.Children.map(this.props.children, (child) => child).reverse().map((child) => {
@@ -51,7 +55,7 @@ export default class StatelessForm extends Component {
render() {
return (
}
)
- }
+ }
}
StatelessForm.propTypes = {
style: PropTypes.oneOfType([
- React.PropTypes.object,
- React.PropTypes.arrayOf(React.PropTypes.object),
+ PropTypes.object,
+ PropTypes.arrayOf(PropTypes.object),
]),
}
diff --git a/widgets/InlineTextInput.js b/components/InlineTextInput.js
similarity index 73%
rename from widgets/InlineTextInput.js
rename to components/InlineTextInput.js
index 2d7aabe..b86a724 100644
--- a/widgets/InlineTextInput.js
+++ b/components/InlineTextInput.js
@@ -1,4 +1,6 @@
-import React, { Component, PropTypes, View, Text, TextInput } from 'react-native'
+import React, { Component } from 'react'
+import { View, Text, TextInput, StyleSheet } from 'react-native'
+import PropTypes from 'prop-types'
export default class InlineTextInput extends Component {
componentDidMount() {
@@ -55,22 +57,28 @@ export default class InlineTextInput extends Component {
renderMessage() {
const { message, messageStyle } = this.props
+ const style = StyleSheet.flatten(this.props.style)
if (this.shouldDisplayMessage()) {
return(
-
- { message }
-
+
+
+ { message }
+
+
)
}
}
render() {
- const { title, value, style, titleStyle, inputStyle, nextInput, onBlur, multiline } = this.props
+ const { label, value, labelStyle, inputStyle, nextInput, onBlur, multiline } = this.props
+ const style = StyleSheet.flatten(this.props.style)
return (
{ this.renderIcon() }
- {title}
+ {label}
0 && !valid && message)
+ }
+
+ handleSubmitEditing() {
+ const { nextInput, onNextInputFocus } = this.props
+ onNextInputFocus && onNextInputFocus(nextInput, this)
+ }
+
+ renderIcon() {
+ const { icon, validIcon, invalidIcon, valid, value, iconStyle } = this.props
+ if (!icon)
+ return
+ let renderedIcon = null
+ if (value && value.length > 0) {
+ renderedIcon = (valid ? (validIcon ? validIcon : icon) : (invalidIcon ? invalidIcon : icon))
+ } else {
+ renderedIcon = icon
+ }
+ return (
+
+ {renderedIcon}
+
+ )
+ }
+
+ renderMessage() {
+ const { message, messageStyle } = this.props
+ const style = StyleSheet.flatten(this.props.style)
+ if (this.shouldDisplayMessage()) {
+ return(
+
+
+ { message }
+
+
+ )
+ }
+ }
+
+ render() {
+ const { label, value, labelStyle, inputStyle, nextInput, onBlur, multiline } = this.props
+ const style = StyleSheet.flatten(this.props.style)
+ return (
+
+
+ {label}
+
+
+ { this.renderIcon() }
+
+
+ { this.renderMessage() }
+
+ )
+ }
+}
+
+const stylePropType = PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.arrayOf(PropTypes.object),
+])
+
+LabeledTextInput.propTypes = {
+ label: PropTypes.string,
+ value: PropTypes.string,
+ valid: PropTypes.bool,
+ message: PropTypes.string,
+ style: stylePropType,
+ iconStyle: stylePropType,
+ labelStyle: stylePropType,
+ inputStyle: stylePropType,
+ messageStyle: stylePropType,
+ icon: PropTypes.element,
+ validIcon: PropTypes.element,
+ invalidIcon: PropTypes.element,
+}
+
+LabeledTextInput.defaultProps = {
+ label: 'Use label prop',
+ value: null,
+ valid: false,
+ message: null,
+ style: {},
+ iconStyle: {},
+ labelStyle: {},
+ inputStyle: {},
+ messageStyle: {},
+ icon: null,
+ validIcon: null,
+ invalidIcon: null,
+}
diff --git a/index.js b/index.js
index 74de23b..0b4b8cb 100644
--- a/index.js
+++ b/index.js
@@ -1,4 +1,5 @@
import StatelessForm from './StatelessForm'
-import InlineTextInput from './widgets/InlineTextInput'
+import LabeledTextInput from './components/LabeledTextInput'
+import InlineTextInput from './components/InlineTextInput'
-module.exports = { StatelessForm, InlineTextInput }
+module.exports = { StatelessForm, LabeledTextInput, InlineTextInput }
diff --git a/package.json b/package.json
index 44916c9..613dbff 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-native-stateless-form",
- "version": "0.2.0",
+ "version": "0.3.1",
"description": "Stateless presentational form components for React Native",
"main": "index.js",
"scripts": {
@@ -26,5 +26,8 @@
"bugs": {
"url": "https://github.com/danielweinmann/react-native-stateless-form/issues"
},
- "homepage": "https://github.com/danielweinmann/react-native-stateless-form#readme"
+ "homepage": "https://github.com/danielweinmann/react-native-stateless-form#readme",
+ "dependencies": {
+ "prop-types": "^15.6.0"
+ }
}