├── .npmignore ├── LICENSE ├── README.md ├── examples └── FormView.js ├── index.js ├── package.json ├── src ├── Form.js ├── KeyboardAwareScrollView.js ├── fields │ ├── CountDownField.ios.js │ ├── DatePickerField.android.js │ ├── DatePickerField.ios.js │ ├── InputField.android.js │ ├── InputField.ios.js │ ├── LinkField.android.js │ ├── LinkField.ios.js │ ├── PickerField.android.js │ ├── PickerField.ios.js │ ├── Separator.js │ ├── SwitchField.android.js │ ├── SwitchField.ios.js │ ├── TimePickerField.android.js │ └── TimePickerField.ios.js ├── index.js └── lib │ ├── DatePickerComponent.android.js │ ├── DatePickerComponent.ios.js │ ├── Field.js │ ├── HelpText.js │ ├── InputComponent.js │ ├── LinkComponent.js │ ├── PickerComponent.android.js │ ├── PickerComponent.ios.js │ ├── SwitchComponent.js │ └── TimePickerComponent.android.js └── yarn.lock /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 28 | node_modules 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Michael Cereda 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | React Native Form Generator 2 | ================ 3 | 4 | Generate forms with native look and feel in a breeze 5 | 6 | [![NPM](https://nodei.co/npm/react-native-form-generator.png)](https://nodei.co/npm/react-native-form-generator/) 7 | 8 | 9 | ![rnformgendatetimefields](https://cloud.githubusercontent.com/assets/107390/16767512/1dfbf33c-4840-11e6-8085-1521b6d0c3ce.gif) 10 | 11 | ![react-native-form-generator](https://cloud.githubusercontent.com/assets/107390/12499709/edc1c298-c07a-11e5-916c-394de83ebe51.gif) 12 | 13 | ## Components 14 | * Picker 15 | * DatePicker 16 | * TimePicker 17 | * Input 18 | * Link 19 | * Separator 20 | * Switch 21 | 22 | ## Features 23 | * Android and IOS support, Yeah Baby! 24 | * Pleasant Defaults, totally overridable 25 | * Doesn't have dependencies 26 | * Use your own icon pack 27 | * Easy to use and clean, react style syntax 28 | * Automatic events handling 29 | * Supports custom fields and styles without adding any weird syntax (just create a react component) 30 | * Applies by default the current OS style 31 | * Inspired by tcomb, the good parts 32 | * Performances: just the field changed gets a setState 33 | * You don't need to create a 'Model' or a 'Struct' that contains your data, just create a form component (the React's way) 34 | * Validate InputFields based on keyboardType (can be overridden using validationFunction) 35 | * Multiple validators 36 | * Reset/Set Fields programmatically (setValue, setDate, setTime, focus) 37 | * Custom Wrapper for Picker & DatePicker Components (iOS Only) 38 | 39 | [My blogpost about React Native Form Generator](https://medium.com/@michaelcereda/react-native-forms-the-right-way-315802f989d6#.p9oj79vt3) 40 | 41 | ## Installation 42 | ``` 43 | npm install --save react-native-form-generator 44 | ``` 45 | ## I'm actively working on this project 46 | 47 | * Pull requests are very very welcome. They make my day ;). 48 | * Master should be considered 'unstable' even if I do my best to keep it nice and safe. 49 | * Every release has its own branch. 50 | * Slider hasn't been created. 51 | * I have to document the code properly and do some housekeeping, i apologize in advance. 52 | 53 | ## Example 54 | 55 | Please check the folder _examples_ for an always up to date use case. 56 | 57 | the example below generates the form you see in the animation 58 | ```javascript 59 | 60 | /* 61 | This is a view i use in a test app, 62 | very useful to list all the use cases 63 | */ 64 | 65 | import React from 'react'; 66 | 67 | import { 68 | AppRegistry, 69 | StyleSheet, 70 | Text, 71 | View,ScrollView, 72 | } from 'react-native'; 73 | 74 | 75 | import { Form, 76 | Separator,InputField, LinkField, 77 | SwitchField, PickerField,DatePickerField,TimePickerField 78 | } from 'react-native-form-generator'; 79 | 80 | export class FormView extends React.Component{ 81 | constructor(props){ 82 | super(props); 83 | this.state = { 84 | formData:{} 85 | } 86 | } 87 | handleFormChange(formData){ 88 | /* 89 | formData will contain all the values of the form, 90 | in this example. 91 | 92 | formData = { 93 | first_name:"", 94 | last_name:"", 95 | gender: '', 96 | birthday: Date, 97 | has_accepted_conditions: bool 98 | } 99 | */ 100 | 101 | this.setState({formData:formData}) 102 | this.props.onFormChange && this.props.onFormChange(formData); 103 | } 104 | handleFormFocus(e, component){ 105 | //console.log(e, component); 106 | } 107 | openTermsAndConditionsURL(){ 108 | 109 | } 110 | render(){ 111 | return ( 112 |
117 | 118 | { 123 | 124 | if(Object.keys(self.refs).length !== 0){ 125 | if(!self.refs.registrationForm.refs.first_name.valid){ 126 | return self.refs.registrationForm.refs.first_name.validationErrors.join("\n"); 127 | } 128 | 129 | } 130 | // if(!!(self.refs && self.refs.first_name.valid)){ 131 | // } 132 | })(this)} 133 | validationFunction={[(value)=>{ 134 | /* 135 | you can have multiple validators in a single function or an array of functions 136 | */ 137 | 138 | if(value == '') return "Required"; 139 | //Initial state is null/undefined 140 | if(!value) return true; 141 | // Check if First Name Contains Numbers 142 | var matches = value.match(/\d+/g); 143 | if (matches != null) { 144 | return "First Name can't contain numbers"; 145 | } 146 | 147 | return true; 148 | }, (value)=>{ 149 | ///Initial state is null/undefined 150 | if(!value) return true; 151 | if(value.indexOf('4')!=-1){ 152 | return "I can't stand number 4"; 153 | } 154 | return true; 155 | }]} 156 | /> 157 | 158 | 163 | 164 | {}}/> 165 | 168 | 175 | 179 | 181 | 184 | 185 | {JSON.stringify(this.state.formData)} 186 | 187 |
); 188 | } 189 | } 190 | 191 | ``` 192 | 193 | ## Form 194 | Form automatically attaches on change events so you just have to attach an handle to the onFocus attibute of Form to monitor all the changes. 195 | 196 | It's just a wrapper that allows you to attach onFocus (used to track focus events and keyboard events) and onChange (used to track changes in every field) 197 | 198 | ## Fields 199 | #### Common Rules 200 | * __Every__ field that has to propagate its value in the form __MUST__ have a ref attribute. (Separator and LinkField don't have a ref). 201 | Check the example to understand the use of the ref attribute. 202 | * All the components provided use _Field_ as wrapper in order to have the following props. 203 | 204 | | Prop (parameters) | Description | 205 | | --- | --- | 206 | | helpText | String shown as text under the component | 207 | | helpTextComponent | Custom component that replaces the one provided | 208 | | onPress | onPress method | 209 | 210 | 211 | ### Separator 212 | ```javascript 213 | 216 | ``` 217 | 218 | ### InputField 219 | Input fields can be used to receive text, you can add icons (a react component) to the left and the right side of the field. 220 | 221 | InputField can validate values based on keyboardType property, validation is not "aggressive", just changes a value inside the class, you can access the value using the ref (ex. this.ref.example_input_field.valid). 222 | InputField automatically provides the attibutes _valid_ and _validationErrors_ to guarantee full control to the developer. 223 | 224 | you can customize your validation function by adding a _validationFunction_ prop to the component. _validationFunction_ supports also an array of validators. 225 | 226 | #### Creating a validator 227 | Validators are simple functions have one paramenter (value) and that return true or a string containing an error. 228 | 229 | ```javascript 230 | let workingValidator = (value)=>{ 231 | if(value == '') return "Required"; 232 | //Initial state is null/undefined 233 | if(!value) return true; 234 | var matches = value.match(/\d+/g); 235 | if (matches != null) { 236 | return "First Name can't contain numbers"; 237 | } 238 | 239 | return true; 240 | } 241 | ``` 242 | 243 | _react-native-form-generator_ doesn't depend on any icon library, that gives you freedom of adding any icon or react component you want. 244 | 245 | look at the example here. 246 | 247 | ![react-native-form-generator-inputfield](https://cloud.githubusercontent.com/assets/107390/12533401/1f6d1e7c-c1fd-11e5-96d0-aeba9a313ab9.gif) 248 | 249 | ```javascript 250 | {return true;}} 256 | iconRight={ 257 | { 262 | //i can change the style of the component related to the attibute of example_input_field 263 | if(!!(self.refs && self.refs.example_input_field)){ 264 | if(!self.refs.example_input_field.valid) return {color:'#d52222'} 265 | } 266 | } 267 | )(this)]} 268 | /> 269 | } //React Component 270 | /> 271 | ``` 272 | All the props are passed down to the underlying TextInput Component 273 | 274 | | Prop (parameters) | Description | 275 | | --- | --- | 276 | | label | Text to show in the field, if exists will move the textinput on the right, providing also the right alignment | 277 | | iconLeft | React component, shown on the left of the field, the component needs to have a prop size to allow the inputText to resize properly | 278 | | iconRight | React component, shown on the right of the field, the component needs to have a prop size to allow the inputText to resize properly | 279 | | validationFunction | Function or array of functions, used to pass custom validators to the component| 280 | | keyboardType | possible values: __undefined__, __email-address__| 281 | 282 | | ref methods | Description | 283 | | --- | --- | 284 | | setValue | Sets the value programmatically | 285 | | focus | Focus the textinput component | 286 | 287 | 288 | ### SwitchField 289 | 290 | | Prop (parameters) | Description | 291 | | --- | --- | 292 | | onValueChange(value) | triggered at every value change, returns the new value of the field| 293 | | value | Initial value of the component (Boolean)| 294 | 295 | 296 | ### PickerField 297 | | Prop (parameters) | Description | 298 | | --- | --- | 299 | | onValueChange(value) | triggered at every value change, returns the new value of the field| 300 | | value | Initial value of the component| 301 | | options=[{label:"test",value="Test"},...] | All the possible options, array of objects| 302 | | iconRight | React component, shown on the left of the text field (i suggest Ionicons 'ios-arrow-right' for a nice iOS effect) | 303 | | pickerWrapper | Optional, Custom wrapper of the picker, check the example | 304 | 305 | ### DatePickerField 306 | Every prop is passed down to the underlying DatePickerIOS/DatePickerAndroid component. 307 | 308 | | Prop (parameters) | Description | 309 | | --- | --- | 310 | | onValueChange(date) | triggered at every value change, returns the new value of the field| 311 | | date | Initial date of the component, defaults to (new Date()) | 312 | | iconRight | React component, shown on the left of the text field (i suggest Ionicons 'ios-arrow-right' for a nice iOS effect) | 313 | | dateTimeFormat | Optional, Custom date formatter | 314 | | pickerWrapper | Optional, Custom wrapper of the picker, check the example | 315 | | prettyPrint | Boolean, if true the component returns a string formatted using dateTimeFormat, if false a Date object is returned | 316 | | placeholderComponent | Substitutes the component used to render the placeholder | 317 | | placeholderStyle | Used to style the placeholder | 318 | | valueStyle | Used to style the field's value | 319 | 320 | ### TimePickerField 321 | Every prop is passed down to the underlying DatePickerIOS/DatePickerAndroid component. 322 | Mode is set to 'time' 323 | 324 | | Prop (parameters) | Description | 325 | | --- | --- | 326 | | onValueChange(date) | triggered at every value change, returns the new value of the field| 327 | | date | Initial date of the component, defaults to (new Date()) | 328 | | iconRight | React component, shown on the left of the text field (i suggest Ionicons 'ios-arrow-right' for a nice iOS effect) | 329 | | dateTimeFormat | Optional, Custom date formatter | 330 | | pickerWrapper | Optional, Custom wrapper of the picker, check the example | 331 | | prettyPrint | Boolean, if true the component returns a string formatted using dateTimeFormat, if false a Date object is returned | 332 | | placeholderComponent | Substitutes the component used to render the placeholder | 333 | | placeholderStyle | Used to style the placeholder | 334 | | valueStyle | Used to style the field's value | 335 | 336 | ### LinkField 337 | Every prop is passed down to the underlying DatePickerIOS component. 338 | 339 | | Prop (parameters) | Description | 340 | | --- | --- | 341 | | label | Text to show in the field | 342 | | iconLeft | React component, shown on the left of the text field | 343 | | iconRight | React component, shown on the left of the text field (i suggest Ionicons 'ios-arrow-right' for a nice iOS effect) | 344 | 345 | ### KeyboardEvents 346 | react-native-form-generator ships with an implementation ok KeyboardAwareScrollView that make handling keyboard events a breeze. 347 | check here https://medium.com/@michaelcereda/react-native-forms-the-right-way-315802f989d6#.p9oj79vt3 348 | 349 | ![react-native-form-generator-keyevents](https://cloud.githubusercontent.com/assets/107390/12499708/edb63838-c07a-11e5-9fe4-87979285ccc0.gif) 350 | 351 | ### Custom Fields 352 | With react-native-form-generator is extremely easy to create your own custom fields. 353 | You just need to know that: 354 | 1. Every field is a react component 355 | 2. Evey field will receive 3 props from the Form object: 356 | - fieldRef: contains the reference of the field (workaround on a react-native bug). 357 | - onChange: must be called every time i want to update the values inside the form component. (required) 358 | - onValueChange: can be used whenever you prefer to pass the values to another component. 359 | 360 | Example 361 | ```javascript 362 | 'use strict'; 363 | import {Field} from '../lib/Field'; 364 | 365 | export class SimpleInputField extends React.Component{ 366 | constructor(props){ 367 | super(); 368 | } 369 | } 370 | 371 | handleChange(event){ 372 | var value = event.nativeEvent.text; 373 | 374 | this.setState({value:value}); 375 | 376 | // This updates values in form everytime i update 377 | if(this.props.onChange) this.props.onChange(this.props.fieldRef, value); 378 | if(this.props.onValueChange) this.props.onValueChange(value); 379 | } 380 | 381 | render(){ 382 | return( 383 | 392 | 393 | ) 394 | } 395 | 396 | } 397 | ``` 398 | ### Wrapping fields 399 | You can decide to wrap every field in a component to mantain design uniformity and avoid repetitions (ex. Icons ?!). 400 | 401 | Battle tested example 402 | ```javascript 403 | import {PickerField, LinkField} from 'react-native-form-generator'; 404 | import Icon from 'react-native-vector-icons/Ionicons'; 405 | 406 | let { 407 | StyleSheet 408 | } = React; 409 | 410 | export class WrappedLinkField extends React.Component{ 411 | render(){ 412 | 413 | return 419 | } 420 | } 421 | 422 | export class WrappedPickerField extends React.Component{ 423 | render(){ 424 | 425 | return 439 | } 440 | /> 441 | } 442 | } 443 | 444 | let formStyles = StyleSheet.create({ 445 | alignRight:{ 446 | marginTop: 7, position:'absolute', right: 10 447 | } 448 | }); 449 | ``` 450 | -------------------------------------------------------------------------------- /examples/FormView.js: -------------------------------------------------------------------------------- 1 | /* 2 | This is a view i use in a test app, 3 | very useful to list all the use cases 4 | */ 5 | 6 | import React from 'react'; 7 | 8 | import { 9 | AppRegistry, 10 | StyleSheet, 11 | Text, 12 | View,ScrollView,TouchableHighlight, Modal 13 | } from 'react-native'; 14 | import Icon from 'react-native-vector-icons/Ionicons'; 15 | 16 | 17 | 18 | import { Form, 19 | Separator,InputField, LinkField, 20 | SwitchField, PickerField, DatePickerField, 21 | TimePickerField, CountDownField 22 | } from 'react-native-form-generator'; 23 | 24 | class CustomModal extends React.Component{ 25 | handleClose(){ 26 | this.props.onHidePicker && this.props.onHidePicker(); 27 | } 28 | render(){ 29 | return 30 | 31 | 39 | A Custom Wrapper for your picker 46 | {this.props.children} 47 | 48 | 51 | Close 54 | 55 | 56 | 57 | } 58 | } 59 | 60 | class WrappedIcon extends React.Component { 61 | render() { 62 | return ( 63 | 64 | ); 65 | } 66 | } 67 | 68 | export class FormView extends React.Component{ 69 | constructor(props){ 70 | super(props); 71 | this.state = { 72 | formData:{} 73 | } 74 | } 75 | handleFormChange(formData){ 76 | /* 77 | formData will contain all the values of the form, 78 | in this example. 79 | 80 | formData = { 81 | first_name:"", 82 | last_name:"", 83 | gender: '', 84 | birthday: Date, 85 | has_accepted_conditions: bool 86 | } 87 | */ 88 | 89 | this.setState({formData:formData}) 90 | this.props.onFormChange && this.props.onFormChange(formData); 91 | } 92 | handleFormFocus(e, component){ 93 | //console.log(e, component); 94 | } 95 | openTermsAndConditionsURL(){ 96 | 97 | } 98 | resetForm(){ 99 | 100 | this.refs.registrationForm.refs.first_name.setValue(""); 101 | this.refs.registrationForm.refs.last_name.setValue(""); 102 | this.refs.registrationForm.refs.other_input.setValue(""); 103 | this.refs.registrationForm.refs.meeting.setDate(new Date()); 104 | this.refs.registrationForm.refs.has_accepted_conditions.setValue(false); 105 | } 106 | render(){ 107 | 108 | 109 | 110 | return ( 111 |
116 | 117 | { 122 | 123 | if(Object.keys(self.refs).length !== 0){ 124 | if(!self.refs.registrationForm.refs.first_name.valid){ 125 | return self.refs.registrationForm.refs.first_name.validationErrors.join("\n"); 126 | } 127 | 128 | } 129 | // if(!!(self.refs && self.refs.first_name.valid)){ 130 | // } 131 | })(this)} 132 | validationFunction={[(value)=>{ 133 | /* 134 | you can have multiple validators in a single function or an array of functions 135 | */ 136 | 137 | if(value == '') return "Required"; 138 | //Initial state is null/undefined 139 | if(!value) return true; 140 | var matches = value.match(/\d+/g); 141 | if (matches != null) { 142 | return "First Name can't contain numbers"; 143 | } 144 | 145 | return true; 146 | }, (value)=>{ 147 | if(!value) return true; 148 | if(value.indexOf('4')!=-1){ 149 | return "I can't stand number 4"; 150 | } 151 | return true; 152 | }]} 153 | /> 154 | } 156 | ref='last_name' value="Default Value" placeholder='Last Name'/> 157 | 162 | Custom Help Text Component} /> 168 | 169 | {}} 171 | iconLeft={} 172 | iconRight={} 173 | /> 174 | 177 | 187 | , 191 | 192 | ]} 193 | placeholder='Birthday'/> 194 | } 197 | prettyPrint={true} 198 | pickerWrapper={} 199 | /> 200 | 203 | , 205 | 206 | ]} 207 | minimumDate={new Date('1/1/1900')} 208 | maximumDate={new Date()} mode="datetime" placeholder='Meeting'/> 209 | 210 | {JSON.stringify(this.state.formData)} 211 | 214 | Reset 218 | this.refs.registrationForm.refs.other_input.focus()} 221 | underlayColor='#78ac05'> 222 | Focus First Name 228 |
); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import {Form} from './src/Form'; 4 | import {Separator} from './src/fields/Separator'; 5 | import {InputField} from './src/fields/InputField'; 6 | import {LinkField} from './src/fields/LinkField'; 7 | import {SwitchField} from './src/fields/SwitchField'; 8 | import {PickerField} from './src/fields/PickerField'; 9 | import {DatePickerField} from './src/fields/DatePickerField'; 10 | import {TimePickerField} from './src/fields/TimePickerField'; 11 | import {CountDownField} from './src/fields/CountDownField'; 12 | 13 | 14 | //import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view' 15 | export { 16 | Form, 17 | Separator, InputField, LinkField, 18 | SwitchField, PickerField, DatePickerField, 19 | CountDownField, TimePickerField 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "https://github.com/MichaelCereda/react-native-form-generator.git", 5 | "/Users/michael/Projects/OpenSource/TestApp" 6 | ] 7 | ], 8 | "_from": "git+https://github.com/MichaelCereda/react-native-form-generator.git", 9 | "_id": "react-native-form-generator@0.7.2", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/react-native-form-generator", 13 | "_phantomChildren": {}, 14 | "_requested": { 15 | "hosted": { 16 | "directUrl": "https://raw.githubusercontent.com/MichaelCereda/react-native-form-generator/master/package.json", 17 | "gitUrl": "git://github.com/MichaelCereda/react-native-form-generator.git", 18 | "httpsUrl": "git+https://github.com/MichaelCereda/react-native-form-generator.git", 19 | "shortcut": "github:MichaelCereda/react-native-form-generator", 20 | "ssh": "git@github.com:MichaelCereda/react-native-form-generator.git", 21 | "sshUrl": "git+ssh://git@github.com/MichaelCereda/react-native-form-generator.git", 22 | "type": "github" 23 | }, 24 | "name": null, 25 | "raw": "https://github.com/MichaelCereda/react-native-form-generator.git", 26 | "rawSpec": "https://github.com/MichaelCereda/react-native-form-generator.git", 27 | "scope": null, 28 | "spec": "git+https://github.com/MichaelCereda/react-native-form-generator.git", 29 | "type": "hosted" 30 | }, 31 | "_requiredBy": [ 32 | "/" 33 | ], 34 | "_resolved": "git+https://github.com/MichaelCereda/react-native-form-generator.git#f67d26986f3916668d0c3e9e74b66d0ed6165cba", 35 | "_shasum": "cca28fd48d65a98a0a502b5cf84c3b02462ac1c9", 36 | "_shrinkwrap": null, 37 | "_spec": "https://github.com/MichaelCereda/react-native-form-generator.git", 38 | "_where": "/Users/michael/Projects/OpenSource/TestApp", 39 | "author": { 40 | "name": "Michael Cereda", 41 | "url": "info@michaelcereda.com" 42 | }, 43 | "bugs": { 44 | "url": "https://github.com/MichaelCereda/react-native-form-generator/issues" 45 | }, 46 | "dependencies": { 47 | "prop-types": "^15.5.10" 48 | }, 49 | "description": "Generate amazing React Native forms in a breeze", 50 | "gitHead": "f67d26986f3916668d0c3e9e74b66d0ed6165cba", 51 | "homepage": "https://github.com/MichaelCereda/react-native-form-generator#readme", 52 | "keywords": [ 53 | "react", 54 | "native", 55 | "forms", 56 | "input", 57 | "react", 58 | "native" 59 | ], 60 | "license": "MIT", 61 | "main": "index.js", 62 | "name": "react-native-form-generator", 63 | "optionalDependencies": {}, 64 | "readme": "React Native Form Generator\n================\nGenerate forms with native look and feel in a breeze\n\n[![NPM](https://nodei.co/npm/react-native-form-generator.png)](https://nodei.co/npm/react-native-form-generator/)\n\n\n\n![react-native-form-generator](https://cloud.githubusercontent.com/assets/107390/12499709/edc1c298-c07a-11e5-916c-394de83ebe51.gif)\n\n\n## Features\n* Doesn't have dependencies\n* Use your own icon pack\n* Easy to use and clean, react style syntax\n* Automatic events handling\n* Supports custom fields and styles without adding any weird syntax (just create a react component)\n* Applies by default the current OS style\n* Inspired by tcomb, the good parts\n* Performances: just the field changed gets a setState\n* You don't need to create a 'Model' or a 'Struct' that contains your data, just create a form component (the React's way)\n* Validate InputFields based on keyboardType (can be overridden using validationFunction)\n\n[My blogpost about React Native Form Generator](https://medium.com/@michaelcereda/react-native-forms-the-right-way-315802f989d6#.p9oj79vt3)\n\n## Installation\n```\n npm install --save react-native-form-generator\n```\n## Warning: I'm actively working on this project\n\n* Pull requests are very very welcome\n* All the elements are tested and stable against normal use cases (but i expect to do a lot of changes here and there)\n* Slider hasn't been created\n* I have to document the code properly and do some housekeeping, i apologize in advance.\n* Android support is coming.\n\n* This project requires (for some fields) react-native-vector-icons to show icons in some fields (i will remove this dependency soon)\n\n## Example\nthe example below generates the form you see in the animation\n```javascript\n\nimport { Form, InputField,\n Separator, SwitchField, LinkField ,\n PickerField, DatePickerField\n } from 'react-native-form-generator';\n\n export class MyCoolComponent extends React.Component{\n handleFormChange(formData){\n /*\n formData will contain all the values of the form,\n in this example.\n\n formData = {\n first_name:\"\",\n last_name:\"\",\n gender: '',\n birthday: Date,\n has_accepted_conditions: bool\n }\n */\n\n }\n render(){\n \n \n \n \n \n \n \n \n );\n }\n}\n```\n\n## Form\nForm automatically attaches on change events so you just have to attach an handle to the onFocus attibute of Form to monitor all the changes.\n\nIt's just a wrapper that allows you to attach onFocus (used to track focus events and keyboard events) and onChange (used to track changes in every field)\n\n## Fields\n#### Common Rules\n* Every field that has to propagate its value in the form needs to have a ref attribute. (Separator and LinkField don't have a ref).\nCheck the example to understand the use of the ref attribute.\n\n\n### Separator\n```javascript\n \n```\n\n### InputField\nInput fields can be used to receive text, you can add icons (a react component) to the left and the right side of the field.\n\nInputField can validate values based on keyboardType property, validation is not \"aggressive\", just changes a value inside the class, you can access the value using the ref (ex. this.ref.example_input_field.valid).\n\nyou can customize your validation function by adding a validationFunction property to the component\n\nreact-native-form-generator doesn't depend on any icon library, that gives you freedom of adding any icon or react component you want.\n\nlook at the example here.\n\n![react-native-form-generator-inputfield](https://cloud.githubusercontent.com/assets/107390/12533401/1f6d1e7c-c1fd-11e5-96d0-aeba9a313ab9.gif)\n\n```javascript\n {return true;}}\n iconRight={\n {\n //i can change the style of the component related to the attibute of example_input_field\n if(!!(self.refs && self.refs.example_input_field)){\n if(!self.refs.example_input_field.valid) return {color:'#d52222'}\n }\n }\n )(this)]}\n />\n } //React Component\n />\n```\nAll the props are passed down to the underlying TextInput Component\n\n| Prop (parameters) | Description |\n| --- | --- |\n| label | Text to show in the field, if exists will move the textinput on the right, providing also the right alignment |\n| iconLeft | React component, shown on the left of the field, the component needs to have a prop size to allow the inputText to resize properly |\n| iconRight | React component, shown on the right of the field, the component needs to have a prop size to allow the inputText to resize properly |\n\n### SwitchField\n\n| Prop (parameters) | Description |\n| --- | --- |\n| onValueChange(value) | triggered at every value change, returns the new value of the field|\n| value | Initial value of the component (Boolean)|\n\n\n### PickerField\n| Prop (parameters) | Description |\n| --- | --- |\n| onValueChange(value) | triggered at every value change, returns the new value of the field|\n| value | Initial value of the component|\n| options=[{label:\"test\",value=\"Test\"},...] | All the possible options, array of objects|\n| iconRight | React component, shown on the left of the text field (i suggest Ionicons 'ios-arrow-right' for a nice iOS effect) |\n\n### DatePickerField\nEvery prop is passed down to the underlying DatePickerIOS component.\n\n| Prop (parameters) | Description |\n| --- | --- |\n| onValueChange(date) | triggered at every value change, returns the new value of the field|\n| date | Initial date of the component, defaults to (new Date()) |\n| iconRight | React component, shown on the left of the text field (i suggest Ionicons 'ios-arrow-right' for a nice iOS effect) |\n\n### LinkField\nEvery prop is passed down to the underlying DatePickerIOS component.\n\n| Prop (parameters) | Description |\n| --- | --- |\n| label | Text to show in the field |\n| iconLeft | React component, shown on the left of the text field |\n| iconRight | React component, shown on the left of the text field (i suggest Ionicons 'ios-arrow-right' for a nice iOS effect) |\n\n### KeyboardEvents\nreact-native-form-generator ships with an implementation ok KeyboardAwareScrollView that make handling keyboard events a breeze.\ncheck here https://medium.com/@michaelcereda/react-native-forms-the-right-way-315802f989d6#.p9oj79vt3\n\n![react-native-form-generator-keyevents](https://cloud.githubusercontent.com/assets/107390/12499708/edb63838-c07a-11e5-9fe4-87979285ccc0.gif)\n\n### Custom Fields\nWith react-native-form-generator is extremely easy to create your own custom fields.\nYou just need to know that:\n1. Every field is a react component\n2. Evey field will receive 3 props from the Form object:\n - fieldRef: contains the reference of the field (workaround on a react-native bug).\n - onChange: must be called every time i want to update the values inside the form component. (required)\n - onValueChange: can be used whenever you prefer to pass the values to another component.\n\nExample\n```javascript\n'use strict';\nimport {Field} from '../lib/Field';\n\nexport class SimpleInputField extends React.Component{\n constructor(props){\n super();\n }\n }\n\n handleChange(event){\n var value = event.nativeEvent.text;\n\n this.setState({value:value});\n\n // This updates values in form everytime i update\n if(this.props.onChange) this.props.onChange(this.props.fieldRef, value);\n if(this.props.onValueChange) this.props.onValueChange(value);\n }\n\n render(){\n return(\n \n \n )\n}\n\n}\n```\n### Wrapping fields\nYou can decide to wrap every field in a component to mantain design uniformity and avoid repetitions (ex. Icons ?!).\n\nBattle tested example\n```javascript\nimport {PickerField, LinkField} from 'react-native-form-generator';\nimport Icon from 'react-native-vector-icons/Ionicons';\n\nlet {\n StyleSheet\n} = React;\n\nexport class WrappedLinkField extends React.Component{\n render(){\n\n return \n }\n}\n\nexport class WrappedPickerField extends React.Component{\n render(){\n\n return \n }\n />\n }\n}\n\nlet formStyles = StyleSheet.create({\n alignRight:{\n marginTop: 7, position:'absolute', right: 10\n }\n });\n```\n", 65 | "readmeFilename": "README.md", 66 | "repository": { 67 | "type": "git", 68 | "url": "git+https://github.com/MichaelCereda/react-native-form-generator.git" 69 | }, 70 | "scripts": { 71 | "test": "echo \"Error: no test specified\" && exit 1" 72 | }, 73 | "version": "0.9.10" 74 | } 75 | -------------------------------------------------------------------------------- /src/Form.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | let { View, TextInput, 3 | StyleSheet, 4 | ScrollView, 5 | Text, 6 | SliderIOS, 7 | TouchableWithoutFeedback 8 | } = require('react-native'); 9 | 10 | // import {Separator} from './fields/Separator'; 11 | 12 | export class Form extends React.Component{ 13 | constructor(props){ 14 | super(); 15 | 16 | this.values = {}; 17 | 18 | } 19 | 20 | handleFieldFocused(event, inputHandle){ 21 | this.props.onFocus && this.props.onFocus(event, inputHandle); 22 | } 23 | handleFieldChange(field_ref, value){ 24 | this.values[field_ref] = value; 25 | this.props.onChange && this.props.onChange(this.values); 26 | } 27 | getValues(){ 28 | return this.values; 29 | } 30 | 31 | underscoreToSpaced(str){ 32 | var words = str.split('_'); 33 | var res=[]; 34 | words.map(function(word, i){ 35 | res.push(word.charAt(0).toUpperCase() + word.slice(1)); 36 | }) 37 | 38 | return res.join(' '); 39 | } 40 | 41 | render(){ 42 | let wrappedChildren = []; 43 | 44 | React.Children.map(this.props.children, (child, i)=> { 45 | if (!child) { 46 | return; 47 | } 48 | wrappedChildren.push(React.cloneElement(child, { 49 | key: child.ref || child.type+i, 50 | fieldRef : child.ref, 51 | ref: child.ref, 52 | onFocus:this.handleFieldFocused.bind(this), 53 | onChange:this.handleFieldChange.bind(this, child.ref) 54 | } 55 | )); 56 | }, this); 57 | 58 | return ( 59 | 60 | {wrappedChildren} 61 | 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/KeyboardAwareScrollView.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { ScrollView, DeviceEventEmitter } from 'react-native' 4 | import StyleSheetPropType from 'react-native/Libraries/StyleSheet/StyleSheetPropType' 5 | import ViewStylePropTypes from 'react-native/Libraries/Components/View/ViewStylePropTypes' 6 | 7 | export class KeyboardAwareScrollView extends React.Component { 8 | constructor (props) { 9 | super(props) 10 | this.state = { 11 | keyboardSpace: 0, 12 | } 13 | this.updateKeyboardSpace = this.updateKeyboardSpace.bind(this) 14 | this.resetKeyboardSpace = this.resetKeyboardSpace.bind(this) 15 | } 16 | 17 | // Keyboard actions 18 | // TODO: automatically handle TabBar height instead of using props 19 | updateKeyboardSpace (frames) { 20 | // let coordinatesHeight = (frames.endCoordinates)? frames.endCoordinates.height : frames.end.height; 21 | let coordinatesHeight = frames.endCoordinates.height; 22 | const keyboardSpace = (this.props.viewIsInsideTabBar) ? coordinatesHeight - 49 : coordinatesHeight 23 | this.setState({ 24 | keyboardSpace: keyboardSpace, 25 | }) 26 | return { 27 | 28 | } 29 | } 30 | 31 | resetKeyboardSpace () { 32 | this.setState({ 33 | keyboardSpace: 0, 34 | }) 35 | } 36 | 37 | componentDidMount () { 38 | // Keyboard events 39 | DeviceEventEmitter.addListener('keyboardWillShow', this.updateKeyboardSpace) 40 | DeviceEventEmitter.addListener('keyboardWillHide', this.resetKeyboardSpace) 41 | } 42 | 43 | componentWillUnmount () { 44 | // TODO: figure out if removeAllListeners is the right thing to do 45 | DeviceEventEmitter.removeAllListeners('keyboardWillShow') 46 | DeviceEventEmitter.removeAllListeners('keyboardWillHide') 47 | } 48 | 49 | /** 50 | * @param extraHeight: takes an extra height in consideration. 51 | */ 52 | scrollToFocusedInput (event, reactNode, extraHeight = 69) { 53 | const scrollView = this.refs.keyboardScrollView.getScrollResponder(); 54 | setTimeout(() => { 55 | scrollView.scrollResponderScrollNativeHandleToKeyboard( 56 | reactNode, extraHeight, true 57 | ) 58 | }, 220) 59 | } 60 | 61 | render () { 62 | return ( 63 | 70 | {this.props.children} 71 | 72 | ) 73 | } 74 | } 75 | 76 | KeyboardAwareScrollView.propTypes = { 77 | style: StyleSheetPropType(ViewStylePropTypes), 78 | children: PropTypes.node, 79 | viewIsInsideTabBar: PropTypes.bool, 80 | } 81 | -------------------------------------------------------------------------------- /src/fields/CountDownField.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import ReactNative from 'react-native'; 5 | let { View, StyleSheet, TextInput, Text, PickerIOS} = ReactNative; 6 | 7 | 8 | import {DatePickerComponent} from '../lib/DatePickerComponent'; 9 | 10 | export class CountDownField extends React.Component{ 11 | setTime(date){ 12 | this.refs.datePickerComponent.setDate(date); 13 | } 14 | render(){ 15 | /* 16 | 17 | */ 18 | return() 31 | } 32 | 33 | } 34 | 35 | 36 | 37 | let formStyles = StyleSheet.create({ 38 | form:{ 39 | 40 | }, 41 | alignRight:{ 42 | marginTop: 7, position:'absolute', right: 10 43 | }, 44 | noBorder:{ 45 | borderTopWidth: 0, 46 | borderBottomWidth: 0 47 | }, 48 | separatorContainer:{ 49 | // borderTopColor: '#C8C7CC', 50 | // borderTopWidth: 1, 51 | paddingTop: 35, 52 | borderBottomColor: '#C8C7CC', 53 | borderBottomWidth: 1, 54 | 55 | }, 56 | separator:{ 57 | 58 | paddingLeft: 10, 59 | paddingRight: 10, 60 | color: '#6D6D72', 61 | paddingBottom: 7 62 | 63 | }, 64 | fieldsWrapper:{ 65 | // borderTopColor: '#afafaf', 66 | // borderTopWidth: 1, 67 | }, 68 | horizontalContainer:{ 69 | flexDirection: 'row', 70 | 71 | justifyContent: 'flex-start' 72 | }, 73 | fieldContainer:{ 74 | borderBottomWidth: 1, 75 | borderBottomColor: '#C8C7CC', 76 | backgroundColor: 'white', 77 | justifyContent: 'center', 78 | height: 45 79 | }, 80 | fieldValue:{ 81 | fontSize: 34/2, 82 | paddingLeft: 10, 83 | paddingRight: 10, 84 | marginRight:10, 85 | paddingTop: 4, 86 | justifyContent: 'center', 87 | 88 | color: '#C7C7CC' 89 | }, 90 | fieldText:{ 91 | fontSize: 34/2, 92 | paddingLeft: 10, 93 | paddingRight: 10, 94 | justifyContent: 'center', 95 | lineHeight: 32 96 | }, 97 | input:{ 98 | paddingLeft: 10, 99 | paddingRight: 10, 100 | 101 | }, 102 | helpTextContainer:{ 103 | marginTop:9, 104 | marginBottom: 25, 105 | paddingLeft: 20, 106 | paddingRight: 20, 107 | 108 | }, 109 | helpText:{ 110 | color: '#7a7a7a' 111 | } 112 | }); 113 | -------------------------------------------------------------------------------- /src/fields/DatePickerField.android.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import ReactNative from 'react-native'; 5 | let { View, StyleSheet, TextInput, Text, PickerIOS} = ReactNative; 6 | 7 | 8 | import {DatePickerComponent} from '../lib/DatePickerComponent'; 9 | 10 | export class DatePickerField extends React.Component{ 11 | setDate(date){ 12 | this.refs.datePickerComponent.setDate(date); 13 | } 14 | render(){ 15 | 16 | return() 29 | } 30 | 31 | } 32 | 33 | 34 | 35 | let formStyles = StyleSheet.create({ 36 | form:{ 37 | 38 | }, 39 | alignRight:{ 40 | marginTop: 7, position:'absolute', right: 10 41 | }, 42 | noBorder:{ 43 | borderTopWidth: 0, 44 | borderBottomWidth: 0 45 | }, 46 | separatorContainer:{ 47 | // borderTopColor: '#C8C7CC', 48 | // borderTopWidth: 1, 49 | paddingTop: 35, 50 | borderBottomColor: '#C8C7CC', 51 | borderBottomWidth: 1, 52 | 53 | }, 54 | separator:{ 55 | 56 | paddingLeft: 10, 57 | paddingRight: 10, 58 | color: '#6D6D72', 59 | paddingBottom: 7 60 | 61 | }, 62 | fieldsWrapper:{ 63 | // borderTopColor: '#afafaf', 64 | // borderTopWidth: 1, 65 | }, 66 | horizontalContainer:{ 67 | flexDirection: 'row', 68 | 69 | justifyContent: 'flex-start' 70 | }, 71 | fieldContainer:{ 72 | borderBottomWidth: 1, 73 | borderBottomColor: '#C8C7CC', 74 | backgroundColor: 'white', 75 | justifyContent: 'center', 76 | height: 45 77 | }, 78 | fieldValue:{ 79 | fontSize: 34/2, 80 | paddingLeft: 10, 81 | paddingRight: 10, 82 | marginRight:10, 83 | paddingTop: 4, 84 | justifyContent: 'center', 85 | 86 | color: '#C7C7CC' 87 | }, 88 | fieldText:{ 89 | fontSize: 34/2, 90 | paddingLeft: 10, 91 | paddingRight: 10, 92 | justifyContent: 'center', 93 | lineHeight: 32 94 | }, 95 | input:{ 96 | paddingLeft: 10, 97 | paddingRight: 10, 98 | 99 | }, 100 | helpTextContainer:{ 101 | marginTop:9, 102 | marginBottom: 25, 103 | paddingLeft: 20, 104 | paddingRight: 20, 105 | 106 | }, 107 | helpText:{ 108 | color: '#7a7a7a' 109 | } 110 | }); 111 | -------------------------------------------------------------------------------- /src/fields/DatePickerField.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import ReactNative from 'react-native'; 5 | let { View, StyleSheet, TextInput, Text, PickerIOS} = ReactNative; 6 | 7 | 8 | import {DatePickerComponent} from '../lib/DatePickerComponent'; 9 | 10 | export class DatePickerField extends React.Component{ 11 | setDate(date){ 12 | this.refs.datePickerComponent.setDate(date); 13 | } 14 | render(){ 15 | return() 29 | } 30 | 31 | } 32 | 33 | let formStyles = StyleSheet.create({ 34 | form:{ 35 | 36 | }, 37 | alignRight:{ 38 | marginTop: 7, position:'absolute', right: 10 39 | }, 40 | noBorder:{ 41 | borderTopWidth: 0, 42 | borderBottomWidth: 0 43 | }, 44 | separatorContainer:{ 45 | // borderTopColor: '#C8C7CC', 46 | // borderTopWidth: 1, 47 | paddingTop: 35, 48 | borderBottomColor: '#C8C7CC', 49 | borderBottomWidth: 1, 50 | 51 | }, 52 | separator:{ 53 | 54 | paddingLeft: 10, 55 | paddingRight: 10, 56 | color: '#6D6D72', 57 | paddingBottom: 7 58 | 59 | }, 60 | fieldsWrapper:{ 61 | // borderTopColor: '#afafaf', 62 | // borderTopWidth: 1, 63 | }, 64 | horizontalContainer:{ 65 | flexDirection: 'row', 66 | 67 | justifyContent: 'flex-start' 68 | }, 69 | fieldContainer:{ 70 | borderBottomWidth: 1, 71 | borderBottomColor: '#C8C7CC', 72 | backgroundColor: 'white', 73 | justifyContent: 'center', 74 | height: 45 75 | }, 76 | fieldValue:{ 77 | fontSize: 34/2, 78 | paddingLeft: 10, 79 | paddingRight: 10, 80 | marginRight:10, 81 | paddingTop: 4, 82 | justifyContent: 'center', 83 | 84 | color: '#C7C7CC' 85 | }, 86 | fieldText:{ 87 | fontSize: 34/2, 88 | paddingLeft: 10, 89 | paddingRight: 10, 90 | justifyContent: 'center', 91 | lineHeight: 32 92 | }, 93 | input:{ 94 | paddingLeft: 10, 95 | paddingRight: 10, 96 | 97 | }, 98 | helpTextContainer:{ 99 | marginTop:9, 100 | marginBottom: 25, 101 | paddingLeft: 20, 102 | paddingRight: 20, 103 | 104 | }, 105 | helpText:{ 106 | color: '#7a7a7a' 107 | } 108 | }); 109 | -------------------------------------------------------------------------------- /src/fields/InputField.android.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import ReactNative from 'react-native'; 5 | import {InputComponent} from '../lib/InputComponent'; 6 | 7 | const {StyleSheet} = ReactNative; 8 | 9 | export class InputField extends React.Component{ 10 | 11 | handleValidation(isValid, validationErrors){ 12 | this.valid = isValid; 13 | this.validationErrors = validationErrors; 14 | } 15 | setValue(value){ 16 | this.refs.fieldComponent.setValue(value) 17 | } 18 | focus(){ 19 | this.refs.fieldComponent.focus() 20 | } 21 | render(){ 22 | return( 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/fields/InputField.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import PropTypes from 'prop-types'; 5 | import ReactNative from 'react-native'; 6 | import {InputComponent} from '../lib/InputComponent'; 7 | 8 | const {StyleSheet} = ReactNative; 9 | 10 | export class InputField extends React.Component{ 11 | handleValidation(isValid, validationErrors){ 12 | this.valid = isValid; 13 | this.validationErrors = validationErrors; 14 | } 15 | setValue(value){ 16 | this.refs.fieldComponent.setValue(value) 17 | } 18 | focus(){ 19 | this.refs.fieldComponent.focus() 20 | } 21 | render(){ 22 | return( 39 | ) 40 | } 41 | 42 | } 43 | 44 | InputField.propTypes = { 45 | multiline: PropTypes.bool, 46 | placeholder:PropTypes.string, 47 | } 48 | 49 | 50 | let fieldStyles =StyleSheet.create({ 51 | input:{ 52 | paddingLeft: 10, 53 | paddingRight: 10, 54 | }, 55 | }); 56 | 57 | let formStyles = StyleSheet.create({ 58 | form:{ 59 | 60 | }, 61 | alignRight:{ 62 | marginTop: 7, position:'absolute', right: 10 63 | }, 64 | textRight:{ 65 | textAlign: 'right' 66 | }, 67 | multiline:{ 68 | lineHeight: 32, 69 | fontSize: 34/2, 70 | paddingBottom:10 71 | }, 72 | separatorContainer:{ 73 | // borderTopColor: '#C8C7CC', 74 | // borderTopWidth: 1, 75 | paddingTop: 35, 76 | borderBottomColor: '#C8C7CC', 77 | borderBottomWidth: 1, 78 | 79 | }, 80 | 81 | fieldsWrapper:{ 82 | // borderTopColor: '#afafaf', 83 | // borderTopWidth: 1, 84 | }, 85 | horizontalContainer:{ 86 | flexDirection: 'row', 87 | 88 | justifyContent: 'flex-start' 89 | }, 90 | fieldContainer:{ 91 | borderBottomWidth: 1, 92 | borderBottomColor: '#C8C7CC', 93 | backgroundColor: 'white', 94 | justifyContent: 'center', 95 | }, 96 | fieldText:{ 97 | fontSize: 34/2, 98 | paddingLeft: 10, 99 | paddingRight: 10, 100 | justifyContent: 'center', 101 | lineHeight: 32, 102 | 103 | }, 104 | input:{ 105 | paddingLeft: 10, 106 | paddingRight: 10, 107 | 108 | }, 109 | helpTextContainer:{ 110 | marginTop:9, 111 | marginBottom: 25, 112 | paddingLeft: 20, 113 | paddingRight: 20, 114 | 115 | }, 116 | helpText:{ 117 | color: '#7a7a7a' 118 | } 119 | }); 120 | 121 | 122 | {/* 124 | {(this.props.iconLeft) 125 | ? this.props.iconLeft 126 | : null 127 | } 128 | {(this.props.label) 129 | ? 130 | {this.props.label} 134 | : null 135 | } 136 | 157 | {(this.props.iconRight) 158 | ? this.props.iconRight 159 | : null 160 | } 161 | */} 162 | -------------------------------------------------------------------------------- /src/fields/LinkField.android.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | let { View, StyleSheet, TextInput, Text} = require('react-native'); 5 | import {LinkComponent} from '../lib/LinkComponent'; 6 | 7 | 8 | export class LinkField extends React.Component{ 9 | render(){ 10 | return( 18 | ) 19 | } 20 | 21 | } 22 | 23 | let formStyles = StyleSheet.create({ 24 | fieldContainer:{ 25 | borderBottomWidth: 1, 26 | borderBottomColor: '#C8C7CC', 27 | backgroundColor: 'white', 28 | justifyContent: 'center', 29 | height: 45 30 | }, 31 | horizontalContainer:{ 32 | flexDirection: 'row', 33 | 34 | justifyContent: 'flex-start' 35 | }, 36 | fieldText:{ 37 | fontSize: 34/2, 38 | paddingLeft: 10, 39 | paddingRight: 10, 40 | justifyContent: 'center', 41 | lineHeight: 32 42 | }, 43 | }); 44 | -------------------------------------------------------------------------------- /src/fields/LinkField.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | let { View, StyleSheet, TextInput, Text} = require('react-native'); 5 | import {LinkComponent} from '../lib/LinkComponent'; 6 | 7 | 8 | export class LinkField extends React.Component{ 9 | render(){ 10 | return( 19 | ) 20 | } 21 | 22 | } 23 | 24 | let formStyles = StyleSheet.create({ 25 | fieldContainer:{ 26 | borderBottomWidth: 1, 27 | borderBottomColor: '#C8C7CC', 28 | backgroundColor: 'white', 29 | justifyContent: 'center', 30 | height: 45 31 | }, 32 | horizontalContainer:{ 33 | flexDirection: 'row', 34 | 35 | justifyContent: 'flex-start' 36 | }, 37 | fieldText:{ 38 | fontSize: 34/2, 39 | paddingLeft: 10, 40 | paddingRight: 10, 41 | justifyContent: 'center', 42 | lineHeight: 32, 43 | flex:2 44 | }, 45 | }); 46 | -------------------------------------------------------------------------------- /src/fields/PickerField.android.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import ReactNative from 'react-native'; 5 | let { View, StyleSheet, TextInput, Text, PickerIOS} = ReactNative; 6 | 7 | 8 | import {PickerComponent} from '../lib/PickerComponent'; 9 | 10 | export class PickerField extends React.Component{ 11 | setValue(value){ 12 | this.refs.fieldComponent.setValue(value) 13 | } 14 | render(){ 15 | return() 29 | } 30 | 31 | } 32 | 33 | 34 | 35 | let formStyles = StyleSheet.create({ 36 | fieldContainer:{ 37 | borderBottomWidth: 1, 38 | borderBottomColor: '#C8C7CC', 39 | backgroundColor: 'white', 40 | justifyContent: 'center', 41 | height: 45 42 | }, 43 | form:{ 44 | 45 | }, 46 | alignRight:{ 47 | marginTop: 7, position:'absolute', right: 10 48 | }, 49 | noBorder:{ 50 | borderTopWidth: 0, 51 | borderBottomWidth: 0 52 | }, 53 | separatorContainer:{ 54 | // borderTopColor: '#C8C7CC', 55 | // borderTopWidth: 1, 56 | paddingTop: 35, 57 | borderBottomColor: '#C8C7CC', 58 | borderBottomWidth: 1, 59 | 60 | }, 61 | separator:{ 62 | 63 | paddingLeft: 10, 64 | paddingRight: 10, 65 | color: '#6D6D72', 66 | paddingBottom: 7 67 | 68 | }, 69 | fieldsWrapper:{ 70 | // borderTopColor: '#afafaf', 71 | // borderTopWidth: 1, 72 | }, 73 | horizontalContainer:{ 74 | flexDirection: 'row', 75 | 76 | justifyContent: 'flex-start' 77 | }, 78 | 79 | fieldValue:{ 80 | fontSize: 34/2, 81 | paddingLeft: 10, 82 | paddingRight: 10, 83 | marginRight:10, 84 | paddingTop: 4, 85 | justifyContent: 'center', 86 | 87 | color: '#C7C7CC' 88 | }, 89 | fieldText:{ 90 | fontSize: 34/2, 91 | paddingLeft: 10, 92 | paddingRight: 10, 93 | justifyContent: 'center', 94 | lineHeight: 32 95 | }, 96 | input:{ 97 | paddingLeft: 10, 98 | paddingRight: 10, 99 | 100 | }, 101 | helpTextContainer:{ 102 | marginTop:9, 103 | marginBottom: 25, 104 | paddingLeft: 20, 105 | paddingRight: 20, 106 | 107 | }, 108 | helpText:{ 109 | color: '#7a7a7a' 110 | } 111 | }); 112 | -------------------------------------------------------------------------------- /src/fields/PickerField.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import ReactNative from 'react-native'; 5 | let { View, StyleSheet, TextInput, Text, PickerIOS} = ReactNative; 6 | 7 | 8 | import {PickerComponent} from '../lib/PickerComponent'; 9 | 10 | export class PickerField extends React.Component{ 11 | setValue(value){ 12 | this.refs.fieldComponent.setValue(value) 13 | } 14 | render(){ 15 | return() 28 | } 29 | 30 | } 31 | 32 | 33 | 34 | let formStyles = StyleSheet.create({ 35 | form:{ 36 | 37 | }, 38 | alignRight:{ 39 | marginTop: 7, position:'absolute', right: 10 40 | }, 41 | noBorder:{ 42 | borderTopWidth: 0, 43 | borderBottomWidth: 0 44 | }, 45 | separatorContainer:{ 46 | // borderTopColor: '#C8C7CC', 47 | // borderTopWidth: 1, 48 | paddingTop: 35, 49 | borderBottomColor: '#C8C7CC', 50 | borderBottomWidth: 1, 51 | 52 | }, 53 | separator:{ 54 | 55 | paddingLeft: 10, 56 | paddingRight: 10, 57 | color: '#6D6D72', 58 | paddingBottom: 7 59 | 60 | }, 61 | fieldsWrapper:{ 62 | // borderTopColor: '#afafaf', 63 | // borderTopWidth: 1, 64 | }, 65 | horizontalContainer:{ 66 | flexDirection: 'row', 67 | 68 | justifyContent: 'flex-start' 69 | }, 70 | fieldContainer:{ 71 | borderBottomWidth: 1, 72 | borderBottomColor: '#C8C7CC', 73 | backgroundColor: 'white', 74 | justifyContent: 'center', 75 | height: 45 76 | }, 77 | fieldValue:{ 78 | fontSize: 34/2, 79 | paddingLeft: 10, 80 | paddingRight: 10, 81 | marginRight:10, 82 | paddingTop: 4, 83 | justifyContent: 'center', 84 | 85 | color: '#C7C7CC' 86 | }, 87 | fieldText:{ 88 | fontSize: 34/2, 89 | paddingLeft: 10, 90 | paddingRight: 10, 91 | justifyContent: 'center', 92 | lineHeight: 32 93 | }, 94 | input:{ 95 | paddingLeft: 10, 96 | paddingRight: 10, 97 | 98 | }, 99 | helpTextContainer:{ 100 | marginTop:9, 101 | marginBottom: 25, 102 | paddingLeft: 20, 103 | paddingRight: 20, 104 | 105 | }, 106 | helpText:{ 107 | color: '#7a7a7a' 108 | } 109 | }); 110 | -------------------------------------------------------------------------------- /src/fields/Separator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | let { View, StyleSheet, Text} = require('react-native'); 5 | 6 | export class Separator extends React.Component{ 7 | render(){ 8 | return( 9 | { 10 | (this.props.label)? 11 | {this.props.label.toUpperCase()} 12 | : null 13 | } 14 | 15 | ) 16 | } 17 | } 18 | 19 | Separator.propTypes = { 20 | labelStyle: Text.propTypes.style, 21 | containerStyle: View.propTypes.style 22 | } 23 | 24 | 25 | let formStyles = StyleSheet.create({ 26 | form:{ 27 | 28 | }, 29 | alignRight:{ 30 | marginTop: 7, position:'absolute', right: 10 31 | }, 32 | separatorContainer:{ 33 | // borderTopColor: '#C8C7CC', 34 | // borderTopWidth: 1, 35 | paddingTop: 35, 36 | borderBottomColor: '#C8C7CC', 37 | borderBottomWidth: 1, 38 | 39 | }, 40 | separator:{ 41 | 42 | paddingLeft: 10, 43 | paddingRight: 10, 44 | color: '#6D6D72', 45 | paddingBottom: 7 46 | 47 | }, 48 | fieldContainer:{ 49 | borderBottomWidth: 1, 50 | borderBottomColor: '#C8C7CC', 51 | backgroundColor: 'white', 52 | justifyContent: 'center', 53 | height: 45 54 | }, 55 | }); 56 | -------------------------------------------------------------------------------- /src/fields/SwitchField.android.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | let { View, StyleSheet, Text, Switch} = require('react-native'); 5 | 6 | import {SwitchComponent} from '../lib/SwitchComponent'; 7 | 8 | export class SwitchField extends React.Component{ 9 | setValue(value){ 10 | this.refs.fieldComponent.setValue(value) 11 | } 12 | render(){ 13 | 14 | return( 32 | 33 | ) 34 | } 35 | 36 | } 37 | 38 | 39 | 40 | let formStyles = StyleSheet.create({ 41 | fieldContainer:{ 42 | borderBottomWidth: 1, 43 | borderBottomColor: '#C8C7CC', 44 | backgroundColor: 'white', 45 | justifyContent: 'center', 46 | height: 45 47 | }, 48 | horizontalContainer:{ 49 | flexDirection: 'row', 50 | 51 | justifyContent: 'flex-start' 52 | }, 53 | 54 | }); 55 | -------------------------------------------------------------------------------- /src/fields/SwitchField.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | let { View, StyleSheet, Text, Switch} = require('react-native'); 5 | 6 | import {SwitchComponent} from '../lib/SwitchComponent'; 7 | 8 | export class SwitchField extends React.Component{ 9 | setValue(value){ 10 | this.refs.fieldComponent.setValue(value) 11 | } 12 | render(){ 13 | 14 | return( 32 | 33 | ) 34 | } 35 | 36 | } 37 | 38 | 39 | 40 | let formStyles = StyleSheet.create({ 41 | fieldContainer:{ 42 | borderBottomWidth: 1, 43 | borderBottomColor: '#C8C7CC', 44 | backgroundColor: 'white', 45 | justifyContent: 'center', 46 | height: 45 47 | }, 48 | horizontalContainer:{ 49 | flexDirection: 'row', 50 | 51 | justifyContent: 'flex-start' 52 | }, 53 | fieldText:{ 54 | fontSize: 34/2, 55 | paddingLeft: 10, 56 | paddingRight: 10, 57 | justifyContent: 'center', 58 | lineHeight: 32 59 | }, 60 | 61 | }); 62 | -------------------------------------------------------------------------------- /src/fields/TimePickerField.android.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import ReactNative from 'react-native'; 5 | let { View, StyleSheet, TextInput, Text} = ReactNative; 6 | 7 | 8 | import {TimePickerComponent} from '../lib/TimePickerComponent'; 9 | 10 | export class TimePickerField extends React.Component{ 11 | setTime(date){ 12 | this.refs.datePickerComponent.setTime(date); 13 | } 14 | render(){ 15 | /* 16 | 17 | */ 18 | return() 31 | } 32 | 33 | } 34 | 35 | 36 | 37 | let formStyles = StyleSheet.create({ 38 | form:{ 39 | 40 | }, 41 | alignRight:{ 42 | marginTop: 7, position:'absolute', right: 10 43 | }, 44 | noBorder:{ 45 | borderTopWidth: 0, 46 | borderBottomWidth: 0 47 | }, 48 | separatorContainer:{ 49 | // borderTopColor: '#C8C7CC', 50 | // borderTopWidth: 1, 51 | paddingTop: 35, 52 | borderBottomColor: '#C8C7CC', 53 | borderBottomWidth: 1, 54 | 55 | }, 56 | separator:{ 57 | 58 | paddingLeft: 10, 59 | paddingRight: 10, 60 | color: '#6D6D72', 61 | paddingBottom: 7 62 | 63 | }, 64 | fieldsWrapper:{ 65 | // borderTopColor: '#afafaf', 66 | // borderTopWidth: 1, 67 | }, 68 | horizontalContainer:{ 69 | flexDirection: 'row', 70 | 71 | justifyContent: 'flex-start' 72 | }, 73 | fieldContainer:{ 74 | borderBottomWidth: 1, 75 | borderBottomColor: '#C8C7CC', 76 | backgroundColor: 'white', 77 | justifyContent: 'center', 78 | height: 45 79 | }, 80 | fieldValue:{ 81 | fontSize: 34/2, 82 | paddingLeft: 10, 83 | paddingRight: 10, 84 | marginRight:10, 85 | paddingTop: 4, 86 | justifyContent: 'center', 87 | 88 | color: '#C7C7CC' 89 | }, 90 | fieldText:{ 91 | fontSize: 34/2, 92 | paddingLeft: 10, 93 | paddingRight: 10, 94 | justifyContent: 'center', 95 | lineHeight: 32 96 | }, 97 | input:{ 98 | paddingLeft: 10, 99 | paddingRight: 10, 100 | 101 | }, 102 | helpTextContainer:{ 103 | marginTop:9, 104 | marginBottom: 25, 105 | paddingLeft: 20, 106 | paddingRight: 20, 107 | 108 | }, 109 | helpText:{ 110 | color: '#7a7a7a' 111 | } 112 | }); 113 | -------------------------------------------------------------------------------- /src/fields/TimePickerField.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import ReactNative from 'react-native'; 5 | let { View, StyleSheet, TextInput, Text, PickerIOS} = ReactNative; 6 | 7 | 8 | import {DatePickerComponent} from '../lib/DatePickerComponent'; 9 | 10 | export class TimePickerField extends React.Component{ 11 | setTime(date){ 12 | this.refs.datePickerComponent.setDate(date); 13 | } 14 | render(){ 15 | /* 16 | 17 | */ 18 | return() 31 | } 32 | 33 | } 34 | 35 | 36 | 37 | let formStyles = StyleSheet.create({ 38 | form:{ 39 | 40 | }, 41 | alignRight:{ 42 | marginTop: 7, position:'absolute', right: 10 43 | }, 44 | noBorder:{ 45 | borderTopWidth: 0, 46 | borderBottomWidth: 0 47 | }, 48 | separatorContainer:{ 49 | // borderTopColor: '#C8C7CC', 50 | // borderTopWidth: 1, 51 | paddingTop: 35, 52 | borderBottomColor: '#C8C7CC', 53 | borderBottomWidth: 1, 54 | 55 | }, 56 | separator:{ 57 | 58 | paddingLeft: 10, 59 | paddingRight: 10, 60 | color: '#6D6D72', 61 | paddingBottom: 7 62 | 63 | }, 64 | fieldsWrapper:{ 65 | // borderTopColor: '#afafaf', 66 | // borderTopWidth: 1, 67 | }, 68 | horizontalContainer:{ 69 | flexDirection: 'row', 70 | 71 | justifyContent: 'flex-start' 72 | }, 73 | fieldContainer:{ 74 | borderBottomWidth: 1, 75 | borderBottomColor: '#C8C7CC', 76 | backgroundColor: 'white', 77 | justifyContent: 'center', 78 | height: 45 79 | }, 80 | fieldValue:{ 81 | fontSize: 34/2, 82 | paddingLeft: 10, 83 | paddingRight: 10, 84 | marginRight:10, 85 | paddingTop: 4, 86 | justifyContent: 'center', 87 | 88 | color: '#C7C7CC' 89 | }, 90 | fieldText:{ 91 | fontSize: 34/2, 92 | paddingLeft: 10, 93 | paddingRight: 10, 94 | justifyContent: 'center', 95 | lineHeight: 32 96 | }, 97 | input:{ 98 | paddingLeft: 10, 99 | paddingRight: 10, 100 | 101 | }, 102 | helpTextContainer:{ 103 | marginTop:9, 104 | marginBottom: 25, 105 | paddingLeft: 20, 106 | paddingRight: 20, 107 | 108 | }, 109 | helpText:{ 110 | color: '#7a7a7a' 111 | } 112 | }); 113 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import {InputField} from './src/fields/InputField'; 4 | import {SwitchField} from './src/fields/SwitchField'; 5 | import {Separator} from './src/fields/Separator'; 6 | import {LinkField} from './src/fields/LinkField'; 7 | 8 | import {DatePickerField} from './src/fields/DatePickerField'; 9 | import {Form} from './src/Form'; 10 | 11 | import {KeyboardAwareScrollView} from './src/KeyboardAwareScrollView'; 12 | //import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view' 13 | export {InputField, SwitchField, 14 | Separator, LinkField, 15 | KeyboardAwareScrollView, 16 | Form 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/DatePickerComponent.android.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import React from 'react'; 5 | import PropTypes from 'prop-types'; 6 | let { View, StyleSheet, TextInput, Text, DatePickerAndroid} = require('react-native'); 7 | import {Field} from './Field'; 8 | 9 | 10 | export class DatePickerComponent extends React.Component{ 11 | constructor(props){ 12 | super(props); 13 | this.state = { 14 | date: props.date? new Date(props.date) :'', 15 | isPickerVisible: false 16 | } 17 | 18 | } 19 | 20 | handleLayoutChange(e){ 21 | let {x, y, width, height} = {... e.nativeEvent.layout}; 22 | 23 | this.setState(e.nativeEvent.layout); 24 | //e.nativeEvent.layout: {x, y, width, height}}}. 25 | } 26 | 27 | handleValueChange(date){ 28 | 29 | this.setState({date:date}); 30 | 31 | if(this.props.onChange) this.props.onChange((this.props.prettyPrint)?this.props.dateTimeFormat(date):date); 32 | if(this.props.onValueChange) this.props.onValueChange(date); 33 | } 34 | setDate(date){ 35 | this.setState({date:date}); 36 | if(this.props.onChange) this.props.onChange((this.props.prettyPrint)?this.props.dateTimeFormat(date):date); 37 | if(this.props.onValueChange) this.props.onValueChange(date); 38 | } 39 | 40 | 41 | // this.refs.picker.measure(this.getPickerLayout.bind(this)); 42 | 43 | 44 | async _togglePicker(event){ 45 | try { 46 | 47 | const {action, year, month, day} = await DatePickerAndroid.open({ 48 | date: this.props.date || new Date(), 49 | minDate:this.props.minimumDate, 50 | 51 | maxDate:this.props.maximumDate 52 | }); 53 | if (action !== DatePickerAndroid.dismissedAction) { 54 | this.handleValueChange(new Date(year,month,day)); 55 | // Selected year, month (0-11), day 56 | } 57 | } catch ({code, message}) { 58 | console.warn('Cannot open time picker', message); 59 | } 60 | this.props.onPress && this.props.onPress(event); 61 | } 62 | 63 | render(){ 64 | let placeholderComponent = (this.props.placeholderComponent) 65 | ? this.props.placeholderComponent 66 | : {this.props.placeholder} 67 | return( 71 | 75 | {(this.props.iconLeft) 76 | ? this.props.iconLeft 77 | : null 78 | } 79 | {placeholderComponent} 80 | 81 | { 82 | (this.state.date)?this.state.date.toLocaleDateString():"" 83 | } 84 | 85 | 86 | 87 | {(this.props.iconRight) 88 | ? this.props.iconRight 89 | : null 90 | } 91 | 92 | 93 | {(this.state.isPickerVisible)? 94 | 100 | 101 | : null 102 | } 103 | 104 | 105 | ) 106 | } 107 | 108 | } 109 | 110 | DatePickerComponent.propTypes = { 111 | dateTimeFormat: PropTypes.func 112 | } 113 | 114 | DatePickerComponent.defaultProps = { 115 | dateTimeFormat: (date)=>{ 116 | if(!date) return ""; 117 | return date.toLocaleDateString() 118 | } 119 | }; 120 | 121 | let formStyles = StyleSheet.create({ 122 | form:{ 123 | 124 | }, 125 | alignRight:{ 126 | marginTop: 7, position:'absolute', right: 10 127 | }, 128 | noBorder:{ 129 | borderTopWidth: 0, 130 | borderBottomWidth: 0 131 | }, 132 | separatorContainer:{ 133 | // borderTopColor: '#C8C7CC', 134 | // borderTopWidth: 1, 135 | paddingTop: 35, 136 | borderBottomColor: '#C8C7CC', 137 | borderBottomWidth: 1, 138 | 139 | }, 140 | separator:{ 141 | 142 | paddingLeft: 10, 143 | paddingRight: 10, 144 | color: '#6D6D72', 145 | paddingBottom: 7 146 | 147 | }, 148 | fieldsWrapper:{ 149 | // borderTopColor: '#afafaf', 150 | // borderTopWidth: 1, 151 | }, 152 | horizontalContainer:{ 153 | flexDirection: 'row', 154 | 155 | justifyContent: 'flex-start' 156 | }, 157 | fieldContainer:{ 158 | borderBottomWidth: 1, 159 | borderBottomColor: '#C8C7CC', 160 | backgroundColor: 'white', 161 | justifyContent: 'center', 162 | height: 45 163 | }, 164 | fieldValue:{ 165 | fontSize: 34/2, 166 | paddingLeft: 10, 167 | paddingRight: 10, 168 | marginRight:10, 169 | paddingTop: 4, 170 | justifyContent: 'center', 171 | 172 | color: '#C7C7CC' 173 | }, 174 | fieldText:{ 175 | fontSize: 34/2, 176 | paddingLeft: 10, 177 | paddingRight: 10, 178 | justifyContent: 'center', 179 | lineHeight: 32 180 | }, 181 | input:{ 182 | paddingLeft: 10, 183 | paddingRight: 10, 184 | 185 | }, 186 | helpTextContainer:{ 187 | marginTop:9, 188 | marginBottom: 25, 189 | paddingLeft: 20, 190 | paddingRight: 20, 191 | 192 | }, 193 | helpText:{ 194 | color: '#7a7a7a' 195 | } 196 | }); 197 | -------------------------------------------------------------------------------- /src/lib/DatePickerComponent.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import React from 'react'; 5 | import PropTypes from 'prop-types'; 6 | let { View, StyleSheet, TextInput, Text, DatePickerIOS} = require('react-native'); 7 | import {Field} from './Field'; 8 | 9 | 10 | export class DatePickerComponent extends React.Component{ 11 | constructor(props){ 12 | super(props); 13 | this.state = { 14 | date: props.date? new Date(props.date) : '', 15 | isPickerVisible: false 16 | } 17 | 18 | } 19 | setDate(date){ 20 | this.setState({date:date}); 21 | if(this.props.onChange) this.props.onChange((this.props.prettyPrint)?this.props.dateTimeFormat(date):date); 22 | if(this.props.onValueChange) this.props.onValueChange(date); 23 | } 24 | handleLayoutChange(e){ 25 | let {x, y, width, height} = {... e.nativeEvent.layout}; 26 | 27 | this.setState(e.nativeEvent.layout); 28 | //e.nativeEvent.layout: {x, y, width, height}}}. 29 | } 30 | 31 | handleValueChange(date){ 32 | 33 | this.setState({date:date}); 34 | 35 | this.props.onChange && this.props.onChange((this.props.prettyPrint)?this.props.dateTimeFormat(date, this.props.mode):date); 36 | this.props.onValueChange && this.props.onValueChange(date); 37 | 38 | } 39 | 40 | 41 | 42 | // this.refs.picker.measure(this.getPickerLayout.bind(this)); 43 | 44 | 45 | _togglePicker(event){ 46 | this.setState({isPickerVisible:!this.state.isPickerVisible}); 47 | //this._scrollToInput(event); 48 | this.props.onPress && this.props.onPress(event); 49 | } 50 | 51 | render(){ 52 | let { maximumDate, minimumDate, 53 | minuteInterval, mode, 54 | onDateChange, timeZoneOffsetInMinutes } = this.props; 55 | 56 | let valueString = this.props.dateTimeFormat(this.state.date, this.props.mode); 57 | 58 | let datePicker= 67 | 68 | let pickerWrapper = React.cloneElement(this.props.pickerWrapper,{onHidePicker:()=>{this.setState({isPickerVisible:false})}},datePicker); 69 | 70 | let iconLeft = this.props.iconLeft, 71 | iconRight = this.props.iconRight; 72 | 73 | if(iconLeft && iconLeft.constructor === Array){ 74 | iconLeft = (!this.state.isPickerVisible) 75 | ? iconLeft[0] 76 | : iconLeft[1] 77 | } 78 | if(iconRight && iconRight.constructor === Array){ 79 | iconRight = (!this.state.isPickerVisible) 80 | ? iconRight[0] 81 | : iconRight[1] 82 | } 83 | let placeholderComponent = (this.props.placeholderComponent) 84 | ? this.props.placeholderComponent 85 | : {this.props.placeholder} 86 | return( 90 | 94 | {(iconLeft) 95 | ? iconLeft 96 | : null 97 | } 98 | {placeholderComponent} 99 | 100 | { valueString } 101 | 102 | {(iconRight) 103 | ? iconRight 104 | : null 105 | } 106 | 107 | 108 | 109 | 110 | {(this.state.isPickerVisible)? 111 | pickerWrapper : null 112 | } 113 | 114 | 115 | ) 116 | } 117 | 118 | } 119 | 120 | DatePickerComponent.propTypes = { 121 | dateTimeFormat: PropTypes.func, 122 | pickerWrapper: PropTypes.element, 123 | prettyPrint: PropTypes.bool 124 | } 125 | 126 | DatePickerComponent.defaultProps = { 127 | pickerWrapper: , 128 | dateTimeFormat: (date, mode)=>{ 129 | if(!date) return ""; 130 | let value=''; 131 | switch(mode){ 132 | case 'datetime': 133 | value = date.toLocaleDateString() 134 | + ' ' 135 | + date.toLocaleTimeString() 136 | break; 137 | case 'time': 138 | value = date.toLocaleTimeString() 139 | break; 140 | case 'countdown': 141 | value = date.getHours() + ":" + date.getMinutes(); 142 | break; 143 | default: 144 | value = date.toLocaleDateString() 145 | } 146 | return value; 147 | } 148 | }; 149 | 150 | let formStyles = StyleSheet.create({ 151 | form:{ 152 | 153 | }, 154 | alignRight:{ 155 | marginTop: 7, position:'absolute', right: 10 156 | }, 157 | noBorder:{ 158 | borderTopWidth: 0, 159 | borderBottomWidth: 0 160 | }, 161 | separatorContainer:{ 162 | // borderTopColor: '#C8C7CC', 163 | // borderTopWidth: 1, 164 | paddingTop: 35, 165 | borderBottomColor: '#C8C7CC', 166 | borderBottomWidth: 1, 167 | 168 | }, 169 | separator:{ 170 | 171 | paddingLeft: 10, 172 | paddingRight: 10, 173 | color: '#6D6D72', 174 | paddingBottom: 7 175 | 176 | }, 177 | fieldsWrapper:{ 178 | // borderTopColor: '#afafaf', 179 | // borderTopWidth: 1, 180 | }, 181 | horizontalContainer:{ 182 | flexDirection: 'row', 183 | justifyContent: 'flex-start' 184 | }, 185 | fieldContainer:{ 186 | borderBottomWidth: 1, 187 | borderBottomColor: '#C8C7CC', 188 | backgroundColor: 'white', 189 | justifyContent: 'center', 190 | height: 45 191 | }, 192 | fieldValue:{ 193 | fontSize: 34/2, 194 | paddingLeft: 10, 195 | paddingRight: 10, 196 | marginRight:10, 197 | paddingTop: 4, 198 | justifyContent: 'center', 199 | 200 | color: '#C7C7CC' 201 | }, 202 | fieldText:{ 203 | fontSize: 34/2, 204 | paddingLeft: 10, 205 | paddingRight: 10, 206 | justifyContent: 'center', 207 | lineHeight: 32 208 | }, 209 | input:{ 210 | paddingLeft: 10, 211 | paddingRight: 10, 212 | 213 | }, 214 | helpTextContainer:{ 215 | marginTop:9, 216 | marginBottom: 25, 217 | paddingLeft: 20, 218 | paddingRight: 20, 219 | 220 | }, 221 | helpText:{ 222 | color: '#7a7a7a' 223 | } 224 | }); 225 | -------------------------------------------------------------------------------- /src/lib/Field.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import PropTypes from 'prop-types'; 5 | import {HelpText} from './HelpText'; 6 | let { View, StyleSheet, Text, TouchableHighlight} = require('react-native'); 7 | 8 | export class Field extends React.Component{ 9 | render(){ 10 | let fieldHelpText = 11 | this.props.helpTextComponent 12 | || ((this.props.helpText) 13 | ? 14 | : null); 15 | 16 | if(this.props.onPress){ 17 | return 18 | 19 | {this.props.children} 20 | {fieldHelpText} 21 | 22 | 23 | } 24 | return 25 | {this.props.children} 26 | {fieldHelpText} 27 | ; 28 | 29 | 30 | } 31 | } 32 | Field.propTypes = { 33 | helpTextComponent: PropTypes.element, 34 | helpText: PropTypes.string 35 | } 36 | 37 | 38 | let formStyles = StyleSheet.create({ 39 | 40 | helpTextContainer:{ 41 | marginTop:9, 42 | marginBottom: 25, 43 | paddingLeft: 20, 44 | paddingRight: 20, 45 | 46 | }, 47 | helpText:{ 48 | color: '#7a7a7a' 49 | } 50 | }); 51 | -------------------------------------------------------------------------------- /src/lib/HelpText.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 'use strict'; 4 | 5 | import React from 'react'; 6 | import PropTypes from 'prop-types'; 7 | 8 | import { View, StyleSheet, Text} from 'react-native'; 9 | 10 | export class HelpText extends React.Component{ 11 | render(){ 12 | if(!this.props.text) return null; 13 | return ( 14 | 15 | {this.props.text} 16 | ); 17 | } 18 | } 19 | 20 | HelpText.propTypes = { 21 | text: PropTypes.string 22 | } 23 | 24 | 25 | let formStyles = StyleSheet.create({ 26 | 27 | helpTextContainer:{ 28 | marginTop:9, 29 | marginBottom: 25, 30 | paddingLeft: 20, 31 | paddingRight: 20, 32 | 33 | }, 34 | helpText:{ 35 | color: '#7a7a7a' 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /src/lib/InputComponent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import PropTypes from 'prop-types'; 5 | import ReactNative, { Platform } from 'react-native'; 6 | import {Field} from './Field.js'; 7 | 8 | const {View, StyleSheet, TextInput, Text} = ReactNative; 9 | 10 | function validateEmail(email) { 11 | var re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 12 | if(re.test(email)) return true; 13 | return 'Invalid email'; 14 | } 15 | 16 | export class InputComponent extends React.Component{ 17 | constructor(props){ 18 | super(props); 19 | 20 | this.triggerValidation = this.triggerValidation.bind(this); 21 | // this.validate = this.validate.bind(this) 22 | this.validate(props.value); 23 | this.validationErrors = []; 24 | this.state = { 25 | labelWidth: 0, 26 | value: props.value, 27 | minFieldHeight: props.height || 44, 28 | inputHeight: Math.max(props.height || 44), 29 | // isValid: 30 | }; 31 | this.setValue = this.setValue.bind(this) 32 | this.focus = this.focus.bind(this) 33 | this.triggerValidation = this.triggerValidation.bind(this) 34 | this.validate = this.validate.bind(this) 35 | this.handleLayoutChange = this.handleLayoutChange.bind(this) 36 | this.handleLabelLayoutChange = this.handleLabelLayoutChange.bind(this) 37 | this.handleChange = this.handleChange.bind(this) 38 | this.handleFieldPress = this.handleFieldPress.bind(this) 39 | this._scrollToInput = this._scrollToInput.bind(this) 40 | } 41 | 42 | setValue(value){ 43 | this.setState({value:value}); 44 | if(this.props.onChange) this.props.onChange(value); 45 | if(this.props.onValueChange) this.props.onValueChange(value); 46 | } 47 | focus(){ 48 | this.refs.inputBox.focus() 49 | } 50 | triggerValidation() { 51 | this.setState({isValid:this.validate(this.state.value)}); 52 | } 53 | validate(value){ 54 | let validationResult; 55 | this.validationErrors = []; 56 | 57 | if(!!this.props.validationFunction) { 58 | if(this.props.validationFunction.constructor === Array){ 59 | /* 60 | validationFunction has to return an object in case of error, 61 | true in case of successful validation 62 | */ 63 | this.props.validationFunction.map((valFn, i)=>{ 64 | 65 | let validationResult = valFn(value, this); 66 | if(validationResult === true){ 67 | this.valid = (this.valid !== false)? validationResult : this.valid; 68 | } else{ 69 | this.validationErrors.push(validationResult); 70 | this.valid = false; 71 | } 72 | 73 | }) 74 | } else { 75 | let validationResult = this.props.validationFunction(value, this); 76 | if(validationResult === true){ 77 | this.valid = true; 78 | } else{ 79 | this.validationErrors.push(validationResult); 80 | this.valid = false; 81 | } 82 | } 83 | 84 | } else 85 | if(this.props.keyboardType){ 86 | switch (this.props.keyboardType) { 87 | case 'email-address': 88 | validationResult = validateEmail(value); 89 | break; 90 | } 91 | if(validationResult === true){ 92 | this.valid = true; 93 | } else{ 94 | this.validationErrors.push(validationResult); 95 | this.valid = false; 96 | } 97 | } 98 | this.props.onValidation(this.valid, this.validationErrors); 99 | return this.valid; 100 | } 101 | handleLayoutChange(e){ 102 | if (Platform.OS === 'ios') { 103 | let {x, y, width, height} = {... e.nativeEvent.layout}; 104 | 105 | this.setState(e.nativeEvent.layout); 106 | } 107 | // //e.nativeEvent.layout: {x, y, width, height}}}. 108 | } 109 | 110 | handleLabelLayoutChange(e){ 111 | if (Platform.OS === 'ios') { 112 | let {x, y, width, height} = {... e.nativeEvent.layout}; 113 | 114 | this.setState({labelWidth:width}); 115 | } 116 | // //e.nativeEvent.layout: {x, y, width, height}}}. 117 | } 118 | handleChange(event){ 119 | const value = event.nativeEvent.text; 120 | 121 | this.validate(value); 122 | 123 | this.setState({value, 124 | inputHeight: Math.max(this.state.minFieldHeight, 125 | (event.nativeEvent.contentSize && this.props.multiline) 126 | ? event.nativeEvent.contentSize.height 127 | : 0) 128 | }); 129 | //this.props.onChange(this.props.fieldRef, value); 130 | if(this.props.onChange) this.props.onChange(value, this.valid); 131 | if(this.props.onValueChange) this.props.onValueChange(value,this.valid); 132 | } 133 | 134 | _scrollToInput (event) { 135 | //debugger; 136 | if (this.props.onFocus) { 137 | let handle = ReactNative.findNodeHandle(this.refs.inputBox); 138 | this.props.onFocus( 139 | event, 140 | handle 141 | ) 142 | } 143 | } 144 | handleFieldPress(event){ 145 | this.refs.inputBox.focus(); 146 | } 147 | render(){ 148 | // style={[formStyles.fieldContainer, 149 | // formStyles.horizontalContainer, 150 | // this.props.containerStyle, 151 | // {height: this.state.inputHeight+1} 152 | // ]} 153 | return( 154 | 160 | {(this.props.iconLeft) 161 | ? this.props.iconLeft 162 | : null 163 | } 164 | {(this.props.label) 165 | ? 166 | {this.props.label} 171 | : null 172 | } 173 | 192 | {(this.props.iconRight) 193 | ? this.props.iconRight 194 | : null 195 | } 196 | 197 | 198 | ) 199 | } 200 | 201 | } 202 | 203 | // InputComponent.propTypes = { 204 | // multiline: PropTypes.bool, 205 | // placeholder:PropTypes.string, 206 | // } 207 | 208 | InputComponent.propTypes = { 209 | labelStyle: Text.propTypes.style, 210 | inputStyle: TextInput.propTypes.style, 211 | containerStyle: View.propTypes.style 212 | } 213 | -------------------------------------------------------------------------------- /src/lib/LinkComponent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | let { View, StyleSheet, Text} = require('react-native'); 5 | import {Field} from './Field'; 6 | 7 | 8 | export class LinkComponent extends React.Component{ 9 | constructor(props){ 10 | super(props); 11 | this.state = { 12 | } 13 | } 14 | handleLayoutChange(e){ 15 | let {x, y, width, height} = {... e.nativeEvent.layout}; 16 | 17 | this.setState(e.nativeEvent.layout); 18 | //e.nativeEvent.layout: {x, y, width, height}}}. 19 | } 20 | 21 | 22 | render(){ 23 | return( 24 | 27 | 28 | {(this.props.iconLeft) 29 | ? this.props.iconLeft 30 | : null 31 | } 32 | 34 | {this.props.label} 35 | 36 | 37 | {(this.props.iconRight) 38 | ? this.props.iconRight 39 | : null 40 | } 41 | 42 | 43 | 44 | ) 45 | } 46 | 47 | } 48 | 49 | LinkComponent.propTypes = { 50 | labelStyle: Text.propTypes.style, 51 | containerStyle: View.propTypes.style 52 | } 53 | -------------------------------------------------------------------------------- /src/lib/PickerComponent.android.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import ReactNative from 'react-native'; 5 | let { View, StyleSheet, TextInput, Text, Picker} = ReactNative; 6 | import {Field} from '../lib/Field'; 7 | 8 | var PickerItem = Picker.Item; 9 | 10 | export class PickerComponent extends React.Component{ 11 | constructor(props){ 12 | super(props); 13 | this.state = { 14 | value: props.value || props.label, 15 | } 16 | this.pickerMeasures = {}; 17 | } 18 | setValue(value){ 19 | this.setState({value:value}); 20 | if(this.props.onChange) this.props.onChange(value); 21 | if(this.props.onValueChange) this.props.onValueChange(value); 22 | } 23 | handleLayoutChange(e){ 24 | let {x, y, width, height} = {... e.nativeEvent.layout}; 25 | 26 | this.setState(e.nativeEvent.layout); 27 | //e.nativeEvent.layout: {x, y, width, height}}}. 28 | } 29 | 30 | handleValueChange(value){ 31 | 32 | this.setState({value:(value && value!='')?value:this.props.label}); 33 | 34 | if(this.props.onChange) this.props.onChange(value); 35 | if(this.props.onValueChange) this.props.onValueChange(value); 36 | } 37 | 38 | _scrollToInput (event) { 39 | 40 | if (this.props.onFocus) { 41 | let handle = ReactNative.findNodeHandle(this.refs.inputBox); 42 | 43 | this.props.onFocus( 44 | event, 45 | handle 46 | ) 47 | } 48 | 49 | // this.refs.picker.measure(this.getPickerLayout.bind(this)); 50 | 51 | } 52 | _togglePicker(event){ 53 | //this.setState({isPickerVisible:!this.state.isPickerVisible}); 54 | //this._scrollToInput(event); 55 | } 56 | render(){ 57 | 58 | return( 63 | 66 | 67 | {this.props.label} 68 | 73 | {Object.keys(this.props.options).map((value) => ( 74 | 79 | ), this)} 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | ) 90 | } 91 | 92 | } 93 | 94 | 95 | 96 | let formStyles = StyleSheet.create({ 97 | form:{ 98 | 99 | }, 100 | alignRight:{ 101 | marginTop: 7, position:'absolute', right: 10 102 | }, 103 | noBorder:{ 104 | borderTopWidth: 0, 105 | borderBottomWidth: 0 106 | }, 107 | separatorContainer:{ 108 | // borderTopColor: '#C8C7CC', 109 | // borderTopWidth: 1, 110 | paddingTop: 35, 111 | borderBottomColor: '#C8C7CC', 112 | borderBottomWidth: 1, 113 | 114 | }, 115 | separator:{ 116 | 117 | paddingLeft: 10, 118 | paddingRight: 10, 119 | color: '#6D6D72', 120 | paddingBottom: 7 121 | 122 | }, 123 | fieldsWrapper:{ 124 | // borderTopColor: '#afafaf', 125 | // borderTopWidth: 1, 126 | }, 127 | horizontalContainer:{ 128 | flexDirection: 'row', 129 | 130 | justifyContent: 'flex-start' 131 | }, 132 | fieldContainer:{ 133 | borderBottomWidth: 1, 134 | borderBottomColor: '#C8C7CC', 135 | backgroundColor: 'white', 136 | justifyContent: 'center', 137 | height: 45 138 | }, 139 | fieldValue:{ 140 | fontSize: 34/2, 141 | paddingLeft: 10, 142 | paddingRight: 10, 143 | marginRight:10, 144 | paddingTop: 4, 145 | justifyContent: 'center', 146 | 147 | color: '#C7C7CC' 148 | }, 149 | fieldText:{ 150 | fontSize: 34/2, 151 | paddingLeft: 10, 152 | paddingRight: 10, 153 | justifyContent: 'center', 154 | lineHeight: 32 155 | }, 156 | input:{ 157 | paddingLeft: 10, 158 | paddingRight: 10, 159 | 160 | }, 161 | helpTextContainer:{ 162 | marginTop:9, 163 | marginBottom: 25, 164 | paddingLeft: 20, 165 | paddingRight: 20, 166 | 167 | }, 168 | helpText:{ 169 | color: '#7a7a7a' 170 | } 171 | }); 172 | -------------------------------------------------------------------------------- /src/lib/PickerComponent.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import PropTypes from 'prop-types'; 5 | import ReactNative from 'react-native'; 6 | let { View, StyleSheet, TextInput, Text, Picker} = ReactNative; 7 | import {Field} from '../lib/Field'; 8 | 9 | var PickerItem = Picker.Item; 10 | 11 | export class PickerComponent extends React.Component{ 12 | constructor(props){ 13 | super(props); 14 | this.state = { 15 | value: props.value, 16 | isPickerVisible: false 17 | } 18 | this.pickerMeasures = {}; 19 | } 20 | setValue(value){ 21 | this.setState({value:value}); 22 | if(this.props.onChange) this.props.onChange(value); 23 | if(this.props.onValueChange) this.props.onValueChange(value); 24 | } 25 | handleLayoutChange(e){ 26 | let {x, y, width, height} = {... e.nativeEvent.layout}; 27 | 28 | this.setState(e.nativeEvent.layout); 29 | //e.nativeEvent.layout: {x, y, width, height}}}. 30 | } 31 | 32 | handleValueChange(value){ 33 | 34 | this.setState({value:value}); 35 | 36 | if(this.props.onChange) this.props.onChange(value); 37 | if(this.props.onValueChange) this.props.onValueChange(value); 38 | if(this.props.autoclose) this._togglePicker(); 39 | } 40 | 41 | _scrollToInput (event) { 42 | 43 | if (this.props.onFocus) { 44 | let handle = ReactNative.findNodeHandle(this.refs.inputBox); 45 | 46 | this.props.onFocus( 47 | event, 48 | handle 49 | ) 50 | } 51 | 52 | // this.refs.picker.measure(this.getPickerLayout.bind(this)); 53 | 54 | } 55 | _togglePicker(event){ 56 | this.setState({isPickerVisible:!this.state.isPickerVisible}); 57 | this.props.onPress && this.props.onPress(event); 58 | //this._scrollToInput(event); 59 | } 60 | render(){ 61 | // 62 | // if (this.state.isMultipleSelect){ 63 | // let iconName = 'ios-circle-outline'; 64 | // let iconColor = {}; 65 | // if (this.state.multipleSelectValue[name]) { 66 | // iconName = 'ios-checkmark-outline'; 67 | // iconColor = {color:'red'}; 68 | // } 69 | // return ( 70 | // {this.checkStateChange(name)}}> 72 | // 73 | // 74 | // ); 75 | // }else { 76 | // return ( 77 | // 78 | // ); 79 | // } 80 | 81 | // this.setState({falseSwitchIsOn: value})} 83 | // 84 | // value={this.state.falseSwitchIsOn} /> 85 | 86 | // this.props.options.map((option, i) => { 87 | // pickerOptions.push(); 92 | // }); 93 | let picker = 99 | {Object.keys(this.props.options).map((value) => ( 100 | 105 | ), this)} 106 | 107 | ; 108 | let pickerWrapper = React.cloneElement(this.props.pickerWrapper,{ onHidePicker:()=>{this.setState({isPickerVisible:false})}}, picker); 109 | let iconLeft = this.props.iconLeft, 110 | iconRight = this.props.iconRight; 111 | 112 | if(iconLeft && iconLeft.constructor === Array){ 113 | iconLeft = (!this.state.isPickerVisible) 114 | ? iconLeft[0] 115 | : iconLeft[1] 116 | } 117 | if(iconRight && iconRight.constructor === Array){ 118 | iconRight = (!this.state.isPickerVisible) 119 | ? iconRight[0] 120 | : iconRight[1] 121 | } 122 | return( 126 | 129 | {(iconLeft) 130 | ? iconLeft 131 | : null 132 | } 133 | {this.props.label} 134 | 135 | 136 | {(this.state.value)?this.props.options[this.state.value]:''} 137 | 138 | 139 | 140 | {(this.props.iconRight) 141 | ? this.props.iconRight 142 | : null 143 | } 144 | 145 | 146 | 147 | {(this.state.isPickerVisible)? 148 | pickerWrapper : null 149 | } 150 | 151 | 152 | ) 153 | } 154 | 155 | } 156 | 157 | PickerComponent.propTypes = { 158 | pickerWrapper: PropTypes.element, 159 | } 160 | 161 | PickerComponent.defaultProps = { 162 | pickerWrapper: 163 | } 164 | 165 | let formStyles = StyleSheet.create({ 166 | form:{ 167 | 168 | }, 169 | alignRight:{ 170 | marginTop: 7, position:'absolute', right: 10 171 | }, 172 | noBorder:{ 173 | borderTopWidth: 0, 174 | borderBottomWidth: 0 175 | }, 176 | separatorContainer:{ 177 | // borderTopColor: '#C8C7CC', 178 | // borderTopWidth: 1, 179 | paddingTop: 35, 180 | borderBottomColor: '#C8C7CC', 181 | borderBottomWidth: 1, 182 | 183 | }, 184 | separator:{ 185 | 186 | paddingLeft: 10, 187 | paddingRight: 10, 188 | color: '#6D6D72', 189 | paddingBottom: 7 190 | 191 | }, 192 | fieldsWrapper:{ 193 | // borderTopColor: '#afafaf', 194 | // borderTopWidth: 1, 195 | }, 196 | horizontalContainer:{ 197 | flexDirection: 'row', 198 | 199 | justifyContent: 'flex-start' 200 | }, 201 | fieldContainer:{ 202 | borderBottomWidth: 1, 203 | borderBottomColor: '#C8C7CC', 204 | backgroundColor: 'white', 205 | justifyContent: 'center', 206 | height: 45 207 | }, 208 | fieldValue:{ 209 | fontSize: 34/2, 210 | paddingLeft: 10, 211 | paddingRight: 10, 212 | marginRight:10, 213 | paddingTop: 4, 214 | justifyContent: 'center', 215 | 216 | color: '#C7C7CC' 217 | }, 218 | fieldText:{ 219 | fontSize: 34/2, 220 | paddingLeft: 10, 221 | paddingRight: 10, 222 | justifyContent: 'center', 223 | lineHeight: 32 224 | }, 225 | input:{ 226 | paddingLeft: 10, 227 | paddingRight: 10, 228 | 229 | }, 230 | helpTextContainer:{ 231 | marginTop:9, 232 | marginBottom: 25, 233 | paddingLeft: 20, 234 | paddingRight: 20, 235 | 236 | }, 237 | helpText:{ 238 | color: '#7a7a7a' 239 | } 240 | }); 241 | -------------------------------------------------------------------------------- /src/lib/SwitchComponent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | let { View, StyleSheet, Text, Switch} = require('react-native'); 5 | 6 | import {Field} from './Field'; 7 | 8 | export class SwitchComponent extends React.Component{ 9 | constructor(props){ 10 | super(props); 11 | this.state = { 12 | value: props.value, 13 | } 14 | } 15 | handleLayoutChange(e){ 16 | let {x, y, width, height} = {... e.nativeEvent.layout}; 17 | 18 | this.setState(e.nativeEvent.layout); 19 | //e.nativeEvent.layout: {x, y, width, height}}}. 20 | } 21 | setValue(value){ 22 | this.setState({value:value}); 23 | if(this.props.onChange) this.props.onChange(value); 24 | if(this.props.onValueChange) this.props.onValueChange(value); 25 | } 26 | 27 | handleValueChange(value){ 28 | // debugger; 29 | this.setState({value:value}); 30 | if(this.props.onChange) this.props.onChange(value); 31 | if(this.props.onValueChange) this.props.onValueChange(value); 32 | } 33 | 34 | 35 | render(){ 36 | 37 | return( 38 | 40 | 41 | {this.props.label} 42 | 46 | 47 | 48 | 49 | ) 50 | } 51 | 52 | } 53 | 54 | SwitchComponent.propTypes = { 55 | labelStyle: Text.propTypes.style, 56 | containerStyle: View.propTypes.style, 57 | switchStyle: Switch.propTypes.style 58 | } 59 | 60 | 61 | let formStyles = StyleSheet.create({ 62 | form:{ 63 | 64 | }, 65 | alignRight:{ 66 | marginTop: 7, position:'absolute', right: 10 67 | }, 68 | noBorder:{ 69 | borderTopWidth: 0, 70 | borderBottomWidth: 0 71 | }, 72 | separatorContainer:{ 73 | // borderTopColor: '#C8C7CC', 74 | // borderTopWidth: 1, 75 | paddingTop: 35, 76 | borderBottomColor: '#C8C7CC', 77 | borderBottomWidth: 1, 78 | 79 | }, 80 | separator:{ 81 | 82 | paddingLeft: 10, 83 | paddingRight: 10, 84 | color: '#6D6D72', 85 | paddingBottom: 7 86 | 87 | }, 88 | fieldsWrapper:{ 89 | // borderTopColor: '#afafaf', 90 | // borderTopWidth: 1, 91 | }, 92 | horizontalContainer:{ 93 | flexDirection: 'row', 94 | 95 | justifyContent: 'flex-start' 96 | }, 97 | fieldContainer:{ 98 | borderBottomWidth: 1, 99 | borderBottomColor: '#C8C7CC', 100 | backgroundColor: 'white', 101 | justifyContent: 'center', 102 | height: 45 103 | }, 104 | fieldText:{ 105 | fontSize: 34/2, 106 | paddingLeft: 10, 107 | paddingRight: 10, 108 | justifyContent: 'center', 109 | lineHeight: 32 110 | }, 111 | input:{ 112 | paddingLeft: 10, 113 | paddingRight: 10, 114 | 115 | }, 116 | helpTextContainer:{ 117 | marginTop:9, 118 | marginBottom: 25, 119 | paddingLeft: 20, 120 | paddingRight: 20, 121 | 122 | }, 123 | helpText:{ 124 | color: '#7a7a7a' 125 | } 126 | }); 127 | -------------------------------------------------------------------------------- /src/lib/TimePickerComponent.android.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import React from 'react'; 5 | import PropTypes from 'prop-types'; 6 | let { View, StyleSheet, TextInput, Text, TimePickerAndroid} = require('react-native'); 7 | import {Field} from './Field'; 8 | 9 | 10 | export class TimePickerComponent extends React.Component{ 11 | constructor(props){ 12 | super(props); 13 | this.state = { 14 | date: props.date? new Date(props.date) :'', 15 | isPickerVisible: false 16 | } 17 | 18 | } 19 | 20 | handleLayoutChange(e){ 21 | let {x, y, width, height} = {... e.nativeEvent.layout}; 22 | 23 | this.setState(e.nativeEvent.layout); 24 | //e.nativeEvent.layout: {x, y, width, height}}}. 25 | } 26 | 27 | handleValueChange(date){ 28 | 29 | this.setState({date:date}); 30 | 31 | if(this.props.onChange) this.props.onChange(date); 32 | if(this.props.onValueChange) this.props.onValueChange(date); 33 | } 34 | 35 | setTime(date){ 36 | this.setState({date:date}); 37 | if(this.props.onChange) this.props.onChange((this.props.prettyPrint)?this.props.dateTimeFormat(date):date); 38 | if(this.props.onValueChange) this.props.onValueChange(date); 39 | } 40 | 41 | async _togglePicker(event){ 42 | try { 43 | const {action, hour, minute} = await TimePickerAndroid.open({...this.props.options}); 44 | if (action !== TimePickerAndroid.dismissedAction) { 45 | let date = new Date(0,0,0,hour, minute); 46 | 47 | this.handleValueChange(date); 48 | // Selected year, month (0-11), day 49 | } 50 | } catch ({code, message}) { 51 | console.warn('Cannot open time picker', message); 52 | } 53 | 54 | 55 | } 56 | render(){ 57 | let placeholderComponent = (this.props.placeholderComponent) 58 | ? this.props.placeholderComponent 59 | : {this.props.placeholder} 60 | return( 64 | 68 | 69 | {placeholderComponent} 70 | 71 | { 72 | this.props.dateTimeFormat(this.state.date) 73 | } 74 | 75 | 76 | 77 | {(this.props.iconRight) 78 | ? this.props.iconRight 79 | : null 80 | } 81 | 82 | 83 | {(this.state.isPickerVisible)? 84 | 90 | 91 | : null 92 | } 93 | 94 | 95 | ) 96 | } 97 | 98 | } 99 | TimePickerComponent.propTypes = { 100 | dateTimeFormat: PropTypes.func, 101 | prettyPrint: PropTypes.bool 102 | } 103 | 104 | TimePickerComponent.defaultProps = { 105 | dateTimeFormat: (date)=>{ 106 | if(!date) return ""; 107 | return date.toLocaleTimeString(); 108 | } 109 | }; 110 | 111 | 112 | let formStyles = StyleSheet.create({ 113 | form:{ 114 | 115 | }, 116 | alignRight:{ 117 | marginTop: 7, position:'absolute', right: 10 118 | }, 119 | noBorder:{ 120 | borderTopWidth: 0, 121 | borderBottomWidth: 0 122 | }, 123 | separatorContainer:{ 124 | // borderTopColor: '#C8C7CC', 125 | // borderTopWidth: 1, 126 | paddingTop: 35, 127 | borderBottomColor: '#C8C7CC', 128 | borderBottomWidth: 1, 129 | 130 | }, 131 | separator:{ 132 | 133 | paddingLeft: 10, 134 | paddingRight: 10, 135 | color: '#6D6D72', 136 | paddingBottom: 7 137 | 138 | }, 139 | fieldsWrapper:{ 140 | // borderTopColor: '#afafaf', 141 | // borderTopWidth: 1, 142 | }, 143 | horizontalContainer:{ 144 | flexDirection: 'row', 145 | 146 | justifyContent: 'flex-start' 147 | }, 148 | fieldContainer:{ 149 | borderBottomWidth: 1, 150 | borderBottomColor: '#C8C7CC', 151 | backgroundColor: 'white', 152 | justifyContent: 'center', 153 | height: 45 154 | }, 155 | fieldValue:{ 156 | fontSize: 34/2, 157 | paddingLeft: 10, 158 | paddingRight: 10, 159 | marginRight:10, 160 | paddingTop: 4, 161 | justifyContent: 'center', 162 | 163 | color: '#C7C7CC' 164 | }, 165 | fieldText:{ 166 | fontSize: 34/2, 167 | paddingLeft: 10, 168 | paddingRight: 10, 169 | justifyContent: 'center', 170 | lineHeight: 32 171 | }, 172 | input:{ 173 | paddingLeft: 10, 174 | paddingRight: 10, 175 | 176 | }, 177 | helpTextContainer:{ 178 | marginTop:9, 179 | marginBottom: 25, 180 | paddingLeft: 20, 181 | paddingRight: 20, 182 | 183 | }, 184 | helpText:{ 185 | color: '#7a7a7a' 186 | } 187 | }); 188 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | asap@~2.0.3: 6 | version "2.0.6" 7 | resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" 8 | 9 | core-js@^1.0.0: 10 | version "1.2.7" 11 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" 12 | 13 | encoding@^0.1.11: 14 | version "0.1.12" 15 | resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" 16 | dependencies: 17 | iconv-lite "~0.4.13" 18 | 19 | fbjs@^0.8.9: 20 | version "0.8.14" 21 | resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.14.tgz#d1dbe2be254c35a91e09f31f9cd50a40b2a0ed1c" 22 | dependencies: 23 | core-js "^1.0.0" 24 | isomorphic-fetch "^2.1.1" 25 | loose-envify "^1.0.0" 26 | object-assign "^4.1.0" 27 | promise "^7.1.1" 28 | setimmediate "^1.0.5" 29 | ua-parser-js "^0.7.9" 30 | 31 | iconv-lite@~0.4.13: 32 | version "0.4.18" 33 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2" 34 | 35 | is-stream@^1.0.1: 36 | version "1.1.0" 37 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 38 | 39 | isomorphic-fetch@^2.1.1: 40 | version "2.2.1" 41 | resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" 42 | dependencies: 43 | node-fetch "^1.0.1" 44 | whatwg-fetch ">=0.10.0" 45 | 46 | js-tokens@^3.0.0: 47 | version "3.0.2" 48 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" 49 | 50 | loose-envify@^1.0.0, loose-envify@^1.3.1: 51 | version "1.3.1" 52 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" 53 | dependencies: 54 | js-tokens "^3.0.0" 55 | 56 | node-fetch@^1.0.1: 57 | version "1.7.1" 58 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.1.tgz#899cb3d0a3c92f952c47f1b876f4c8aeabd400d5" 59 | dependencies: 60 | encoding "^0.1.11" 61 | is-stream "^1.0.1" 62 | 63 | object-assign@^4.1.0: 64 | version "4.1.1" 65 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 66 | 67 | promise@^7.1.1: 68 | version "7.3.1" 69 | resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" 70 | dependencies: 71 | asap "~2.0.3" 72 | 73 | prop-types@^15.5.10: 74 | version "15.5.10" 75 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" 76 | dependencies: 77 | fbjs "^0.8.9" 78 | loose-envify "^1.3.1" 79 | 80 | setimmediate@^1.0.5: 81 | version "1.0.5" 82 | resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" 83 | 84 | ua-parser-js@^0.7.9: 85 | version "0.7.14" 86 | resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.14.tgz#110d53fa4c3f326c121292bbeac904d2e03387ca" 87 | 88 | whatwg-fetch@>=0.10.0: 89 | version "2.0.3" 90 | resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" 91 | --------------------------------------------------------------------------------