├── .gitignore
├── elementNames.md
├── fieldComponents
├── AddressField.js
├── CheckboxField.js
├── EmailField.js
├── HiddenField.js
├── HtmlField.js
├── NameField.js
├── NumberField.js
├── PhoneField.js
├── RadioField.js
├── SectionField.js
├── SelectField.js
├── TextField.js
└── index.js
├── gravityforms.zip
├── index.js
├── package-lock.json
├── package.json
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/elementNames.md:
--------------------------------------------------------------------------------
1 | - Global:
2 | - formWrapper
3 | - formHeader
4 | - formTitle
5 | - formFooter
6 | - button
7 | - buttonText
8 | - AddressField:
9 | - fieldSubLabel
10 | - addressFieldSubLabel
11 | - fieldInput
12 | - addressFieldInput
13 | - fieldSubLabel
14 | - addressFieldSubLabel
15 | - fieldWrapper
16 | - addressFieldWrapper
17 | - fieldLabel
18 | - addressFieldLabel
19 | - fieldDescription
20 | - addressFieldDescription
21 | - CheckboxField:
22 | - choiceWrapper
23 | - checkboxChoiceWrapper
24 | - checkboxButtonWrapper
25 | - checkboxButton
26 | - selectedCheckboxButton
27 | - choiceTextWrapper
28 | - checkboxChoiceTextWrapper
29 | - fieldWrapper
30 | - checkboxFieldWrapper
31 | - fieldLabel
32 | - checkboxFieldLabel
33 | - fieldDescription
34 | - checkboxFieldDescription
35 | - EmailField:
36 | - fieldWrapper
37 | - textFieldWrapper
38 | - fieldLabel
39 | - emailFieldLabel
40 | - fieldDescription
41 | - emailFieldDescription
42 | - fieldInput
43 | - textFieldInput
44 | - HiddenField (Just in case you want to un-hide them for any reason):
45 | - fieldWrapper
46 | - hiddenFieldWrapper
47 | - fieldLabel
48 | - hiddenFieldLabel
49 | - fieldDescription
50 | - hiddenFieldDescription
51 | - fieldInput
52 | - hiddenFieldInput
53 | - HiddenField:
54 | - fieldWrapper
55 | - htmlFieldWrapper
56 | - NameField:
57 | - fieldInput
58 | - fieldSelect
59 | - fieldSelectText
60 | - fieldSelectArrow
61 | - fieldSubLabel
62 | - nameFieldSubLabel
63 | - fieldInput
64 | - nameFieldInput
65 | - fieldSubLabel
66 | - nameFieldSubLabel
67 | - fieldWrapper
68 | - nameFieldWrapper
69 | - fieldLabel
70 | - nameFieldLabel
71 | - fieldDescription
72 | - nameFieldDescription
73 | - NumberField:
74 | - fieldWrapper
75 | - numberFieldWrapper
76 | - fieldLabel
77 | - numberFieldLabel
78 | - fieldDescription
79 | - numberFieldDescription
80 | - fieldInput
81 | - numberFieldInput
82 | - PhoneField:
83 | - fieldWrapper
84 | - phoneFieldWrapper
85 | - fieldLabel
86 | - phoneFieldLabel
87 | - fieldDescription
88 | - phoneFieldDescription
89 | - fieldInput
90 | - phoneFieldInput
91 | - RadioField:
92 | - choiceWrapper
93 | - radioChoiceWrapper
94 | - radioButtonWrapper
95 | - radioButton
96 | - selectedRadioButton
97 | - choiceText
98 | - radioChoiceText
99 | - fieldWrapper
100 | - radioFieldWrapper
101 | - fieldLabel
102 | - radioFieldLabel
103 | - fieldDescription
104 | - radioFieldDescription
105 | - SectionField:
106 | - fieldWrapper
107 | - sectionFieldWrapper
108 | - fieldLabel
109 | - sectionFieldLabel
110 | - fieldDescription
111 | - sectionFieldDescription
112 | - headerRow
113 | - sectionHeaderRow
114 | - SelectField:
115 | - fieldWrapper
116 | - selectFieldWrapper
117 | - fieldLabel
118 | - selectFieldLabel
119 | - fieldDescription
120 | - selectFieldDescription
121 | - fieldInput
122 | - fieldSelect
123 | - fieldSelectText
124 | - fieldSelectArrow
125 | - TextField:
126 | - fieldWrapper
127 | - textFieldWrapper
128 | - fieldLabel
129 | - textFieldLabel
130 | - fieldDescription
131 | - textFieldDescription
132 | - fieldInput
133 | - textFieldInput
134 |
--------------------------------------------------------------------------------
/fieldComponents/AddressField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, TextInput, Picker } from 'react-native'
3 |
4 | export default class AddressField extends Component {
5 | constructor(props) {
6 | super(props)
7 | this.data = this.props.data
8 | this.handleChange = this.handleChange.bind(this)
9 | this.style = this.props.style
10 | }
11 |
12 | handleChange(field, text, input) {
13 | this.props.onChange(field, text, input)
14 | }
15 |
16 | render() {
17 | const inputs = this.data.inputs.map(input => {
18 | const items = input.choices && input.choices.map((choice, index) => {
19 | return
20 | })
21 | if (input.isHidden) return
22 | if (input.choices) {
23 | return (
24 |
25 | this.handleChange(this.data.id, value, input.id)}
28 | >
29 | {items}
30 |
31 | {input.label}
32 |
33 | )
34 | }
35 | return (
36 |
37 | this.handleChange(this.data.id, text, input.id)}
40 | placeholder={input.placeholder}
41 | value={this.props.value[input.id]}
42 | />
43 | {input.label}
44 |
45 | )
46 | })
47 | return (
48 |
49 | {this.data.label.length > 0 &&
50 | {this.data.label}
51 | }
52 | {this.data.description.length > 0 &&
53 | {this.data.description}
54 | }
55 | {inputs}
56 |
57 | )
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/fieldComponents/CheckboxField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
3 | import HTML from 'react-native-render-html'
4 |
5 | export default class CheckboxField extends Component {
6 | constructor(props) {
7 | super(props)
8 | this.data = this.props.data
9 | this.style = this.props.style
10 | this.handleChange = this.handleChange.bind(this)
11 | }
12 |
13 | handleChange(field, value, input) {
14 | this.props.onChange(field, value, input)
15 | }
16 |
17 | render() {
18 | const inputs = this.data.inputs.map((input, index) => {
19 | return (
20 | this.handleChange(this.data.id, this.props.value[input.id] == this.data.choices[index].value ? false : this.data.choices[index].value, input.id)}
24 | >
25 |
26 |
31 |
32 |
33 |
34 |
35 |
36 | )
37 | })
38 | return (
39 |
40 | {this.data.label.length > 0 &&
41 | {this.data.label}
42 | }
43 | {this.data.description.length > 0 &&
44 | {this.data.description}
45 | }
46 | {inputs}
47 |
48 | )
49 | }
50 | }
51 |
52 | const styles = StyleSheet.create({
53 | selectedButton: {
54 | backgroundColor: '#000',
55 | flex: 1,
56 | borderRadius: 1,
57 | },
58 | buttonWrapper: {
59 | borderColor: '#000',
60 | borderWidth: 2,
61 | borderStyle: 'solid',
62 | borderRadius: 4,
63 | width: 16,
64 | height: 16,
65 | overflow: 'hidden',
66 | padding: 2,
67 | },
68 | })
--------------------------------------------------------------------------------
/fieldComponents/EmailField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, TextInput } from 'react-native'
3 |
4 | export default class EmailField extends Component {
5 | constructor(props) {
6 | super(props)
7 | this.data = this.props.data
8 | this.handleChange = this.handleChange.bind(this)
9 | this.style = this.props.style
10 | }
11 |
12 | handleChange(text) {
13 | this.props.onChange(this.data.id, text)
14 | }
15 |
16 | render() {
17 | return (
18 |
19 | {this.data.label.length > 0 &&
20 | {this.data.label}
21 | }
22 | {this.data.description.length > 0 &&
23 | {this.data.description}
24 | }
25 | this.handleChange(text)}
28 | placeholder={this.data.placeholder}
29 | value={this.props.value}
30 | />
31 |
32 | )
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/fieldComponents/HiddenField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, TextInput } from 'react-native'
3 |
4 | export default class HiddenField extends Component {
5 | constructor(props) {
6 | super(props)
7 | this.data = this.props.data
8 | this.handleChange = this.handleChange.bind(this)
9 | this.style = this.props.style
10 | }
11 |
12 | handleChange(number) {
13 | this.props.onChange(this.data.id, number)
14 | }
15 |
16 | render() {
17 | return (
18 |
19 | {this.data.label.length > 0 &&
20 | {this.data.label}
21 | }
22 | {this.data.description.length > 0 &&
23 | {this.data.description}
24 | }
25 | this.handleChange(number)}
28 | placeholder={this.data.placeholder}
29 | value={this.props.value}
30 | />
31 |
32 | )
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/fieldComponents/HtmlField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from 'react-native'
3 | import HTML from 'react-native-render-html'
4 |
5 | export default class HtmlField extends Component {
6 | constructor(props) {
7 | super(props)
8 | this.data = this.props.data
9 | this.style = this.props.style
10 | }
11 |
12 | render() {
13 | return (
14 |
15 |
16 |
17 | )
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/fieldComponents/NameField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, TextInput, Picker, TouchableOpacity, StyleSheet } from 'react-native'
3 |
4 | export default class NameField extends Component {
5 | constructor(props) {
6 | super(props)
7 | this.data = this.props.data
8 | this.handleChange = this.handleChange.bind(this)
9 | this.style = this.props.style
10 | this.state = {
11 | selecting: false,
12 | }
13 | }
14 |
15 | handleChange(field, text, input) {
16 | this.props.onChange(field, text, input)
17 | }
18 |
19 | // TODO: Separate out into components
20 | render() {
21 | const inputs = this.data.inputs.map(input => {
22 | if (input.isHidden) return
23 | if (input.choices) {
24 | const items = input.choices && input.choices.map((choice, index) => {
25 | return
26 | })
27 | return (
28 |
29 | {this.state.selecting &&
30 | {
33 | this.handleChange(this.data.id, value, input.id)
34 | this.setState({ selecting: false })
35 | }}
36 | >
37 | {items}
38 |
39 | ||
40 |
41 | this.setState({ selecting: true })}>
42 | {this.props.value[input.id]}
43 |
44 |
45 | {input.label}
46 |
47 | }
48 |
49 | )
50 | }
51 | return (
52 |
53 | this.handleChange(this.data.id, text, input.id)}
56 | placeholder={input.placeholder}
57 | value={this.props.value[input.id]}
58 | />
59 | {input.label}
60 |
61 | )
62 | })
63 | return (
64 |
65 | {this.data.label.length > 0 &&
66 | {this.data.label}
67 | }
68 | {this.data.description.length > 0 &&
69 | {this.data.description}
70 | }
71 | {inputs}
72 |
73 | )
74 | }
75 | }
76 |
77 | const styles = StyleSheet.create({
78 | fieldSelect: {
79 | flexDirection: 'row',
80 | justifyContent: 'space-between',
81 | alignItems: 'center',
82 | },
83 | arrow: {
84 | borderStyle: 'solid',
85 | borderTopColor: '#000',
86 | borderTopWidth: 10,
87 | borderLeftColor: 'transparent',
88 | borderLeftWidth: 7,
89 | borderRightColor: 'transparent',
90 | borderRightWidth: 7,
91 | width: 0,
92 | }
93 | })
--------------------------------------------------------------------------------
/fieldComponents/NumberField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, TextInput } from 'react-native'
3 |
4 | export default class NumberField extends Component {
5 | constructor(props) {
6 | super(props)
7 | this.data = this.props.data
8 | this.handleChange = this.handleChange.bind(this)
9 | this.style = this.props.style
10 | }
11 |
12 | handleChange(text) {
13 | this.props.onChange(this.data.id, text)
14 | }
15 |
16 | render() {
17 | return (
18 |
19 | {this.data.label.length > 0 &&
20 | {this.data.label}
21 | }
22 | {this.data.description.length > 0 &&
23 | {this.data.description}
24 | }
25 | this.handleChange(text)}
28 | placeholder={this.data.placeholder}
29 | value={this.props.value}
30 | />
31 |
32 | )
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/fieldComponents/PhoneField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, TextInput } from 'react-native'
3 |
4 | export default class PhoneField extends Component {
5 | constructor(props) {
6 | super(props)
7 | this.data = this.props.data
8 | this.handleChange = this.handleChange.bind(this)
9 | this.style = this.props.style
10 | }
11 |
12 | handleChange(number) {
13 | this.props.onChange(this.data.id, number)
14 | }
15 |
16 | render() {
17 | return (
18 |
19 | {this.data.label.length > 0 &&
20 | {this.data.label}
21 | }
22 | {this.data.description.length > 0 &&
23 | {this.data.description}
24 | }
25 | this.handleChange(number)}
28 | placeholder={this.data.placeholder}
29 | value={this.props.value}
30 | />
31 |
32 | )
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/fieldComponents/RadioField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
3 |
4 | export default class RadioField extends Component {
5 | constructor(props) {
6 | super(props)
7 | this.data = this.props.data
8 | this.style = this.props.style
9 | this.handleChange = this.handleChange.bind(this)
10 | }
11 |
12 | handleChange(field, value) {
13 | this.props.onChange(field, value)
14 | }
15 |
16 | render() {
17 | const choices = this.data.choices.map((choice, index) => {
18 | return (
19 | this.handleChange(this.data.id, choice.value)}
23 | >
24 |
25 |
30 |
31 | {choice.text}
32 |
33 | )
34 | })
35 | return (
36 |
37 | {this.data.label.length > 0 &&
38 | {this.data.label}
39 | }
40 | {this.data.description.length > 0 &&
41 | {this.data.description}
42 | }
43 | {choices}
44 |
45 | )
46 | }
47 | }
48 |
49 | const styles = StyleSheet.create({
50 | selectedButton: {
51 | backgroundColor: '#000',
52 | flex: 1,
53 | borderRadius: 999,
54 | },
55 | buttonWrapper: {
56 | borderColor: '#000',
57 | borderWidth: 2,
58 | borderStyle: 'solid',
59 | borderRadius: 999,
60 | width: 16,
61 | height: 16,
62 | overflow: 'hidden',
63 | padding: 2,
64 | },
65 | })
--------------------------------------------------------------------------------
/fieldComponents/SectionField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, StyleSheet } from 'react-native'
3 |
4 | export default class SectionField extends Component {
5 | constructor(props) {
6 | super(props)
7 | this.data = this.props.data
8 | this.style = this.props.style
9 | }
10 |
11 | render() {
12 | return (
13 |
14 | {this.data.label.length > 0 &&
15 | {this.data.label}
16 | }
17 | {this.data.description.length > 0 &&
18 | {this.data.description}
19 | }
20 |
21 |
22 | )
23 | }
24 | }
25 |
26 | const styles = StyleSheet.create({
27 | headerRow: {
28 | borderBottomColor: '#888',
29 | borderBottomWidth: 1,
30 | borderStyle: 'solid',
31 | },
32 | })
--------------------------------------------------------------------------------
/fieldComponents/SelectField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, Picker, TouchableOpacity, StyleSheet } from 'react-native'
3 |
4 | export default class SelectField extends Component {
5 | constructor(props) {
6 | super(props)
7 | this.data = this.props.data
8 | this.handleChange = this.handleChange.bind(this)
9 | this.style = this.props.style
10 | this.state = {
11 | selecting: false,
12 | }
13 | }
14 |
15 | handleChange(field, text) {
16 | this.props.onChange(field, text)
17 | this.setState({ selecting: false })
18 | }
19 |
20 | render() {
21 | const items = this.data.choices && this.data.choices.map((choice, index) => {
22 | return
23 | })
24 | return (
25 |
26 | {this.data.label.length > 0 &&
27 | {this.data.label}
28 | }
29 | {this.data.description.length > 0 &&
30 | {this.data.description}
31 | }
32 | {this.state.selecting &&
33 | this.handleChange(this.data.id, value)}
36 | >
37 | {items}
38 |
39 | ||
40 | this.setState({ selecting: true })}>
41 | {this.props.value}
42 |
43 |
44 | }
45 |
46 | )
47 | }
48 | }
49 |
50 | const styles = StyleSheet.create({
51 | fieldSelect: {
52 | flexDirection: 'row',
53 | justifyContent: 'space-between',
54 | alignItems: 'center',
55 | },
56 | arrow: {
57 | borderStyle: 'solid',
58 | borderTopColor: '#000',
59 | borderTopWidth: 10,
60 | borderLeftColor: 'transparent',
61 | borderLeftWidth: 7,
62 | borderRightColor: 'transparent',
63 | borderRightWidth: 7,
64 | width: 0,
65 | }
66 | })
--------------------------------------------------------------------------------
/fieldComponents/TextField.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, TextInput, StyleSheet } from 'react-native'
3 |
4 | export default class TextField extends Component {
5 | constructor(props) {
6 | super(props)
7 | this.data = this.props.data
8 | this.handleChange = this.handleChange.bind(this)
9 | this.style = this.props.style
10 | }
11 |
12 | handleChange(text) {
13 | this.props.onChange(this.data.id, text)
14 | }
15 |
16 | styles() {
17 | return StyleSheet.create(this.style)
18 | }
19 |
20 | render() {
21 | return (
22 |
23 | {this.data.label.length > 0 &&
24 | {this.data.label}
25 | }
26 | {this.data.description.length > 0 &&
27 | {this.data.description}
28 | }
29 | this.handleChange(text)}
32 | placeholder={this.data.placeholder}
33 | value={this.props.value}
34 | />
35 |
36 | )
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/fieldComponents/index.js:
--------------------------------------------------------------------------------
1 | // TODO: add support for 'website', 'date', 'time', 'textarea', and 'fileupload' field types
2 | export { default as AddressField } from './AddressField'
3 | export { default as CheckboxField } from './CheckboxField'
4 | export { default as EmailField } from './EmailField'
5 | export { default as HiddenField } from './HiddenField'
6 | export { default as HtmlField } from './HtmlField'
7 | export { default as NameField } from './NameField'
8 | export { default as NumberField } from './NumberField'
9 | export { default as PhoneField } from './PhoneField'
10 | export { default as RadioField } from './RadioField'
11 | export { default as SectionField } from './SectionField'
12 | export { default as SelectField } from './SelectField'
13 | export { default as TextField } from './TextField'
14 |
--------------------------------------------------------------------------------
/gravityforms.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sethcwhiting/react-native-gravityform/cf08dc12e8746220727274e8a1da7e66120aa621/gravityforms.zip
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { ScrollView, View, Text, TouchableOpacity } from 'react-native'
3 | // import ValidationComponent from 'react-native-form-validator'
4 | import base64 from 'react-native-base64'
5 | import {
6 | AddressField,
7 | CheckboxField,
8 | EmailField,
9 | HiddenField,
10 | HtmlField,
11 | NameField,
12 | NumberField,
13 | PhoneField,
14 | RadioField,
15 | SectionField,
16 | SelectField,
17 | TextField,
18 | } from './fieldComponents'
19 |
20 | export default class GravityForm extends Component {
21 | constructor(props) {
22 | super(props)
23 | this.siteURL = this.props.siteURL
24 | this.formID = this.props.formID
25 | const credentials = this.props.credentials
26 | const credentialString = `${credentials.userName}:${credentials.password}`
27 | this.encodedCredentials = base64.encode(credentialString)
28 | this.style = this.props.style
29 | this.state = {
30 | formData: {},
31 | fieldValues: {},
32 | isLoading: true,
33 | isSending: false,
34 | submitSuccess: false,
35 | submitFailure: false,
36 | }
37 | this.handleFieldChange = this.handleFieldChange.bind(this)
38 | }
39 |
40 | componentDidMount() {
41 | this.fetchFormData()
42 | .then(formData => {
43 | this.setState({ formData })
44 | return this.setDefaultValues(formData)
45 | })
46 | .then(() => this.setState({ isLoading: false }))
47 | .catch(err => console.warn('There was a problem retrieving form data: ', err))
48 | }
49 |
50 | fetchFormData() {
51 | return new Promise((resolve, reject) => {
52 | fetch(`${this.siteURL}/wp-json/gf/v2/forms/${this.formID}`, {
53 | method: 'GET',
54 | headers: {
55 | 'Accept': 'application/json',
56 | 'Content-Type': 'application/json',
57 | 'Authorization': `Basic ${this.encodedCredentials}`,
58 | }
59 | })
60 | .then(response => response.json().then(formData => resolve(formData)))
61 | .catch(err => reject('ERROR: ', err))
62 | })
63 | }
64 |
65 | values = {}
66 |
67 | setDefaultValues(formData) {
68 | return new Promise((resolve, reject) => {
69 | let fieldCount = formData.fields.length
70 | formData.fields.forEach(field => {
71 | switch (field.type) {
72 | case 'html':
73 | case 'section':
74 | fieldCount--
75 | break
76 |
77 | case 'name':
78 | case 'address':
79 | fieldCount = fieldCount + field.inputs.length
80 | this.populateComplexValues(field)
81 | break
82 |
83 | case 'checkbox':
84 | fieldCount = fieldCount + field.inputs.length
85 | this.populateCheckboxValues(field)
86 | break
87 |
88 | case 'radio':
89 | case 'select':
90 | this.populateChoiceValues(field)
91 | break
92 |
93 | default:
94 | if (field.id == '36') console.log(field)
95 | this.populateSimpleValue(field)
96 | break
97 | }
98 | })
99 | if (Object.keys(this.state.fieldValues).length == fieldCount) resolve()
100 | setTimeout(() => reject('"setDefaultValues" function timed out. Field values never populated completely.'), 5000)
101 | })
102 | }
103 |
104 | populateComplexValues(field) {
105 | field.inputs.forEach(input => {
106 | if (input.choices) {
107 | const selected = input.choices.filter(choice => choice.isSelected)
108 | this.setState({
109 | fieldValues: {
110 | ...this.state.fieldValues,
111 | [input.id]: selected.length ? selected[0].value : '',
112 | [field.id]: {
113 | ...this.state.fieldValues[field.id],
114 | [input.id]: selected[0] ? selected[0].value : ''
115 | }
116 | }
117 | })
118 | } else {
119 | this.setState({
120 | fieldValues: {
121 | ...this.state.fieldValues,
122 | [input.id]: input.defaultValue ? input.defaultValue : '',
123 | [field.id]: {
124 | ...this.state.fieldValues[field.id],
125 | [input.id]: input.defaultValue ? input.defaultValue : ''
126 | }
127 | }
128 | })
129 | }
130 | })
131 | }
132 |
133 | populateCheckboxValues(field) {
134 | field.inputs.forEach((input, index) => {
135 | this.setState({
136 | fieldValues: {
137 | ...this.state.fieldValues,
138 | [input.id]: field.choices[index].isSelected ? field.choices[index].value : false,
139 | [field.id]: {
140 | ...this.state.fieldValues[field.id],
141 | [input.id]: field.choices[index].isSelected ? field.choices[index].value : false,
142 | }
143 | }
144 | })
145 | })
146 | }
147 |
148 | populateChoiceValues(field) {
149 | const selected = field.choices.filter(choice => {
150 | return choice.isSelected
151 | })
152 | this.setState({ fieldValues: { ...this.state.fieldValues, [field.id]: selected.length ? selected[0].value : '' } })
153 | }
154 |
155 | populateSimpleValue(field) {
156 | this.setState({ fieldValues: { ...this.state.fieldValues, [field.id]: field.defaultValue } })
157 | }
158 |
159 | fieldHidden(field) {
160 | if (field.visibility != 'visible') return true
161 | if (typeof field.conditionalLogic == 'object' && field.conditionalLogic !== null) {
162 | return this.handleConditionalLogic(field)
163 | }
164 | return false
165 | }
166 |
167 | handleConditionalLogic(field) {
168 | const rulesMet = field.conditionalLogic.rules.map(rule => {
169 | let conditionalValue = this.state.fieldValues[rule.fieldId]
170 | if (typeof conditionalValue == 'object') {
171 | matchKey = Object.keys(conditionalValue).filter(key => this.state.fieldValues[key] == rule.value)[0]
172 | conditionalValue = matchKey ? this.state.fieldValues[matchKey] : false
173 | }
174 | switch (rule.operator) {
175 | case 'is':
176 | return conditionalValue == rule.value
177 |
178 | case 'is not':
179 | return conditionalValue != rule.value
180 |
181 | case 'greater than':
182 | return conditionalValue > rule.value
183 |
184 | case 'less than':
185 | return conditionalValue < rule.value
186 |
187 | case 'contains':
188 | return conditionalValue.indexOf(rule.value) >= 0
189 |
190 | case 'starts with':
191 | return conditionalValue.indexOf(rule.value) == 0
192 |
193 | case 'ends with':
194 | return conditionalValue.indexOf(rule.value) == conditionalValue.length - rule.value.length
195 |
196 | }
197 | })
198 | if (field.conditionalLogic.actionType == 'show') {
199 | return field.conditionalLogic.logicType == 'all' ? rulesMet.indexOf(false) >= 0 : rulesMet.indexOf(true) < 0
200 | } else {
201 | return field.conditionalLogic.logicType == 'all' ? rulesMet.indexOf(true) < 0 : rulesMet.indexOf(false) >= 0
202 | }
203 | }
204 |
205 | handleFieldChange(fieldId, value, inputId) {
206 | if (inputId) {
207 | this.setState({
208 | fieldValues: {
209 | ...this.state.fieldValues,
210 | [inputId]: value,
211 | [fieldId]: {
212 | ...this.state.fieldValues[fieldId],
213 | [inputId]: value,
214 | },
215 | }
216 | })
217 | } else {
218 | this.setState({
219 | fieldValues: {
220 | ...this.state.fieldValues,
221 | [fieldId]: value,
222 | }
223 | })
224 | }
225 | }
226 |
227 | submitForm() {
228 | this.setState({ isSending: true })
229 | let formData = {}
230 | let fieldCount = Object.keys(this.state.fieldValues).length
231 | Object.keys(this.state.fieldValues).forEach(key => {
232 | if (typeof this.state.fieldValues[key] == 'object' && this.state.fieldValues[key] !== null) {
233 | fieldCount--
234 | } else {
235 | formData = { ...formData, [key]: this.state.fieldValues[key] }
236 | }
237 | if (Object.keys(formData).length == fieldCount) this.postFormData(formData)
238 | })
239 | }
240 |
241 | postFormData(formData) {
242 | fetch(`${this.siteURL}/wp-json/gf/v2/forms/${this.formID}/entries`, {
243 | method: 'POST',
244 | headers: {
245 | 'Accept': 'application/json',
246 | 'Content-Type': 'application/json',
247 | 'Authorization': `Basic ${this.encodedCredentials}`,
248 | },
249 | body: JSON.stringify(formData),
250 | })
251 | .then(() => this.setState({ isSending: false }))
252 | .catch(err => console.error('ERROR: ', err));
253 | }
254 |
255 | fieldComponents = {
256 | address: AddressField,
257 | checkbox: CheckboxField,
258 | email: EmailField,
259 | hidden: HiddenField,
260 | html: HtmlField,
261 | name: NameField,
262 | number: NumberField,
263 | phone: PhoneField,
264 | radio: RadioField,
265 | section: SectionField,
266 | select: SelectField,
267 | text: TextField,
268 | }
269 |
270 | render() {
271 | if (this.state.isLoading) {
272 | return (
273 |
274 | Fetching Gravity Form...
275 |
276 | )
277 | }
278 | if (this.state.isSending) {
279 | return (
280 |
281 | Submitting Form...
282 |
283 | )
284 | }
285 | let parentHidden = false
286 | const fields = this.state.formData.fields && this.state.formData.fields.map((field) => {
287 | if (Object.keys(this.fieldComponents).indexOf(field.type) < 0) {
288 | console.warn(`React Native Gravityform: No field component currently available for type "${field.type}".`)
289 | return
290 | }
291 | const FieldComponent = this.fieldComponents[field.type || 'text']
292 | if (field.type == 'section') parentHidden = this.fieldHidden(field)
293 | return (
294 |
298 |
304 |
305 | )
306 | })
307 | return (
308 |
309 | {this.state.formData.title.length > 0 && !this.props.hideFormTitle &&
310 |
311 | {this.state.formData.title}
312 |
313 | }
314 | {fields}
315 | {this.state.formData.button &&
316 |
317 | this.submitForm()} style={this.style.button}>
318 | {this.state.formData.button.text}
319 |
320 |
321 | }
322 |
323 | )
324 | }
325 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-gravityform",
3 | "version": "1.0.14",
4 | "description": "A component for including Gravity Forms on React Native apps via the Wordpress API",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/sethcwhiting/react-native-gravityform.git"
12 | },
13 | "keywords": [
14 | "React",
15 | "Native",
16 | "Gravity",
17 | "Form",
18 | "Forms",
19 | "GravityForms",
20 | "Component",
21 | "Wordpress",
22 | "API",
23 | "Headless",
24 | "CMS"
25 | ],
26 | "author": "Seth C Whiting",
27 | "license": "MIT",
28 | "bugs": {
29 | "url": "https://github.com/sethcwhiting/react-native-gravityform/issues"
30 | },
31 | "homepage": "https://github.com/sethcwhiting/react-native-gravityform#readme",
32 | "devDependencies": {
33 | "react": "^16.7.0",
34 | "react-native": "^0.58.3",
35 | "react-native-base64": "0.0.2",
36 | "react-native-form-validator": "^0.2.0",
37 | "react-native-render-html": "^4.0.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # ✨ React Native Gravityform ✨
2 |
3 | [](https://www.npmjs.com/package/react-native-gravityform)
4 |
5 | This module includes a react native component for dropping Gravity Forms from your Wordpress site into your native applications.
6 |
7 | The package is both **Android** and **iOS** compatible.
8 |
9 | This project is compatible with Expo/CRNA without ejecting.
10 |
11 | ## Installation
12 |
13 | ```
14 | $ npm install --save react-native-gravityform
15 | ```
16 |
17 | The solution is implemented in JavaScript so no native module linking is required.
18 |
19 | ## Usage
20 |
21 | ### Authentication
22 |
23 | Gravity Forms requires that API requests be authenticated. In order to get this working, install Wordpress's [JSON Basic Authentication](https://github.com/WP-API/Basic-Auth) plugin.
24 |
25 | Once you've done this, make a file named something to the effect of `credentials.js` and add it anywhere in your project. The entire contents of this file should look something like this:
26 |
27 | ```javascript
28 | export default {
29 | userName: 'your-wp-username',
30 | password: 'your-wp-password',
31 | };
32 | ```
33 |
34 | It may be a good idea to make a new Wordpress user with read/write permissions for the sole purpose of posting to your Gravity Forms and include that new user's information to the file above. Also, **make sure to include this file in your `.gitignore` so no one ever sees this information.**
35 |
36 | Once your `credentials.js` file is all set, you can import it into any file:
37 |
38 | ```javascript
39 | import credentials from '...path_to/credentials';
40 | ```
41 |
42 | ### ✨ The GravityForm Component ✨
43 |
44 | Import the GravityForm component:
45 |
46 | ```javascript
47 | import GravityForm from 'react-native-gravityform';
48 | ```
49 |
50 | Include the component anywhere inside your own components:
51 |
52 | ```javascript
53 |
60 | ```
61 |
62 | ## Props
63 |
64 | ### siteURL
65 |
66 | The base URL for your Wordpress site where your Gravity Forms are hosted.
67 |
68 | ### formID
69 |
70 | The ID of the specific Gravity Form you want to display/post to.
71 |
72 | ### credentials
73 |
74 | The credentials you imported from the file you created in the [Authentication](#authentication) step above.
75 |
76 | ### style
77 |
78 | Out of the box, the GravityForm component is almost entirely unstyled, so you'll probably want to write your own styles for your fields.
79 |
80 | This can be done by including a new StyleSheet and referencing the _built-in element names_ ([see full list](https://github.com/sethcwhiting/react-native-gravityform/blob/master/elementNames.md)), like so:
81 |
82 | ```javascript
83 | const gformStyles = StyleSheet.create({
84 | fieldInput: {
85 | color: '#224',
86 | backgroundColor: '#eee',
87 | padding: 15,
88 | marginBottom: 15,
89 | fontSize: 18,
90 | },
91 | });
92 | ```
93 |
94 | ### hideFormTitle
95 |
96 | Choose wether you want your form title to be hidden or not.
97 |
98 | ## All Together Now
99 |
100 | Here is a basic example of how you would use the GravityForm component within one of your components:
101 |
102 | ```javascript
103 | import React, { Component } from 'react';
104 | import { StyleSheet, View } from 'react-native';
105 | import GravityForm from 'react-native-gravityform';
106 | import credentials from '../Credentials';
107 |
108 | const styles = StyleSheet.create({
109 | container: {
110 | flex: 1,
111 | backgroundColor: '#fff',
112 | },
113 | });
114 |
115 | const gformStyles = StyleSheet.create({
116 | fieldLabel: {
117 | fontWeight: 'bold',
118 | fontSize: 16,
119 | color: '#224',
120 | },
121 | fieldInput: {
122 | color: '#224',
123 | backgroundColor: '#eee',
124 | padding: 15,
125 | marginBottom: 15,
126 | fontSize: 18,
127 | },
128 | button: {
129 | backgroundColor: '#1c9',
130 | padding: 15,
131 | borderRadius: 15,
132 | },
133 | buttonText: {
134 | color: '#fff',
135 | fontSize: 20,
136 | textAlign: 'center',
137 | fontWeight: 'bold',
138 | },
139 | });
140 |
141 | export default class ContactScreen extends Component {
142 | render() {
143 | return (
144 |
145 |
152 |
153 | );
154 | }
155 | }
156 | ```
157 |
158 | ## Supported Fields
159 |
160 | The goal for this component is to support all [Standard](https://docs.gravityforms.com/form-fields/#standard-fields) and [Advanced](https://docs.gravityforms.com/form-fields/#advanced-fields) fields offered by Gravity Forms.
161 |
162 | The list of the fields currently supported by the GravityForm component are marked with a check mark below:
163 |
164 | **Standard:**
165 |
166 | - [x] Single Line Text
167 | - [ ] Paragraph Text
168 | - [x] Drop Down
169 | - [ ] Multi Select
170 | - [x] Number
171 | - [x] Checkboxes
172 | - [x] Radio Buttons
173 | - [x] Hidden
174 | - [x] HTML
175 | - [x] Section Break
176 | - [ ] Page Break
177 |
178 | **Advanced:**
179 |
180 | - [x] Name
181 | - [ ] Date
182 | - [ ] Time
183 | - [x] Phone
184 | - [x] Address
185 | - [ ] Website
186 | - [x] Email
187 | - [ ] File Upload
188 | - [ ] CAPTCHA
189 | - [ ] Password
190 | - [ ] List
191 |
192 | ## Conditional Logic
193 |
194 | Conditional Logic is included and should work right out of the box!
195 |
196 | ## Validation
197 |
198 | There is currently no form validation included with the GravityForm component. This is a major priority for the team and will be coming as soon as we can possibly get to it.
199 |
200 | ## Authors
201 |
202 | - [Seth C Whiting](https://github.com/sethcwhiting/) - Initial code - [@sethcwhiting](https://twitter.com/sethcwhiting)
203 |
204 | ## Contributing
205 |
206 | Pull requests are very welcome.
207 |
--------------------------------------------------------------------------------