Last step
- improved watch command (much simpler robofile + truly recursive) - split all form fields into separate files (JSX) - improved form to allow multiple fields per row - added selection react class for multi select using select2 - added missing files for select2 (webpack doesn't include them)
This commit is contained in:
64
assets/js/src/form/fields/checkbox.jsx
Normal file
64
assets/js/src/form/fields/checkbox.jsx
Normal file
@@ -0,0 +1,64 @@
|
||||
define([
|
||||
'react',
|
||||
'react-checkbox-group'
|
||||
],
|
||||
function(
|
||||
React,
|
||||
CheckboxGroup
|
||||
) {
|
||||
var FormFieldCheckbox = React.createClass({
|
||||
render: function() {
|
||||
var selected_values = this.props.item[this.props.field.name] || '';
|
||||
if(
|
||||
selected_values !== undefined
|
||||
&& selected_values.constructor !== Array
|
||||
) {
|
||||
selected_values = selected_values.split(';').map(function(value) {
|
||||
return value.trim();
|
||||
});
|
||||
}
|
||||
var count = Object.keys(this.props.field.values).length;
|
||||
|
||||
var options = Object.keys(this.props.field.values).map(
|
||||
function(value, index) {
|
||||
return (
|
||||
<p key={ 'checkbox-' + index }>
|
||||
<label>
|
||||
<input type="checkbox" value={ value } />
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
return (
|
||||
<CheckboxGroup
|
||||
name={ this.props.field.name }
|
||||
value={ selected_values }
|
||||
ref={ this.props.field.name }
|
||||
onChange={ this.handleValueChange }>
|
||||
{ options }
|
||||
</CheckboxGroup>
|
||||
);
|
||||
},
|
||||
handleValueChange: function() {
|
||||
var field = this.props.field.name;
|
||||
var group = this.refs[field];
|
||||
var selected_values = [];
|
||||
|
||||
if(group !== undefined) {
|
||||
selected_values = group.getCheckedValues();
|
||||
}
|
||||
|
||||
return this.props.onValueChange({
|
||||
target: {
|
||||
name: field,
|
||||
value: selected_values.join(';')
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return FormFieldCheckbox;
|
||||
});
|
110
assets/js/src/form/fields/field.jsx
Normal file
110
assets/js/src/form/fields/field.jsx
Normal file
@@ -0,0 +1,110 @@
|
||||
define([
|
||||
'react',
|
||||
'form/fields/text.jsx',
|
||||
'form/fields/textarea.jsx',
|
||||
'form/fields/select.jsx',
|
||||
'form/fields/radio.jsx',
|
||||
'form/fields/checkbox.jsx'
|
||||
],
|
||||
function(
|
||||
React,
|
||||
FormFieldText,
|
||||
FormFieldTextarea,
|
||||
FormFieldSelect,
|
||||
FormFieldRadio,
|
||||
FormFieldCheckbox
|
||||
) {
|
||||
var FormField = React.createClass({
|
||||
renderField: function(data, inline = true) {
|
||||
var description = false;
|
||||
if(data.field.description) {
|
||||
description = (
|
||||
<p className="description">{ data.field.description }</p>
|
||||
);
|
||||
}
|
||||
|
||||
var field = false;
|
||||
|
||||
if(data.field['field'] !== undefined) {
|
||||
field = data.field.field;
|
||||
} else{
|
||||
switch(data.field.type) {
|
||||
case 'text':
|
||||
field = (<FormFieldText {...data} />);
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
field = (<FormFieldTextarea {...data} />);
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
field = (<FormFieldSelect {...data} />);
|
||||
break;
|
||||
|
||||
case 'radio':
|
||||
field = (<FormFieldRadio {...data} />);
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
field = (<FormFieldCheckbox {...data} />);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(inline === true) {
|
||||
return (
|
||||
<span>
|
||||
{ field }
|
||||
{ description }
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
{ field }
|
||||
{ description }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
render: function() {
|
||||
var field = false;
|
||||
|
||||
if(this.props.field['fields'] !== undefined) {
|
||||
field = this.props.field.fields.map(function(subfield) {
|
||||
return this.renderField({
|
||||
field: subfield,
|
||||
item: this.props.item
|
||||
});
|
||||
}.bind(this));
|
||||
} else {
|
||||
field = this.renderField(this.props);
|
||||
}
|
||||
|
||||
var tip = false;
|
||||
if(this.props.field.tip) {
|
||||
tip = (
|
||||
<p className="description">{ this.props.field.tip }</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label
|
||||
htmlFor={ 'field_'+this.props.field.name }
|
||||
>
|
||||
{ this.props.field.label }
|
||||
{ tip }
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
{ field }
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return FormField;
|
||||
});
|
39
assets/js/src/form/fields/radio.jsx
Normal file
39
assets/js/src/form/fields/radio.jsx
Normal file
@@ -0,0 +1,39 @@
|
||||
define([
|
||||
'react'
|
||||
],
|
||||
function(
|
||||
React
|
||||
) {
|
||||
var FormFieldRadio = React.createClass({
|
||||
render: function() {
|
||||
var selected_value = this.props.item[this.props.field.name];
|
||||
var count = Object.keys(this.props.field.values).length;
|
||||
|
||||
var options = Object.keys(this.props.field.values).map(
|
||||
function(value, index) {
|
||||
return (
|
||||
<p key={ 'radio-' + index }>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
checked={ selected_value === value }
|
||||
value={ value }
|
||||
onChange={ this.props.onValueChange }
|
||||
name={ this.props.field.name } />
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ options }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return FormFieldRadio;
|
||||
});
|
34
assets/js/src/form/fields/select.jsx
Normal file
34
assets/js/src/form/fields/select.jsx
Normal file
@@ -0,0 +1,34 @@
|
||||
define([
|
||||
'react'
|
||||
],
|
||||
function(
|
||||
React
|
||||
) {
|
||||
var FormFieldSelect = React.createClass({
|
||||
render: function() {
|
||||
var options =
|
||||
Object.keys(this.props.field.values).map(function(value, index) {
|
||||
return (
|
||||
<option
|
||||
key={ 'option-' + index }
|
||||
value={ value }>
|
||||
{ this.props.field.values[value] }
|
||||
</option>
|
||||
);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
return (
|
||||
<select
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
onChange={ this.props.onValueChange }>
|
||||
{options}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return FormFieldSelect;
|
||||
});
|
119
assets/js/src/form/fields/selection.jsx
Normal file
119
assets/js/src/form/fields/selection.jsx
Normal file
@@ -0,0 +1,119 @@
|
||||
define([
|
||||
'react',
|
||||
'jquery'
|
||||
],
|
||||
function(
|
||||
React,
|
||||
jQuery
|
||||
) {
|
||||
var Selection = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
loading: false,
|
||||
items: [],
|
||||
selected: []
|
||||
}
|
||||
},
|
||||
componentWillMount: function() {
|
||||
this.loadCachedItems();
|
||||
},
|
||||
componentDidMount: function() {
|
||||
jQuery('#'+this.props.id).select2({
|
||||
width: '25em'
|
||||
});
|
||||
},
|
||||
loadCachedItems: function() {
|
||||
if(typeof(window['mailpoet_'+this.props.endpoint]) !== 'undefined') {
|
||||
var items = window['mailpoet_'+this.props.endpoint];
|
||||
this.setState({
|
||||
items: items
|
||||
});
|
||||
}
|
||||
},
|
||||
loadItems: function() {
|
||||
this.setState({ loading: true });
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'listing',
|
||||
data: {
|
||||
'offset': 0,
|
||||
'limit': 100,
|
||||
'search': '',
|
||||
'sort_by': 'name',
|
||||
'sort_order': 'asc'
|
||||
}
|
||||
})
|
||||
.done(function(response) {
|
||||
if(this.isMounted()) {
|
||||
if(response === false) {
|
||||
this.setState({
|
||||
loading: false,
|
||||
items: []
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
items: response.items
|
||||
});
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
handleChange: function() {
|
||||
var new_value = this.refs.selection.getDOMNode().value;
|
||||
|
||||
if(this.props.multiple === false) {
|
||||
if(new_value.trim().length === 0) {
|
||||
new_value = false;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selected: new_value
|
||||
});
|
||||
} else {
|
||||
var selected_values = this.state.selected || [];
|
||||
|
||||
if(selected_values.indexOf(new_value) !== -1) {
|
||||
// value already present so remove it
|
||||
selected_values.splice(selected_values.indexOf(new_value), 1);
|
||||
} else {
|
||||
selected_values.push(new_value);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selected: selected_values
|
||||
});
|
||||
}
|
||||
},
|
||||
getSelected: function() {
|
||||
return this.state.selected;
|
||||
},
|
||||
render: function() {
|
||||
var options = this.state.items.map(function(item, index) {
|
||||
return (
|
||||
<option
|
||||
key={ 'action-' + index }
|
||||
value={ item.id }>
|
||||
{ item.name }
|
||||
</option>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<select
|
||||
ref="selection"
|
||||
id={ this.props.id }
|
||||
value={ this.state.selected }
|
||||
onChange={ this.handleChange }
|
||||
placeholder={ this.props.placeholder }
|
||||
multiple
|
||||
>
|
||||
{ options }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return Selection;
|
||||
});
|
24
assets/js/src/form/fields/text.jsx
Normal file
24
assets/js/src/form/fields/text.jsx
Normal file
@@ -0,0 +1,24 @@
|
||||
define([
|
||||
'react'
|
||||
],
|
||||
function(
|
||||
React
|
||||
) {
|
||||
var FormFieldText = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
className={ (this.props.field.size) ? '' : 'regular-text' }
|
||||
size={ (this.props.field.size !== 'auto') ? this.props.field.size : false }
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
placeholder={ this.props.field.placeholder }
|
||||
onChange={ this.props.onValueChange } />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return FormFieldText;
|
||||
});
|
22
assets/js/src/form/fields/textarea.jsx
Normal file
22
assets/js/src/form/fields/textarea.jsx
Normal file
@@ -0,0 +1,22 @@
|
||||
define([
|
||||
'react'
|
||||
],
|
||||
function(
|
||||
React
|
||||
) {
|
||||
var FormFieldTextarea = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<textarea
|
||||
type="text"
|
||||
className="regular-text"
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
onChange={ this.props.onValueChange } />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return FormFieldTextarea;
|
||||
});
|
@@ -4,206 +4,15 @@ define(
|
||||
'mailpoet',
|
||||
'classnames',
|
||||
'react-router',
|
||||
'react-checkbox-group'
|
||||
'form/fields/field.jsx'
|
||||
],
|
||||
function(
|
||||
React,
|
||||
MailPoet,
|
||||
classNames,
|
||||
Router,
|
||||
CheckboxGroup
|
||||
FormField
|
||||
) {
|
||||
|
||||
|
||||
var FormFieldSelect = React.createClass({
|
||||
render: function() {
|
||||
var options =
|
||||
Object.keys(this.props.field.values).map(function(value, index) {
|
||||
return (
|
||||
<option
|
||||
key={ 'option-' + index }
|
||||
value={ value }>
|
||||
{ this.props.field.values[value] }
|
||||
</option>
|
||||
);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
return (
|
||||
<select
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
onChange={ this.props.onValueChange }>
|
||||
{options}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var FormFieldRadio = React.createClass({
|
||||
render: function() {
|
||||
var selected_value = this.props.item[this.props.field.name];
|
||||
var count = Object.keys(this.props.field.values).length;
|
||||
|
||||
var options = Object.keys(this.props.field.values).map(
|
||||
function(value, index) {
|
||||
return (
|
||||
<p key={ 'radio-' + index }>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
checked={ selected_value === value }
|
||||
value={ value }
|
||||
onChange={ this.props.onValueChange }
|
||||
name={ this.props.field.name } />
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ options }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var FormFieldCheckbox = React.createClass({
|
||||
render: function() {
|
||||
var selected_values = this.props.item[this.props.field.name] || '';
|
||||
if(
|
||||
selected_values !== undefined
|
||||
&& selected_values.constructor !== Array
|
||||
) {
|
||||
selected_values = selected_values.split(';').map(function(value) {
|
||||
return value.trim();
|
||||
});
|
||||
}
|
||||
var count = Object.keys(this.props.field.values).length;
|
||||
|
||||
var options = Object.keys(this.props.field.values).map(
|
||||
function(value, index) {
|
||||
return (
|
||||
<p key={ 'checkbox-' + index }>
|
||||
<label>
|
||||
<input type="checkbox" value={ value } />
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
return (
|
||||
<CheckboxGroup
|
||||
name={ this.props.field.name }
|
||||
value={ selected_values }
|
||||
ref={ this.props.field.name }
|
||||
onChange={ this.handleValueChange }>
|
||||
{ options }
|
||||
</CheckboxGroup>
|
||||
);
|
||||
},
|
||||
handleValueChange: function() {
|
||||
var field = this.props.field.name;
|
||||
var group = this.refs[field];
|
||||
var selected_values = [];
|
||||
|
||||
if(group !== undefined) {
|
||||
selected_values = group.getCheckedValues();
|
||||
}
|
||||
|
||||
return this.props.onValueChange({
|
||||
target: {
|
||||
name: field,
|
||||
value: selected_values.join(';')
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var FormFieldText = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
className="regular-text"
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
onChange={ this.props.onValueChange } />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var FormFieldTextarea = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<textarea
|
||||
type="text"
|
||||
className="regular-text"
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
onChange={ this.props.onValueChange } />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var FormField = React.createClass({
|
||||
render: function() {
|
||||
|
||||
var description = false;
|
||||
if(this.props.field.description) {
|
||||
description = (
|
||||
<p className="description">{ this.props.field.description }</p>
|
||||
);
|
||||
}
|
||||
|
||||
var field = false;
|
||||
|
||||
switch(this.props.field.type) {
|
||||
case 'text':
|
||||
field = (<FormFieldText {...this.props} />);
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
field = (<FormFieldTextarea {...this.props} />);
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
field = (<FormFieldSelect {...this.props} />);
|
||||
break;
|
||||
|
||||
case 'radio':
|
||||
field = (<FormFieldRadio {...this.props} />);
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
field = (<FormFieldCheckbox {...this.props} />);
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label
|
||||
htmlFor={ 'field_'+this.props.field.name }
|
||||
>{ this.props.field.label }</label>
|
||||
</th>
|
||||
<td>
|
||||
{ field }
|
||||
{ description }
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var Form = React.createClass({
|
||||
mixins: [
|
||||
Router.Navigation
|
||||
@@ -305,25 +114,42 @@ define(
|
||||
});
|
||||
|
||||
var formClasses = classNames(
|
||||
'mailpoet_form',
|
||||
{ 'mailpoet_form_loading': this.state.loading }
|
||||
);
|
||||
|
||||
var fields = this.props.fields.map(function(field, index) {
|
||||
return (
|
||||
<FormField
|
||||
field={ field }
|
||||
item={ this.state.item }
|
||||
onValueChange={ this.handleValueChange }
|
||||
key={ 'field-'+index } />
|
||||
);
|
||||
var fields = this.props.fields.map(function(field, i) {
|
||||
// if(field['fields'] !== undefined) {
|
||||
// return field.fields.map(function(subfield, j) {
|
||||
// return (
|
||||
// <FormField
|
||||
// field={ subfield }
|
||||
// item={ this.state.item }
|
||||
// onValueChange={ this.handleValueChange }
|
||||
// key={ 'subfield-'+j } />
|
||||
// );
|
||||
// }.bind(this));
|
||||
// } else {
|
||||
return (
|
||||
<FormField
|
||||
field={ field }
|
||||
item={ this.state.item }
|
||||
onValueChange={ this.handleValueChange }
|
||||
key={ 'field-'+i } />
|
||||
);
|
||||
// }
|
||||
}.bind(this));
|
||||
|
||||
return (
|
||||
<form
|
||||
ref="form"
|
||||
className={ formClasses }
|
||||
onSubmit={ this.handleSubmit }>
|
||||
|
||||
onSubmit={
|
||||
(this.props.onSubmit !== undefined)
|
||||
? this.props.onSubmit
|
||||
: this.handleSubmit
|
||||
}
|
||||
>
|
||||
{ errors }
|
||||
|
||||
<table className="form-table">
|
||||
|
Reference in New Issue
Block a user