├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── dist ├── Constants.js ├── components │ ├── Checkbox.js │ ├── Form.js │ ├── HiddenField.js │ ├── RadioGroup.js │ ├── SingleSelect.js │ ├── SubmitButton.js │ ├── TextArea.js │ ├── TextInput.js │ └── common │ │ ├── FieldMixin.js │ │ └── FieldWrapper.js ├── index.js └── styles │ ├── Checkbox.css │ ├── RadioGroup.css │ ├── SingleSelect.css │ ├── SubmitButton.css │ ├── TextArea.css │ ├── TextInput.css │ └── common │ └── FieldWrapper.css ├── documentation ├── book.json ├── components │ ├── checkbox.md │ ├── components.md │ ├── form.md │ ├── hidden-field.md │ ├── radio-group.md │ ├── single-select.md │ ├── submit-button.md │ ├── text-area.md │ └── text-input.md ├── creating-components │ └── creating-components.md ├── examples │ └── examples.md ├── readme.md ├── summary.md └── validation │ └── validation.md ├── example ├── examples │ ├── BasicExample.js │ ├── BigFormExample.js │ ├── CheckboxExample.js │ ├── HiddenExample.js │ ├── ListeningExample.js │ ├── SingleSelectExample.js │ ├── TextAreaExample.js │ └── ValidationExample.js ├── index.html ├── index.js └── style.css ├── lib ├── .flowconfig ├── Constants.js ├── components │ ├── Checkbox.js │ ├── Form.js │ ├── HiddenField.js │ ├── RadioGroup.js │ ├── SingleSelect.js │ ├── SubmitButton.js │ ├── TextArea.js │ ├── TextInput.js │ └── common │ │ ├── FieldMixin.js │ │ └── FieldWrapper.js ├── index.js └── styles │ ├── Checkbox.css │ ├── RadioGroup.css │ ├── SingleSelect.css │ ├── SubmitButton.css │ ├── TextArea.css │ ├── TextInput.css │ └── common │ └── FieldWrapper.css ├── package.json ├── readme.md ├── scripts ├── build.js ├── clean.js ├── docs-build.js ├── docs-watch.js ├── helpers │ ├── Constants.js │ ├── babelOptions.js │ └── webpack.config.js └── watch.js └── test ├── .eslintrc ├── components ├── Form.js ├── SubmitButton.js ├── TextInput.js └── common │ ├── FieldMixin.js │ └── FieldWrapper.js └── helpers └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "react", 4 | "es2015-loose", 5 | "stage-0" 6 | ], 7 | "plugins": [ 8 | "add-module-exports", 9 | "transform-decorators-legacy" 10 | ], 11 | "compact": true 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,py,html}] 8 | charset = utf-8 9 | 10 | [*.html] 11 | indent_size = 2 12 | 13 | [*.py] 14 | indent_style = space 15 | indent_size = 4 16 | 17 | [*.{js,ts,html,styl,css}] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | [*.{ex,exs}] 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [*.{m,mm,h}] 26 | indent_style = space 27 | indent_size = 2 28 | 29 | [*.conf] 30 | indent_style = space 31 | indent_size = 2 32 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "es6": true 7 | }, 8 | "globals": { 9 | "__IOS__": true, 10 | "__MOBILE__": true, 11 | "__OVERLAY__": true, 12 | "__ANDROID__": true, 13 | "__WEB__": true 14 | }, 15 | "parserOptions": { 16 | "ecmaVersion": 7, 17 | "sourceType": "module", 18 | "ecmaFeatures": { 19 | "jsx": true, 20 | "experimentalObjectRestSpread": true 21 | } 22 | }, 23 | "plugins": [ 24 | "react", 25 | "flowtype", 26 | "flow-vars" 27 | ], 28 | "rules": { 29 | "curly": ["error", "multi-line"], 30 | "operator-linebreak": ["error", "after"], 31 | "camelcase": ["error", {"properties": "always"}], 32 | "max-len": ["error", {"code": 120, "ignoreComments": true}], 33 | "indent": ["error", 2, {"SwitchCase": 1}], 34 | "no-multi-spaces": "error", 35 | "no-multi-str": "error", 36 | "no-mixed-spaces-and-tabs": "error", 37 | "no-trailing-spaces": "error", 38 | "space-unary-ops": ["error", { "nonwords": false, "overrides": {}}], 39 | "one-var": ["error", "never"], 40 | "key-spacing": ["error", {"beforeColon": false, "afterColon": true }], 41 | "padded-blocks": ["error", "never"], 42 | "brace-style": ["error", "stroustrup", {"allowSingleLine": true}], 43 | "keyword-spacing": "error", 44 | "space-before-blocks": ["error", "always"], 45 | "eol-last": "error", 46 | "space-before-function-paren": ["error", "never"], 47 | "array-bracket-spacing": ["error", "never"], 48 | "space-in-parens": ["error", "never"], 49 | "no-multiple-empty-lines": "error", 50 | "no-with": "error", 51 | "comma-dangle": ["error", "only-multiline"], 52 | "yoda": ["error", "never"], 53 | "new-cap": ["error", {"newIsCap": true, "capIsNew": false}], 54 | "semi": ["error", "always"], 55 | "prefer-arrow-callback": "error", 56 | "prefer-spread": "error", 57 | "no-undef": "error", 58 | "no-extra-semi": "error", 59 | "no-dupe-keys": "error", 60 | "flowtype/space-before-type-colon": ["error", "never"], 61 | "flow-vars/define-flow-type": 1, 62 | "flow-vars/use-flow-type": 1, 63 | "prefer-const": ["error", {"destructuring": "all"}], 64 | "no-unused-vars": ["error", {"argsIgnorePattern": "^_" }], 65 | "no-var": "error", 66 | "react/jsx-uses-react": "error", 67 | "react/jsx-uses-vars": "error" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | node_modules 3 | npm-debug.log 4 | /index.js 5 | .documentation-output 6 | .example-output 7 | gitbook 8 | index.html 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | node_modules 3 | npm-debug.log 4 | .documentation-output 5 | .example-output 6 | gitbook 7 | index.html 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Discord Inc. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /dist/Constants.js: -------------------------------------------------------------------------------- 1 | 'use strict';exports.__esModule=true;exports.Validators=exports.CONTEXT_TYPES=undefined;var _react=require('react');var CONTEXT_TYPES=exports.CONTEXT_TYPES={setField:_react.PropTypes.func,initField:_react.PropTypes.func,getField:_react.PropTypes.func,removeField:_react.PropTypes.func,fields:_react.PropTypes.object,setHasBeenTouched:_react.PropTypes.func,canSubmit:_react.PropTypes.bool,isSubmitting:_react.PropTypes.bool,submit:_react.PropTypes.func};var Validators=exports.Validators={isFilled:function isFilled(errorMessage){return function(name,_ref){var value=_ref.value;if(!value||!value.length){return errorMessage;}};}}; -------------------------------------------------------------------------------- /dist/components/Checkbox.js: -------------------------------------------------------------------------------- 1 | 'use strict';exports.__esModule=true;var _dec,_class,_class2,_temp;var _react=require('react');var _react2=_interopRequireDefault(_react);var _reactMixin=require('react-mixin');var _reactMixin2=_interopRequireDefault(_reactMixin);var _FieldMixin=require('./common/FieldMixin');var _FieldMixin2=_interopRequireDefault(_FieldMixin);require('../styles/Checkbox.css');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self,call){if(!self){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call&&(typeof call==="object"||typeof call==="function")?call:self;}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function, not "+typeof superClass);}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass;}var Checkbox=(_dec=_reactMixin2.default.decorate(_FieldMixin2.default),_dec(_class=(_temp=_class2=function(_React$Component){_inherits(Checkbox,_React$Component);function Checkbox(){_classCallCheck(this,Checkbox);return _possibleConstructorReturn(this,_React$Component.apply(this,arguments));}Checkbox.prototype.componentWillMount=function componentWillMount(){this.initField();};Checkbox.prototype.shouldComponentUpdate=function shouldComponentUpdate(_nextState,_nextProps,nextContext){return this.shouldUpdate(nextContext);};Checkbox.prototype.onChange=function onChange(_ref){var target=_ref.target;this.setField({value:target.checked});};Checkbox.prototype.toggleValue=function toggleValue(){var field=this.getField();var value=!field.value;this.setField({value:value});};Checkbox.prototype.render=function render(){var _props=this.props;var name=_props.name;var label=_props.label;var field=this.getField();return _react2.default.createElement('div',{className:'forms-checkbox'},_react2.default.createElement('input',{type:'checkbox',onChange:this.onChange.bind(this),checked:field.value,name:name,ref:this.setRef.bind(this)}),_react2.default.createElement('label',{onClick:this.toggleValue.bind(this)},label));};return Checkbox;}(_react2.default.Component),_class2.propTypes={name:_react.PropTypes.string.isRequired,label:_react.PropTypes.oneOfType([_react.PropTypes.string,_react.PropTypes.element]),value:_react.PropTypes.bool},_class2.defaultProps={value:false},_temp))||_class);exports.default=Checkbox;module.exports=exports['default']; -------------------------------------------------------------------------------- /dist/components/Form.js: -------------------------------------------------------------------------------- 1 | 'use strict';exports.__esModule=true;var _extends=Object.assign||function(target){for(var i=1;i=0)continue;if(!Object.prototype.hasOwnProperty.call(obj,i))continue;target[i]=obj[i];}return target;}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self,call){if(!self){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call&&(typeof call==="object"||typeof call==="function")?call:self;}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function, not "+typeof superClass);}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass;}var Form=(_temp2=_class=function(_React$Component){_inherits(Form,_React$Component);function Form(){var _temp,_this,_ret;_classCallCheck(this,Form);for(var _len=arguments.length,args=Array(_len),_key=0;_key<_len;_key++){args[_key]=arguments[_key];}return _ret=(_temp=(_this=_possibleConstructorReturn(this,_React$Component.call.apply(_React$Component,[this].concat(args))),_this),_this.state={fields:{},canSubmit:false,isSubmitting:false},_temp),_possibleConstructorReturn(_this,_ret);}Form.prototype.getChildContext=function getChildContext(){return{setField:this.setField.bind(this),initField:this.initField.bind(this),removeField:this.removeField.bind(this),getField:this.getField.bind(this),fields:this.state.fields,setHasBeenTouched:this.setHasBeenTouched.bind(this),canSubmit:this.state.canSubmit,isSubmitting:this.state.isSubmitting,submit:this.submitForm.bind(this)};};Form.prototype.initField=function initField(_ref){var name=_ref.name;var value=_ref.value;var validator=_ref.validator;var hasDefaultValue=_ref.hasDefaultValue;var rest=_objectWithoutProperties(_ref,['name','value','validator','hasDefaultValue']);var fields=this.state.fields;var onFieldUpdate=this.props.onFieldUpdate;fields[name]=_extends({value:value,validator:validator,hasDefaultValue:hasDefaultValue,hasBeenTouched:false,error:null,displayError:false},rest);fields[name].error=this.getError(name,fields[name]);var canSubmit=this.canSubmit();this.setState({fields:fields,canSubmit:canSubmit});onFieldUpdate(name,fields[name]);};Form.prototype.setField=function setField(_ref2){var _extends2;var name=_ref2.name;var value=_ref2.value;var rest=_objectWithoutProperties(_ref2,['name','value']);var fields=this.state.fields;var onFieldUpdate=this.props.onFieldUpdate;var field=fields[name];var oldValue=field.value;var displayError=field.displayError||value!=oldValue&&field.hasBeenTouched;var newField=_extends({},field,{value:value,error:field.error,displayError:displayError},rest);newField.error=this.getError(name,newField);var newFields=_extends({},fields,(_extends2={},_extends2[name]=newField,_extends2));var canSubmit=this.canSubmit(newFields);this.setState({fields:newFields,canSubmit:canSubmit});onFieldUpdate(name,newField);};Form.prototype.removeField=function removeField(name){var fields=this.state.fields;var onFieldUpdate=this.props.onFieldUpdate;var newFields=_extends({},fields);delete newFields[name];var canSubmit=this.canSubmit(newFields);this.setState({fields:newFields,canSubmit:canSubmit});onFieldUpdate(name,null);};Form.prototype.getField=function getField(name){return this.state.fields[name];};Form.prototype.setHasBeenTouched=function setHasBeenTouched(name){var touched=arguments.length<=1||arguments[1]===undefined?true:arguments[1];var fields=this.state.fields;var onFieldUpdate=this.props.onFieldUpdate;var newFields=_extends({},fields);var error=this.getError(name,fields[name]);newFields[name]=_extends({},newFields[name],{hasBeenTouched:touched,error:error,displayError:touched});var canSubmit=this.canSubmit();this.setState({fields:newFields,canSubmit:canSubmit});onFieldUpdate(name,newFields[name]);};Form.prototype.getError=function getError(name,field){var fields=arguments.length<=2||arguments[2]===undefined?this.state.fields:arguments[2];var validator=field.validator;return validator?validator(name,field,fields):null;};Form.prototype.canSubmit=function canSubmit(){var fields=arguments.length<=0||arguments[0]===undefined?this.state.fields:arguments[0];for(var _key2 in fields){if(fields[_key2].error){return false;}}return true;};Form.prototype.submitForm=function submitForm(){var submit=this.props.submit;var fields=this.state.fields;this.setState({isSubmitting:true});var values={};for(var _key3 in fields){values[_key3]=fields[_key3].value;}submit(values,this.submitFinished.bind(this));};Form.prototype.submitFinished=function submitFinished(errors){var fields=this.state.fields;var newFields=_extends({},fields);for(var _key4 in errors){newFields[_key4]=_extends({},newFields[_key4],{error:errors[_key4]});}this.setState({isSubmitting:false,fields:newFields});this.setState({canSubmit:this.canSubmit(newFields)});};Form.prototype.render=function render(){var children=this.props.children;return _react2.default.createElement('form',{className:'forms-form'},children);};return Form;}(_react2.default.Component),_class.childContextTypes=_Constants.CONTEXT_TYPES,_class.propTypes={submit:_react.PropTypes.func.isRequired,onFieldUpdate:_react.PropTypes.func},_class.defaultProps={onFieldUpdate:function onFieldUpdate(){}},_temp2);exports.default=Form;module.exports=exports['default']; -------------------------------------------------------------------------------- /dist/components/HiddenField.js: -------------------------------------------------------------------------------- 1 | 'use strict';exports.__esModule=true;var _dec,_class,_class2,_temp;var _react=require('react');var _react2=_interopRequireDefault(_react);var _reactMixin=require('react-mixin');var _reactMixin2=_interopRequireDefault(_reactMixin);var _FieldMixin=require('./common/FieldMixin');var _FieldMixin2=_interopRequireDefault(_FieldMixin);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self,call){if(!self){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call&&(typeof call==="object"||typeof call==="function")?call:self;}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function, not "+typeof superClass);}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass;}var HiddenField=(_dec=_reactMixin2.default.decorate(_FieldMixin2.default),_dec(_class=(_temp=_class2=function(_React$Component){_inherits(HiddenField,_React$Component);function HiddenField(){_classCallCheck(this,HiddenField);return _possibleConstructorReturn(this,_React$Component.apply(this,arguments));}HiddenField.prototype.componentWillMount=function componentWillMount(){this.initField();};HiddenField.prototype.shouldComponentUpdate=function shouldComponentUpdate(_nextState,_nextProps,nextContext){return this.shouldUpdate(nextContext);};HiddenField.prototype.render=function render(){if(!this.props.render)return null;return _react2.default.createElement('input',{type:'hidden',name:this.props.name,value:this.getValue(),ref:this.setRef.bind(this)});};return HiddenField;}(_react2.default.Component),_class2.propTypes={name:_react.PropTypes.string.isRequired,value:_react.PropTypes.any.isRequired,render:_react.PropTypes.bool},_class2.defaultProps={render:false},_temp))||_class);exports.default=HiddenField;module.exports=exports['default']; -------------------------------------------------------------------------------- /dist/components/RadioGroup.js: -------------------------------------------------------------------------------- 1 | 'use strict';exports.__esModule=true;var _dec,_class,_class2,_temp;var _react=require('react');var _react2=_interopRequireDefault(_react);var _reactMixin=require('react-mixin');var _reactMixin2=_interopRequireDefault(_reactMixin);var _FieldMixin=require('./common/FieldMixin');var _FieldMixin2=_interopRequireDefault(_FieldMixin);var _FieldWrapper=require('./common/FieldWrapper');var _FieldWrapper2=_interopRequireDefault(_FieldWrapper);require('../styles/RadioGroup.css');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self,call){if(!self){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call&&(typeof call==="object"||typeof call==="function")?call:self;}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function, not "+typeof superClass);}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass;}var RadioGroup=(_dec=_reactMixin2.default.decorate(_FieldMixin2.default),_dec(_class=(_temp=_class2=function(_React$Component){_inherits(RadioGroup,_React$Component);function RadioGroup(){_classCallCheck(this,RadioGroup);return _possibleConstructorReturn(this,_React$Component.apply(this,arguments));}RadioGroup.prototype.componentWillMount=function componentWillMount(){this.initField();};RadioGroup.prototype.shouldComponentUpdate=function shouldComponentUpdate(_nextState,_nextProps,nextContext){return this.shouldUpdate(nextContext);};RadioGroup.prototype.onChange=function onChange(value){this.setField({value:value,hasBeenTouched:true});};RadioGroup.prototype.render=function render(){var _this2=this;var _props=this.props;var options=_props.options;var label=_props.label;var required=_props.required;var field=this.getField();var value=this.getValue();var radios=options.map(function(option,i){var onChange=_this2.onChange.bind(_this2,option.value);return _react2.default.createElement('div',{key:'radio-option-'+i},_react2.default.createElement('input',{type:'radio',checked:value==option.value,onChange:onChange}),_react2.default.createElement('label',{onClick:onChange},option.label));});return _react2.default.createElement(_FieldWrapper2.default,{error:field.error,required:required,label:label,displayError:field.displayError,className:'forms-radio-group'},radios);};return RadioGroup;}(_react2.default.Component),_class2.propTypes={name:_react.PropTypes.string.isRequired,options:_react.PropTypes.array.isRequired,label:_react.PropTypes.oneOfType([_react.PropTypes.string,_react.PropTypes.element]),required:_react.PropTypes.bool,validator:_react.PropTypes.func,value:_react.PropTypes.string},_class2.defaultProps={value:'',required:false},_temp))||_class);exports.default=RadioGroup;module.exports=exports['default']; -------------------------------------------------------------------------------- /dist/components/SingleSelect.js: -------------------------------------------------------------------------------- 1 | 'use strict';exports.__esModule=true;var _dec,_class,_class2,_temp;var _react=require('react');var _react2=_interopRequireDefault(_react);var _reactMixin=require('react-mixin');var _reactMixin2=_interopRequireDefault(_reactMixin);var _FieldMixin=require('./common/FieldMixin');var _FieldMixin2=_interopRequireDefault(_FieldMixin);var _FieldWrapper=require('./common/FieldWrapper');var _FieldWrapper2=_interopRequireDefault(_FieldWrapper);require('../styles/SingleSelect.css');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self,call){if(!self){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call&&(typeof call==="object"||typeof call==="function")?call:self;}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function, not "+typeof superClass);}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass;}var SingleSelect=(_dec=_reactMixin2.default.decorate(_FieldMixin2.default),_dec(_class=(_temp=_class2=function(_React$Component){_inherits(SingleSelect,_React$Component);function SingleSelect(){_classCallCheck(this,SingleSelect);return _possibleConstructorReturn(this,_React$Component.apply(this,arguments));}SingleSelect.prototype.componentWillMount=function componentWillMount(){var _props=this.props;var value=_props.value;var options=_props.options;var placeholder=_props.placeholder;var defaultValue='';if(value){defaultValue=value;}else if(placeholder){defaultValue='';}else if(options&&options[0]){defaultValue=options[0].value;}this.initField({value:defaultValue});};SingleSelect.prototype.shouldComponentUpdate=function shouldComponentUpdate(_nextState,_nextProps,nextContext){return this.shouldUpdate(nextContext);};SingleSelect.prototype.onChange=function onChange(_ref){var target=_ref.target;this.setField({value:target.value,hasBeenTouched:true});};SingleSelect.prototype.render=function render(){var _props2=this.props;var label=_props2.label;var placeholder=_props2.placeholder;var required=_props2.required;var name=_props2.name;var field=this.getField();var options=this.props.options.map(function(option,i){var value=option.value?option.value:option;var label=option.label?option.label:value;return _react2.default.createElement('option',{key:'option-'+i,value:value},label);});var placeholderNode=placeholder?_react2.default.createElement('option',{disabled:true},placeholder):null;return _react2.default.createElement(_FieldWrapper2.default,{error:field.error,required:required,label:label,displayError:field.displayError,className:'forms-single-select'},_react2.default.createElement('select',{onChange:this.onChange.bind(this),value:field.value,name:name,ref:this.setRef.bind(this)},placeholderNode,options));};return SingleSelect;}(_react2.default.Component),_class2.propTypes={intervalCheck:_react.PropTypes.number,name:_react.PropTypes.string.isRequired,options:_react.PropTypes.array.isRequired,label:_react.PropTypes.oneOfType([_react.PropTypes.string,_react.PropTypes.element]),placeholder:_react.PropTypes.string,required:_react.PropTypes.bool,validator:_react.PropTypes.func,value:_react.PropTypes.string},_class2.defaultProps={required:false},_temp))||_class);exports.default=SingleSelect;module.exports=exports['default']; -------------------------------------------------------------------------------- /dist/components/SubmitButton.js: -------------------------------------------------------------------------------- 1 | 'use strict';exports.__esModule=true;var _dec,_class,_class2,_temp;var _react=require('react');var _react2=_interopRequireDefault(_react);var _reactMixin=require('react-mixin');var _reactMixin2=_interopRequireDefault(_reactMixin);var _FieldMixin=require('./common/FieldMixin');var _FieldMixin2=_interopRequireDefault(_FieldMixin);require('../styles/SubmitButton.css');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self,call){if(!self){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call&&(typeof call==="object"||typeof call==="function")?call:self;}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function, not "+typeof superClass);}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass;}var SubmitButton=(_dec=_reactMixin2.default.decorate(_FieldMixin2.default),_dec(_class=(_temp=_class2=function(_React$Component){_inherits(SubmitButton,_React$Component);function SubmitButton(){_classCallCheck(this,SubmitButton);return _possibleConstructorReturn(this,_React$Component.apply(this,arguments));}SubmitButton.prototype.shouldComponentUpdate=function shouldComponentUpdate(_nextState,_nextProps,nextContext){return nextContext.canSubmit!=this.canSubmit()||nextContext.isSubmitting!=this.isSubmitting();};SubmitButton.prototype.render=function render(){var _props=this.props;var isSubmittingText=_props.isSubmittingText;var canSubmitText=_props.canSubmitText;var cannotSubmitText=_props.cannotSubmitText;if(this.isSubmitting()){return _react2.default.createElement('div',{className:'forms-submit-button forms-submitting'},isSubmittingText);}else if(this.canSubmit()){return _react2.default.createElement('div',{className:'forms-submit-button forms-can-submit',onClick:this.submit.bind(this)},canSubmitText);}else{return _react2.default.createElement('div',{className:'forms-submit-button forms-cannot-submit'},cannotSubmitText);}};return SubmitButton;}(_react2.default.Component),_class2.propTypes={canSubmitText:_react.PropTypes.oneOfType([_react.PropTypes.string,_react.PropTypes.element]),cannotSubmitText:_react.PropTypes.oneOfType([_react.PropTypes.string,_react.PropTypes.element]),isSubmittingText:_react.PropTypes.oneOfType([_react.PropTypes.string,_react.PropTypes.element])},_temp))||_class);exports.default=SubmitButton;module.exports=exports['default']; -------------------------------------------------------------------------------- /dist/components/TextArea.js: -------------------------------------------------------------------------------- 1 | 'use strict';exports.__esModule=true;var _extends=Object.assign||function(target){for(var i=1;i=0)continue;if(!Object.prototype.hasOwnProperty.call(obj,i))continue;target[i]=obj[i];}return target;}var FieldMixin={mixins:[_reactTimerMixin2.default],contextTypes:_Constants.CONTEXT_TYPES,_ref:null,getField:function getField(){var name=arguments.length<=0||arguments[0]===undefined?this.props.name:arguments[0];return this.context.getField(name);},initField:function initField(obj){var _props=this.props;var name=_props.name;var value=_props.value;var validator=_props.validator;return this.context.initField(_extends({name:name,value:value,validator:validator},obj));},setHasBeenTouched:function setHasBeenTouched(){var _ref=arguments.length<=0||arguments[0]===undefined?{}:arguments[0];var _ref$name=_ref.name;var name=_ref$name===undefined?this.props.name:_ref$name;var _ref$touched=_ref.touched;var touched=_ref$touched===undefined?true:_ref$touched;return this.context.setHasBeenTouched(name,touched);},setField:function setField(){var _ref2=arguments.length<=0||arguments[0]===undefined?{}:arguments[0];var _ref2$name=_ref2.name;var name=_ref2$name===undefined?this.props.name:_ref2$name;var rest=_objectWithoutProperties(_ref2,['name']);return this.context.setField(_extends({name:name},rest));},removeField:function removeField(){var name=arguments.length<=0||arguments[0]===undefined?this.props.name:arguments[0];return this.context.removeField(name);},getValue:function getValue(){if(this._ref&&this._ref.value){return this._ref.value;}else{return this.getField().value;}},isSubmitting:function isSubmitting(){return this.context.isSubmitting;},canSubmit:function canSubmit(){return this.context.canSubmit;},submit:function submit(){return this.context.submit();},setRef:function setRef(ref){this._ref=ref;},componentDidMount:function componentDidMount(){var _this=this;var intervalCheck=this.props.intervalCheck;if(this._ref&&intervalCheck){this.setInterval(function(){var event=new Event('input',{bubbles:true});_this._ref.dispatchEvent(event);},intervalCheck);}},shouldUpdate:function shouldUpdate(nextContext){var name=this.props.name;return this.context.fields[name]!=nextContext.fields[name];}};exports.default=FieldMixin;module.exports=exports['default']; -------------------------------------------------------------------------------- /dist/components/common/FieldWrapper.js: -------------------------------------------------------------------------------- 1 | 'use strict';exports.__esModule=true;var _extends=Object.assign||function(target){for(var i=1;i=0)continue;if(!Object.prototype.hasOwnProperty.call(obj,i))continue;target[i]=obj[i];}return target;}var FieldWrapper=function FieldWrapper(_ref){var error=_ref.error;var displayError=_ref.displayError;var required=_ref.required;var label=_ref.label;var className=_ref.className;var children=_ref.children;var rest=_objectWithoutProperties(_ref,['error','displayError','required','label','className','children']);return _react2.default.createElement('div',_extends({className:'forms-field '+className},rest),required||label?_react2.default.createElement('div',{className:'forms-required-label-wrapper'},required?_react2.default.createElement('span',{className:'forms-required-star'},'* '):null,label?_react2.default.createElement('label',{className:'forms-label'},label):null):null,children,error&&displayError?_react2.default.createElement('div',{className:'forms-error'},error):null);};FieldWrapper.propTypes={error:_react.PropTypes.oneOfType([_react.PropTypes.string,_react.PropTypes.element]),required:_react.PropTypes.bool,children:_react.PropTypes.any,label:_react.PropTypes.oneOfType([_react.PropTypes.string,_react.PropTypes.element])};exports.default=FieldWrapper;module.exports=exports['default']; -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | 'use strict';exports.__esModule=true;exports.Validators=exports.TextInput=exports.TextArea=exports.SingleSelect=exports.RadioGroup=exports.HiddenField=exports.Checkbox=exports.FieldWrapper=exports.FieldMixin=exports.SubmitButton=exports.Form=undefined;var _Constants=require('./Constants');Object.defineProperty(exports,'Validators',{enumerable:true,get:function get(){return _Constants.Validators;}});var _Form2=require('./components/Form');var _Form3=_interopRequireDefault(_Form2);var _SubmitButton2=require('./components/SubmitButton');var _SubmitButton3=_interopRequireDefault(_SubmitButton2);var _FieldMixin2=require('./components/common/FieldMixin');var _FieldMixin3=_interopRequireDefault(_FieldMixin2);var _FieldWrapper2=require('./components/common/FieldWrapper');var _FieldWrapper3=_interopRequireDefault(_FieldWrapper2);var _Checkbox2=require('./components/Checkbox');var _Checkbox3=_interopRequireDefault(_Checkbox2);var _HiddenField2=require('./components/HiddenField');var _HiddenField3=_interopRequireDefault(_HiddenField2);var _RadioGroup2=require('./components/RadioGroup');var _RadioGroup3=_interopRequireDefault(_RadioGroup2);var _SingleSelect2=require('./components/SingleSelect');var _SingleSelect3=_interopRequireDefault(_SingleSelect2);var _TextArea2=require('./components/TextArea');var _TextArea3=_interopRequireDefault(_TextArea2);var _TextInput2=require('./components/TextInput');var _TextInput3=_interopRequireDefault(_TextInput2);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}exports.Form=_Form3.default;// Main 2 | exports.SubmitButton=_SubmitButton3.default;exports.FieldMixin=_FieldMixin3.default;exports.FieldWrapper=_FieldWrapper3.default;// Fields 3 | exports.Checkbox=_Checkbox3.default;exports.HiddenField=_HiddenField3.default;exports.RadioGroup=_RadioGroup3.default;exports.SingleSelect=_SingleSelect3.default;exports.TextArea=_TextArea3.default;exports.TextInput=_TextInput3.default;// Base set of validators -------------------------------------------------------------------------------- /dist/styles/Checkbox.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/discord-react-forms/f09c42c763f5539facd058eba2ffe9ba03ecd8cd/dist/styles/Checkbox.css -------------------------------------------------------------------------------- /dist/styles/RadioGroup.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/discord-react-forms/f09c42c763f5539facd058eba2ffe9ba03ecd8cd/dist/styles/RadioGroup.css -------------------------------------------------------------------------------- /dist/styles/SingleSelect.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discord/discord-react-forms/f09c42c763f5539facd058eba2ffe9ba03ecd8cd/dist/styles/SingleSelect.css -------------------------------------------------------------------------------- /dist/styles/SubmitButton.css: -------------------------------------------------------------------------------- 1 | .forms-submit-button { 2 | padding: 10px 8px; 3 | border-radius: 4px; 4 | } 5 | 6 | .forms-can-submit { 7 | background: rgb(114, 137, 218); 8 | cursor: pointer; 9 | } 10 | 11 | .forms-cannot-submit { 12 | background: red; 13 | cursor: no-drop; 14 | } 15 | 16 | .forms-submitting { 17 | background: grey; 18 | cursor: progress; 19 | } 20 | -------------------------------------------------------------------------------- /dist/styles/TextArea.css: -------------------------------------------------------------------------------- 1 | .forms-text-area-input { 2 | background: hsla(0, 0%, 100%, 0.06); 3 | color: hsla(0, 0%, 100%, 0.7); 4 | border: 2px solid hsla(0, 0%, 100%, 0.2); 5 | outline: none; 6 | padding: 4px 8px; 7 | border-radius: 4px; 8 | } 9 | -------------------------------------------------------------------------------- /dist/styles/TextInput.css: -------------------------------------------------------------------------------- 1 | .forms-text-input-input { 2 | background: hsla(0, 0%, 100%, 0.06); 3 | color: hsla(0, 0%, 100%, 0.7); 4 | border: 2px solid hsla(0, 0%, 100%, 0.2); 5 | outline: none; 6 | padding: 4px 8px; 7 | border-radius: 4px; 8 | } 9 | 10 | .forms-text-input-input::placeholder { 11 | color: hsla(0, 0%, 100%, 0.4); 12 | } 13 | -------------------------------------------------------------------------------- /dist/styles/common/FieldWrapper.css: -------------------------------------------------------------------------------- 1 | .field { 2 | display: flex; 3 | flex-direction: column; 4 | } 5 | 6 | .required-label-wrapper { 7 | margin-bottom: 8px; 8 | } 9 | 10 | .required-star { 11 | } 12 | 13 | .label { 14 | } 15 | 16 | .error { 17 | color: red; 18 | } 19 | -------------------------------------------------------------------------------- /documentation/book.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "React Forms", 3 | "structure": { 4 | "summary": "summary.md" 5 | }, 6 | 7 | "plugins": [ 8 | "github", 9 | "edit-link" 10 | ], 11 | 12 | "links": { 13 | "home": "discord", 14 | "sharing": { 15 | "twitter": "https://twitter.com/discordapp" 16 | } 17 | }, 18 | 19 | "pluginsConfig": { 20 | "edit-link": { 21 | "base": "https://github.com/discordapp/react-forms/tree/master", 22 | "label": "Suggest changes" 23 | }, 24 | 25 | "github": { 26 | "url": "https://github.com/discordapp/react-forms" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /documentation/components/checkbox.md: -------------------------------------------------------------------------------- 1 | # Checkbox 2 | 3 | This is a basic true/false component. 4 | 5 | ```javascript 6 | const Example = () => ( 7 |
8 | 9 | 10 | ); 11 | ``` 12 | 13 | ## Props 14 | - `name` (string, required): This will map to the key in the form 15 | - `label` (string): This will be displayed next to the checkbox 16 | - `value` (bool): This is the default value of the checkbox 17 | -------------------------------------------------------------------------------- /documentation/components/components.md: -------------------------------------------------------------------------------- 1 | # Components 2 | 3 | These are the componenets built into the library 4 | 5 | 6 | ## Special componenets 7 | - [Form](form.md) 8 | - [SubmitButton](submit-button.md) 9 | 10 | ## Input componenets 11 | - [Checkbox](checkbox.md) 12 | - [HiddenField](hidden-field.md) 13 | - [RadioGroup](radio-group.md) 14 | - [SingleSelect](single-select.md) 15 | - [TextArea](text-area.md) 16 | - [TextInput](text-input.md) 17 | -------------------------------------------------------------------------------- /documentation/components/form.md: -------------------------------------------------------------------------------- 1 | # Form 2 | 3 | The form component will be wrapped around any other component from the library 4 | 5 | ```javascript 6 | function submit(values, callback) { 7 | console.log(values); 8 | callback(); 9 | } 10 | 11 | function onFieldUpdate(name, field) { 12 | console.log(name, field); 13 | } 14 | 15 | const BasicForm = () => ( 16 |
17 | {/* Fields will go here */} 18 |
19 | ) 20 | ``` 21 | 22 | ## Props 23 | ### `submit` (func) 24 | `submit` is a function that takes values and a callback. 25 | This will gather all form values pass them to the function to submit. 26 | You are also passed a callback that should be called with any errors you include 27 | 28 | ### `onFieldUpdate` (func): 29 | `onFieldUpdate` is a function that takes a string `name`, and an object, `field`. 30 | `name` is the key of the field that has been updated. 31 | `field` is the object containing the information about the field that has been updated. 32 | 33 | 34 | ## Basic example 35 | This example creates a Form with two text inputs and a submit button. 36 | On submit, it will print the values and wait 3 seconds (to simulate a server call) before calling the callback. 37 | 38 | ```javascript 39 | function submit(values, callback) { 40 | console.log('submitted: ', values); 41 | setTimeout(callback, 3000); 42 | } 43 | 44 | const BasicExample = () => ( 45 |
46 |
47 | 48 | 49 |
50 |
51 | 52 |
53 |
54 | ); 55 | ``` 56 | -------------------------------------------------------------------------------- /documentation/components/hidden-field.md: -------------------------------------------------------------------------------- 1 | # HiddenField 2 | 3 | This field will not be rendered to the user, but it will show up in the `Form`'s values. 4 | Useful for including values the user doesn't need to know about. 5 | 6 | ```javascript 7 | const Example = () => ( 8 |
9 | 10 | 11 | ); 12 | ``` 13 | 14 | ## Props 15 | - `name` (string, required): This will map to the key in the form 16 | - `value` (any, required): This is the value field 17 | -------------------------------------------------------------------------------- /documentation/components/radio-group.md: -------------------------------------------------------------------------------- 1 | # RadioGroup 2 | `RadioGroup` is a group of radio buttons where only one can be active at a time. 3 | 4 | ```javascript 5 | const radioGroupOptions = [ 6 | {value: 'radio-group-1', label: 'Radio group 1'}, 7 | {value: 'radio-group-2', label: 'Radio group 2'} 8 | ]; 9 | 10 | const RadioGroupExample = () => ( 11 |
{}}> 12 | 14 | 17 | 18 | ); 19 | ``` 20 | 21 | ## Props 22 | - `name` (string, required): This will map to the key in the form 23 | - `label` (string): This will be displayed above the radios 24 | - `options` (Array): An array of objects containing a value and a label, 25 | - `required` (bool): Will tell the field to display a required star next to the label 26 | - `validator` (func): a [validator](../validation/validation.md) function, 27 | - `value` (string): This is the default value of the field 28 | -------------------------------------------------------------------------------- /documentation/components/single-select.md: -------------------------------------------------------------------------------- 1 | # SingleSelect 2 | `SingleSelect` is a dropdown component that allows you to select a value in a list of choices. 3 | 4 | ```javascript 5 | const options = [ 6 | { 7 | value: 'option-1', 8 | label: 'Option 1' 9 | }, 10 | { 11 | value: 'option-2', 12 | label: 'Option 2' 13 | }, 14 | { 15 | value: 'option-3' 16 | }, 17 | ]; 18 | 19 | const SingleSelectExample = () => ( 20 |
{}}> 21 | 22 | 27 | 28 | ); 29 | ``` 30 | 31 | ## Props 32 | - `name` (string, required): This will map to the key in the form 33 | - `label` (string): This will be displayed near the field 34 | - `placeholder` (string): Text to show the user before a value exists 35 | - `options` (Array): An array of objects containing a value and a label 36 | - `required` (bool): Will tell the field to display a required star next to the label 37 | - `value` (string): This is the default value of the field 38 | - `validator` (func): a [validator](../validation/validation.md) function 39 | -------------------------------------------------------------------------------- /documentation/components/submit-button.md: -------------------------------------------------------------------------------- 1 | # SubmitButton 2 | 3 | This component will allow you to submit the `Form` easily. 4 | When the `SubmitButton` is clicked, it tell the `Form` to submit. 5 | 6 | ```javascript 7 | const Example = () => ( 8 |
9 | 13 | 14 | ); 15 | ``` 16 | 17 | ## Props 18 | `SubmitButton` takes three props. 19 | Each prop tells the button what it should display depending on the form's submission state. 20 | -------------------------------------------------------------------------------- /documentation/components/text-area.md: -------------------------------------------------------------------------------- 1 | # TextArea 2 | `TextArea` is a field that accepts multiple lines of text. 3 | 4 | ```javascript 5 | const TextAreaExample = () => ( 6 |
{}}> 7 |