a-plus-forms
Advanced tools
Comparing version 0.6.2 to 0.7.0
1671
dist/index.js
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } | ||
var React = _interopDefault(require('react')); | ||
var PropTypes = _interopDefault(require('prop-types')); | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { | ||
return typeof obj; | ||
} : function (obj) { | ||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; | ||
}; | ||
var classCallCheck = function (instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
}; | ||
var createClass = function () { | ||
function defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
} | ||
return function (Constructor, protoProps, staticProps) { | ||
if (protoProps) defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
}; | ||
}(); | ||
var defineProperty = function (obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
}; | ||
var _extends = Object.assign || function (target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i]; | ||
for (var key in source) { | ||
if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
return target; | ||
}; | ||
var inherits = function (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 objectWithoutProperties = function (obj, keys) { | ||
var target = {}; | ||
for (var i in obj) { | ||
if (keys.indexOf(i) >= 0) continue; | ||
if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; | ||
target[i] = obj[i]; | ||
} | ||
return target; | ||
}; | ||
var possibleConstructorReturn = function (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; | ||
}; | ||
var toConsumableArray = function (arr) { | ||
if (Array.isArray(arr)) { | ||
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; | ||
return arr2; | ||
} else { | ||
return Array.from(arr); | ||
} | ||
}; | ||
var DefaultLayout = function DefaultLayout(_ref) { | ||
var input = _ref.input, | ||
label = _ref.label, | ||
error = _ref.error; | ||
return React.createElement( | ||
'div', | ||
null, | ||
label ? React.createElement( | ||
'label', | ||
null, | ||
label | ||
) : null, | ||
React.createElement( | ||
'div', | ||
null, | ||
input | ||
), | ||
error ? React.createElement( | ||
'small', | ||
null, | ||
error | ||
) : null | ||
); | ||
}; | ||
// just a dummy validator for simple function based validators | ||
var DefaultValidator = function () { | ||
function DefaultValidator(schema) { | ||
classCallCheck(this, DefaultValidator); | ||
this.schema = schema; | ||
} | ||
createClass(DefaultValidator, [{ | ||
key: 'errorsFor', | ||
value: function errorsFor(data) { | ||
return this.schema && this.schema(data); | ||
} | ||
}]); | ||
return DefaultValidator; | ||
}(); | ||
// default form layout | ||
var FormLayout = function FormLayout(_ref2) { | ||
var error = _ref2.error, | ||
input = _ref2.input; | ||
return !error ? input : React.cloneElement(input, { | ||
error: React.createElement( | ||
'small', | ||
{ className: 'error' }, | ||
error | ||
) | ||
}); | ||
}; | ||
var config = { | ||
DefaultValidator: DefaultValidator, | ||
DefaultLayout: DefaultLayout, | ||
FormLayout: FormLayout | ||
}; | ||
var _class; | ||
var _temp; | ||
/** | ||
* This is the actual layout strategy component | ||
*/ | ||
var LayoutHandler = (_temp = _class = function (_React$Component) { | ||
inherits(LayoutHandler, _React$Component); | ||
function LayoutHandler() { | ||
classCallCheck(this, LayoutHandler); | ||
return possibleConstructorReturn(this, (LayoutHandler.__proto__ || Object.getPrototypeOf(LayoutHandler)).apply(this, arguments)); | ||
} | ||
createClass(LayoutHandler, [{ | ||
key: 'inputProps', | ||
/** | ||
* Calculates the actual Input props | ||
* an input receives all the props except the layout ones | ||
*/ | ||
value: function inputProps() { | ||
var _props$props = this.props.props, | ||
label = _props$props.label, | ||
layout = _props$props.layout, | ||
rest = objectWithoutProperties(_props$props, ['label', 'layout']); // eslint-disable-line | ||
return rest; | ||
} | ||
/** | ||
* Calculates the actual layout props | ||
* a layout receives all the same props except id/className | ||
* the class name goes into the input field by default | ||
*/ | ||
}, { | ||
key: 'layoutProps', | ||
value: function layoutProps() { | ||
var _props = this.props, | ||
_props$props2 = _props.props, | ||
id = _props$props2.id, | ||
className = _props$props2.className, | ||
rest = objectWithoutProperties(_props$props2, ['id', 'className']), | ||
error = _props.error, | ||
dirty = _props.dirty; // eslint-disable-line | ||
return _extends({}, rest, { error: dirty === false ? null : error }); | ||
} | ||
// selects the right layout | ||
}, { | ||
key: 'chooseLayout', | ||
value: function chooseLayout() { | ||
var _props2 = this.props, | ||
layout = _props2.layout, | ||
props = _props2.props; | ||
var APFLayout = this.context.APFLayout; | ||
if ('layout' in props) { | ||
return props.layout || null; // individual props layout | ||
} else if (layout !== undefined) { | ||
return layout || null; // the field options layout | ||
} else if (APFLayout) { | ||
return APFLayout; // the context layout | ||
} | ||
return config.DefaultLayout; | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var Input = this.props.input; | ||
var input = React.createElement(Input, this.inputProps()); | ||
var Layout = this.chooseLayout(); | ||
if (!Layout) { | ||
if ('error' in this.props) { | ||
return [React.cloneElement(input, { key: 'input' }), React.createElement( | ||
'small', | ||
{ className: 'error', key: 'error' }, | ||
this.props.error | ||
)]; | ||
} | ||
return input; | ||
} | ||
return React.createElement(Layout, _extends({}, this.layoutProps(), { input: input })); | ||
} | ||
}]); | ||
return LayoutHandler; | ||
}(React.Component), _class.contextTypes = { | ||
APFLayout: PropTypes.any | ||
}, _temp); | ||
/* eslint no-use-before-define: off */ | ||
var StateManager = function () { | ||
function StateManager(element) { | ||
classCallCheck(this, StateManager); | ||
this.listFields = []; | ||
this.element = element; | ||
} | ||
// actual set value that allows to swtich off onChange data propagation | ||
createClass(StateManager, [{ | ||
key: 'setValue', | ||
value: function setValue(value) { | ||
var propagate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; | ||
if (this.element.isUnmounted) return; | ||
var _element$props = this.element.props, | ||
name = _element$props.name, | ||
onChange = _element$props.onChange; | ||
var parent = this.element.context.APFState; | ||
if (parent !== undefined && name !== undefined) { | ||
var parentValue = parent.getValue() || {}; | ||
if (parentValue[name] !== value) { | ||
if (propagate) onChange(value); | ||
var newValue = _extends({}, parentValue, defineProperty({}, name, value)); | ||
parent.setValue(Object.freeze(newValue), propagate); | ||
} | ||
} else if (parent !== undefined && parent.isArray) { | ||
var index = parent.getIndexFor(this.element); | ||
var _parentValue = parent.getValue() || []; | ||
if (_parentValue[index] !== value) { | ||
if (propagate) onChange(value); | ||
var _newValue = [].concat(toConsumableArray(_parentValue.slice(0, index)), [value], toConsumableArray(_parentValue.slice(index + 1))); | ||
parent.setValue(Object.freeze(_newValue)); | ||
} | ||
} else if (this.currentValue !== value) { | ||
this.currentValue = Object.freeze(value); | ||
this.element.forceUpdate(); | ||
if (propagate) onChange(value); | ||
} | ||
} | ||
}, { | ||
key: 'getValue', | ||
value: function getValue() { | ||
var name = this.element.props.name; | ||
var parent = this.element.context.APFState; | ||
if (parent !== undefined && name !== undefined) { | ||
var parentValue = parent.getValue() || {}; | ||
return parentValue[name]; | ||
} else if (parent !== undefined && parent.isArray) { | ||
var index = parent.getIndexFor(this.element); | ||
var value = parent.getValue() || []; | ||
return value[index]; | ||
} | ||
return this.currentValue; | ||
} | ||
}, { | ||
key: 'register', | ||
value: function register(field) { | ||
var name = field.props.name; | ||
var currentValue = this.getValue() || {}; | ||
if (name !== undefined && !(name in currentValue)) { | ||
this.setValue(_extends({}, currentValue, defineProperty({}, name, undefined)), false); | ||
} else if (this.isArray && !this.listFields.includes(field)) { | ||
this.listFields.push(field); | ||
} | ||
} | ||
}, { | ||
key: 'unregister', | ||
value: function unregister(field) { | ||
var name = field.props.name; | ||
if (name !== undefined) { | ||
var newValue = _extends({}, this.getValue()); | ||
delete newValue[name]; | ||
this.setValue(Object.freeze(newValue)); | ||
} else if (this.isArray) { | ||
var index = this.getIndexFor(field); | ||
if (index > -1) this.listFields.splice(index, 1); | ||
} | ||
} | ||
// private | ||
}, { | ||
key: 'getIndexFor', | ||
value: function getIndexFor(field) { | ||
return this.listFields.indexOf(field); | ||
} | ||
}, { | ||
key: 'isArray', | ||
get: function get$$1() { | ||
var options = this.element.constructor.fieldOptions; | ||
return options.array === true; | ||
} | ||
}]); | ||
return StateManager; | ||
}(); | ||
function _extendableBuiltin(cls) { | ||
function ExtendableBuiltin() { | ||
var instance = Reflect.construct(cls, Array.from(arguments)); | ||
Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); | ||
return instance; | ||
} | ||
ExtendableBuiltin.prototype = Object.create(cls.prototype, { | ||
constructor: { | ||
value: cls, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
if (Object.setPrototypeOf) { | ||
Object.setPrototypeOf(ExtendableBuiltin, cls); | ||
} else { | ||
ExtendableBuiltin.__proto__ = cls; | ||
} | ||
return ExtendableBuiltin; | ||
} | ||
/** | ||
* A dedicated exception for third party code to trigger | ||
* validation errors in the `Form` | ||
*/ | ||
var ValidationError = function (_extendableBuiltin2) { | ||
inherits(ValidationError, _extendableBuiltin2); | ||
function ValidationError(errors) { | ||
classCallCheck(this, ValidationError); | ||
var _this = possibleConstructorReturn(this, (ValidationError.__proto__ || Object.getPrototypeOf(ValidationError)).call(this, JSON.stringify(errors))); | ||
_this.errors = errors; | ||
return _this; | ||
} | ||
return ValidationError; | ||
}(_extendableBuiltin(Error)); | ||
var ErrorsManager = function () { | ||
function ErrorsManager(element) { | ||
classCallCheck(this, ErrorsManager); | ||
this.element = element; | ||
} | ||
createClass(ErrorsManager, [{ | ||
key: 'getCurrentError', | ||
value: function getCurrentError() { | ||
var _element$context = this.element.context, | ||
_element$context$APFE = _element$context.APFError, | ||
APFError = _element$context$APFE === undefined ? {} : _element$context$APFE, | ||
APFState = _element$context.APFState; | ||
var _element$props = this.element.props, | ||
propsError = _element$props.error, | ||
name = _element$props.name; | ||
if (APFState && APFState.isArray) { | ||
var index = APFState.getIndexFor(this.element); | ||
return APFError[index.toString()]; | ||
} | ||
return propsError == null ? APFError[name] : propsError; // eslint-disable-line | ||
} | ||
}, { | ||
key: 'getErrorMessage', | ||
value: function getErrorMessage() { | ||
var options = this.element.constructor.fieldOptions; | ||
var error = this.getCurrentError(); | ||
if (error != null && typeof error !== 'string') { | ||
// eslint-disable-line | ||
var nestedErrors = error; | ||
if (options.nested || options.array) { | ||
nestedErrors = _extends({}, error); | ||
var currentValue = this.element.stateManager.getValue(); | ||
// filter out the existing nested fields | ||
var names = options.nested ? Object.keys(currentValue || {}) : (currentValue || []).map(function (_, index) { | ||
return index.toString(); | ||
}); | ||
for (var i = 0; i < names.length; i++) { | ||
delete nestedErrors[names[i]]; | ||
} | ||
} | ||
return nestedErrorToString(nestedErrors); | ||
} | ||
return error; | ||
} | ||
}]); | ||
return ErrorsManager; | ||
}(); | ||
function nestedErrorToString(errors) { | ||
var messages = Object.keys(errors).map(function (key) { | ||
var error = errors[key]; | ||
if (error && typeof error !== 'string') { | ||
error = nestedErrorToString(error); | ||
} | ||
return (key + ' ' + error).trim(); | ||
}); | ||
return humanizeMessages(messages); | ||
} | ||
function humanizeMessages(list) { | ||
var lastMessage = list.pop(); | ||
if (list.length > 0) { | ||
return list.join(', ') + ', and ' + lastMessage; | ||
} | ||
return lastMessage; | ||
} | ||
var field = (function () { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
return function (Input) { | ||
var _class, _temp; | ||
var Field = (_temp = _class = function (_React$Component) { | ||
inherits(Field, _React$Component); | ||
function Field() { | ||
classCallCheck(this, Field); | ||
var _this = possibleConstructorReturn(this, (Field.__proto__ || Object.getPrototypeOf(Field)).call(this)); | ||
_this.isUnmounted = false; | ||
_this.onChange = function (value) { | ||
_this.value = value; | ||
}; | ||
_this.stateManager = new StateManager(_this); | ||
_this.errorsManager = new ErrorsManager(_this); | ||
if (options.array) { | ||
_this.addEntry = _this.addEntry.bind(_this); | ||
_this.removeEntry = _this.removeEntry.bind(_this); | ||
} | ||
return _this; | ||
} | ||
createClass(Field, [{ | ||
key: 'getChildContext', | ||
value: function getChildContext() { | ||
var isCompound = options.nested || options.array; | ||
var error = this.errorsManager.getCurrentError(); | ||
var isNestedError = error && (typeof error === 'undefined' ? 'undefined' : _typeof(error)) === 'object'; | ||
return { | ||
APFProps: this.props, | ||
APFDirty: this.props.dirty, | ||
APFState: isCompound && this.stateManager, | ||
APFError: isCompound && isNestedError ? error : undefined | ||
}; | ||
} | ||
}, { | ||
key: 'componentWillMount', | ||
value: function componentWillMount() { | ||
this.checkForNewValueIn(this.props, true); | ||
if (this.context.APFState) { | ||
this.context.APFState.register(this); | ||
} | ||
} | ||
}, { | ||
key: 'componentWillUnmount', | ||
value: function componentWillUnmount() { | ||
this.isUnmounted = true; | ||
if (this.context.APFState) { | ||
this.context.APFState.unregister(this); | ||
} | ||
} | ||
}, { | ||
key: 'componentWillReceiveProps', | ||
value: function componentWillReceiveProps(props) { | ||
this.checkForNewValueIn(props, false); | ||
} | ||
}, { | ||
key: 'checkForNewValueIn', | ||
value: function checkForNewValueIn(props, isInitialCall) { | ||
var triggerOnChange = !isInitialCall; | ||
if ('value' in props) { | ||
this.stateManager.setValue(props.value, triggerOnChange); | ||
} else if ('defaultValue' in props) { | ||
// something was changed or an initial call | ||
if (isInitialCall || this.props.defaultValue !== props.defaultValue) { | ||
this.stateManager.setValue(props.defaultValue, triggerOnChange); | ||
} | ||
} | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var _props = this.props, | ||
defaultValue = _props.defaultValue, | ||
error = _props.error, | ||
dirty = _props.dirty, | ||
props = objectWithoutProperties(_props, ['defaultValue', 'error', 'dirty']); // eslint-disable-line | ||
Object.assign(props, { value: this.value, onChange: this.onChange }); | ||
if (options.array === true) { | ||
props.value = props.value || []; | ||
props.addEntry = this.addEntry; | ||
props.removeEntry = this.removeEntry; | ||
} | ||
return React.createElement(LayoutHandler, { | ||
input: Input, | ||
props: props, | ||
error: this.error, | ||
dirty: this.dirty, | ||
layout: options.layout | ||
}); | ||
} | ||
}, { | ||
key: 'name', | ||
get: function get$$1() { | ||
return this.props.name; | ||
} | ||
}, { | ||
key: 'value', | ||
get: function get$$1() { | ||
return this.stateManager.getValue(); | ||
}, | ||
set: function set$$1(value) { | ||
this.stateManager.setValue(value); | ||
} | ||
}, { | ||
key: 'error', | ||
get: function get$$1() { | ||
return this.errorsManager.getErrorMessage(); | ||
} | ||
}, { | ||
key: 'dirty', | ||
get: function get$$1() { | ||
var dirty = this.props.dirty; | ||
return dirty === undefined ? this.context.APFDirty : dirty; | ||
} | ||
}]); | ||
return Field; | ||
}(React.Component), _class.defaultProps = { | ||
onChange: function onChange() {} | ||
}, _class.contextTypes = { | ||
APFState: PropTypes.object, | ||
APFError: PropTypes.object, | ||
APFDirty: PropTypes.bool | ||
}, _class.childContextTypes = { | ||
APFState: PropTypes.object, // nested field anchor | ||
APFProps: PropTypes.object, // original field props, | ||
APFError: PropTypes.object, // nested field errors | ||
APFDirty: PropTypes.bool | ||
}, _class.InnerInput = Input, _class.fieldOptions = options, _temp); | ||
if (options.array) { | ||
Object.assign(Field.prototype, { | ||
addEntry: function addEntry(newItem) { | ||
var _value = this.value, | ||
value = _value === undefined ? [] : _value; | ||
this.value = value.concat(newItem); | ||
}, | ||
removeEntry: function removeEntry(index) { | ||
var _value2 = this.value, | ||
value = _value2 === undefined ? [] : _value2; | ||
this.value = [].concat(toConsumableArray(value.slice(0, index)), toConsumableArray(value.slice(index + 1))); | ||
} | ||
}); | ||
} | ||
return Field; | ||
}; | ||
}); | ||
exports.default = exports.config = exports.optionizer = exports.trimmer = exports.Radios = exports.Select = exports.Slider = exports.Checkbox = exports.Textarea = exports.HiddenInput = exports.PasswordInput = exports.NumberInput = exports.SearchInput = exports.PhoneInput = exports.EmailInput = exports.TextInput = exports.ValidatorProvider = exports.LayoutProvider = exports.Error = exports.Form = exports.field = undefined; | ||
var _field2 = require('./core/field'); | ||
var _class$1; | ||
var _temp2; | ||
var _field3 = _interopRequireDefault(_field2); | ||
// just an empty field container to hold the form state | ||
var StateContainer = field({ layout: null, nested: true })(function (_ref) { | ||
var children = _ref.children, | ||
error = _ref.error; | ||
var _form = require('./core/form'); | ||
var output = React.Children.toArray(children); | ||
var _form2 = _interopRequireDefault(_form); | ||
if (error != null) { | ||
// eslint-disable-line | ||
output.unshift(React.cloneElement(error, { key: 'errors' })); | ||
} | ||
var _error = require('./core/error'); | ||
return output; | ||
}); | ||
var isPromisish = function isPromisish(smth) { | ||
return smth && typeof smth.then === 'function' && typeof smth.catch === 'function'; | ||
}; | ||
var _error2 = _interopRequireDefault(_error); | ||
var Form = (_temp2 = _class$1 = function (_React$Component) { | ||
inherits(Form, _React$Component); | ||
var _layout = require('./providers/layout'); | ||
function Form() { | ||
var _ref2; | ||
var _layout2 = _interopRequireDefault(_layout); | ||
var _temp, _this, _ret; | ||
var _validator = require('./providers/validator'); | ||
classCallCheck(this, Form); | ||
var _validator2 = _interopRequireDefault(_validator); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
var _text = require('./inputs/text'); | ||
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref2 = Form.__proto__ || Object.getPrototypeOf(Form)).call.apply(_ref2, [this].concat(args))), _this), _this.state = { errors: null, dirty: false, disabled: false }, _this.onChange = function (value) { | ||
_this.props.onChange(value); | ||
var _text2 = _interopRequireDefault(_text); | ||
if (_this.state.dirty) { | ||
_this.validate(value); | ||
} | ||
}, _this.onSubmit = function (event) { | ||
event.preventDefault(); | ||
_this.submit(); | ||
}, _this.handleErrors = function (errors) { | ||
_this.setState({ errors: errors }); | ||
var _email = require('./inputs/email'); | ||
if (errors) { | ||
_this.props.onError(errors, _this.value); | ||
} | ||
var _email2 = _interopRequireDefault(_email); | ||
return errors; | ||
}, _this.setStateRef = function (e) { | ||
_this.stateContainer = e; | ||
}, _temp), possibleConstructorReturn(_this, _ret); | ||
} | ||
var _phone = require('./inputs/phone'); | ||
createClass(Form, [{ | ||
key: 'componentWillMount', | ||
value: function componentWillMount() { | ||
var APFValidator = this.context.APFValidator; | ||
var schema = this.props.schema; | ||
var _phone2 = _interopRequireDefault(_phone); | ||
var _search = require('./inputs/search'); | ||
this.validator = new (APFValidator || config.DefaultValidator)(schema); | ||
} | ||
}, { | ||
key: 'componentWillReceiveProps', | ||
value: function componentWillReceiveProps(props) { | ||
var schema = props.schema; | ||
var _search2 = _interopRequireDefault(_search); | ||
var _number = require('./inputs/number'); | ||
this.validator.schema = schema; | ||
} | ||
}, { | ||
key: 'waitForServerResponse', | ||
value: function waitForServerResponse(request) { | ||
var _this2 = this; | ||
var _number2 = _interopRequireDefault(_number); | ||
this.setState({ disabled: true }); | ||
var _password = require('./inputs/password'); | ||
request.then(function () { | ||
_this2.setState({ disabled: false }); | ||
}).catch(function (error) { | ||
_this2.setState({ disabled: false }); | ||
var _password2 = _interopRequireDefault(_password); | ||
if (error instanceof ValidationError) { | ||
_this2.handleErrors(error.errors); | ||
} else { | ||
throw error; | ||
} | ||
}); | ||
} | ||
}, { | ||
key: 'validate', | ||
value: function validate() { | ||
var value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.value; | ||
var _hidden = require('./inputs/hidden'); | ||
var errors = this.validator.errorsFor(value); | ||
var _hidden2 = _interopRequireDefault(_hidden); | ||
if (isPromisish(errors)) { | ||
return errors.then(this.handleErrors); | ||
} | ||
var _textarea = require('./inputs/textarea'); | ||
this.handleErrors(errors); | ||
var _textarea2 = _interopRequireDefault(_textarea); | ||
return { then: function then(cb) { | ||
return cb(errors); | ||
} }; | ||
} | ||
}, { | ||
key: 'submit', | ||
value: function submit() { | ||
var _this3 = this; | ||
var _checkbox = require('./inputs/checkbox'); | ||
this.validate().then(function (errors) { | ||
_this3.setState({ dirty: true }); | ||
var _checkbox2 = _interopRequireDefault(_checkbox); | ||
if (!errors) { | ||
var result = _this3.props.onSubmit(_this3.value); | ||
if (isPromisish(result)) { | ||
_this3.waitForServerResponse(result); | ||
} | ||
} | ||
}); | ||
} | ||
}, { | ||
key: 'reset', | ||
value: function reset() { | ||
this.setState({ dirty: false }); | ||
this.value = this.props.defaultValue || {}; | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var _props = this.props, | ||
children = _props.children, | ||
defaultValue = _props.defaultValue, | ||
className = _props.className; | ||
var _state = this.state, | ||
errors = _state.errors, | ||
dirty = _state.dirty, | ||
disabled = _state.disabled; | ||
var _slider = require('./inputs/slider'); | ||
var _slider2 = _interopRequireDefault(_slider); | ||
return React.createElement( | ||
'form', | ||
{ onSubmit: this.onSubmit, className: className, noValidate: true, disabled: disabled }, | ||
React.createElement( | ||
StateContainer, | ||
{ | ||
dirty: dirty, | ||
error: errors, | ||
defaultValue: defaultValue, | ||
onChange: this.onChange, | ||
layout: config.FormLayout, | ||
ref: this.setStateRef | ||
}, | ||
children | ||
) | ||
); | ||
} | ||
}, { | ||
key: 'value', | ||
get: function get$$1() { | ||
return this.stateContainer.value; | ||
}, | ||
set: function set$$1(data) { | ||
this.stateContainer.value = data; | ||
} | ||
}]); | ||
return Form; | ||
}(React.Component), _class$1.contextTypes = { | ||
APFValidator: PropTypes.func | ||
}, _class$1.defaultProps = { | ||
onSubmit: function onSubmit() {}, | ||
onChange: function onChange() {}, | ||
onError: function onError() {}, | ||
schema: undefined, | ||
defaultValue: {} | ||
}, _temp2); | ||
var _select = require('./inputs/select'); | ||
var _class$2; | ||
var _temp$1; | ||
var _select2 = _interopRequireDefault(_select); | ||
/** | ||
* This is the standard interface to feed different field | ||
* layouts into the forms in different contexts | ||
*/ | ||
var LayoutProvider = (_temp$1 = _class$2 = function (_React$Component) { | ||
inherits(LayoutProvider, _React$Component); | ||
var _radios = require('./inputs/radios'); | ||
function LayoutProvider() { | ||
classCallCheck(this, LayoutProvider); | ||
return possibleConstructorReturn(this, (LayoutProvider.__proto__ || Object.getPrototypeOf(LayoutProvider)).apply(this, arguments)); | ||
} | ||
var _radios2 = _interopRequireDefault(_radios); | ||
createClass(LayoutProvider, [{ | ||
key: 'getChildContext', | ||
value: function getChildContext() { | ||
return { APFLayout: this.props.layout }; | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
return this.props.children; | ||
} | ||
}]); | ||
return LayoutProvider; | ||
}(React.Component), _class$2.childContextTypes = { | ||
APFLayout: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired | ||
}, _temp$1); | ||
var _trimmer2 = require('./utils/trimmer'); | ||
var _class$3; | ||
var _temp$2; | ||
var _trimmer3 = _interopRequireDefault(_trimmer2); | ||
var ValidatorProvider = (_temp$2 = _class$3 = function (_React$Component) { | ||
inherits(ValidatorProvider, _React$Component); | ||
var _optionizer2 = require('./utils/optionizer'); | ||
function ValidatorProvider() { | ||
classCallCheck(this, ValidatorProvider); | ||
return possibleConstructorReturn(this, (ValidatorProvider.__proto__ || Object.getPrototypeOf(ValidatorProvider)).apply(this, arguments)); | ||
} | ||
var _optionizer3 = _interopRequireDefault(_optionizer2); | ||
createClass(ValidatorProvider, [{ | ||
key: 'getChildContext', | ||
value: function getChildContext() { | ||
return { | ||
APFValidator: this.props.validator | ||
}; | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
return this.props.children; | ||
} | ||
}]); | ||
return ValidatorProvider; | ||
}(React.Component), _class$3.childContextTypes = { | ||
APFValidator: PropTypes.func.isRequired | ||
}, _temp$2); | ||
var _config2 = require('./config'); | ||
var trimmer = (function () { | ||
return function (TextInput) { | ||
return function (_React$Component) { | ||
inherits(Trimmer, _React$Component); | ||
var _config3 = _interopRequireDefault(_config2); | ||
function Trimmer() { | ||
var _ref; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var _temp, _this, _ret; | ||
exports.field = _field3.default; | ||
exports.Form = _form2.default; | ||
exports.Error = _error2.default; | ||
exports.LayoutProvider = _layout2.default; | ||
exports.ValidatorProvider = _validator2.default; | ||
exports.TextInput = _text2.default; | ||
exports.EmailInput = _email2.default; | ||
exports.PhoneInput = _phone2.default; | ||
exports.SearchInput = _search2.default; | ||
exports.NumberInput = _number2.default; | ||
exports.PasswordInput = _password2.default; | ||
exports.HiddenInput = _hidden2.default; | ||
exports.Textarea = _textarea2.default; | ||
exports.Checkbox = _checkbox2.default; | ||
exports.Slider = _slider2.default; | ||
exports.Select = _select2.default; | ||
exports.Radios = _radios2.default; | ||
exports.trimmer = _trimmer3.default; | ||
exports.optionizer = _optionizer3.default; | ||
exports.config = _config3.default; | ||
exports.default = _config3.default; | ||
classCallCheck(this, Trimmer); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = Trimmer.__proto__ || Object.getPrototypeOf(Trimmer)).call.apply(_ref, [this].concat(args))), _this), _this.onChange = function (event) { | ||
_this.rawValue = event.target.value; | ||
var trimmedValue = _this.rawValue.trim(); | ||
if (_this.props.value !== trimmedValue) { | ||
_this.props.onChange(trimmedValue); | ||
} | ||
_this.forceUpdate(); | ||
}, _this.rawValue = '', _temp), possibleConstructorReturn(_this, _ret); | ||
} | ||
createClass(Trimmer, [{ | ||
key: 'componentWillMount', | ||
value: function componentWillMount() { | ||
this.rawValue = this.props.value || ''; | ||
} | ||
}, { | ||
key: 'componentWillReceiveProps', | ||
value: function componentWillReceiveProps(props) { | ||
if (props.value !== this.rawValue.trim()) { | ||
this.rawValue = props.value == null ? '' : props.value; | ||
this.forceUpdate(); | ||
} | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
return React.createElement(TextInput, _extends({}, this.props, { value: this.rawValue, onChange: this.onChange })); | ||
} | ||
}]); | ||
return Trimmer; | ||
}(React.Component); | ||
}; | ||
}); | ||
var _dec; | ||
var _dec2; | ||
var _class$4; | ||
var TextInput = (_dec = field(), _dec2 = trimmer(), _dec(_class$4 = _dec2(_class$4 = function (_React$Component) { | ||
inherits(TextInput, _React$Component); | ||
function TextInput() { | ||
classCallCheck(this, TextInput); | ||
return possibleConstructorReturn(this, (TextInput.__proto__ || Object.getPrototypeOf(TextInput)).apply(this, arguments)); | ||
} | ||
createClass(TextInput, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _props = this.props, | ||
_props$type = _props.type, | ||
type = _props$type === undefined ? 'text' : _props$type, | ||
rest = objectWithoutProperties(_props, ['type']); | ||
return React.createElement('input', _extends({ type: type }, rest)); | ||
} | ||
}]); | ||
return TextInput; | ||
}(React.Component)) || _class$4) || _class$4); | ||
var _dec$1; | ||
var _class$5; | ||
var EmailInput = (_dec$1 = field(), _dec$1(_class$5 = function (_React$Component) { | ||
inherits(EmailInput, _React$Component); | ||
function EmailInput() { | ||
classCallCheck(this, EmailInput); | ||
return possibleConstructorReturn(this, (EmailInput.__proto__ || Object.getPrototypeOf(EmailInput)).apply(this, arguments)); | ||
} | ||
createClass(EmailInput, [{ | ||
key: 'render', | ||
value: function render() { | ||
return React.createElement(TextInput, _extends({}, this.props, { type: 'email', layout: null })); | ||
} | ||
}]); | ||
return EmailInput; | ||
}(React.Component)) || _class$5); | ||
var _dec$2; | ||
var _class$6; | ||
var PhoneInput = (_dec$2 = field(), _dec$2(_class$6 = function (_React$Component) { | ||
inherits(PhoneInput, _React$Component); | ||
function PhoneInput() { | ||
classCallCheck(this, PhoneInput); | ||
return possibleConstructorReturn(this, (PhoneInput.__proto__ || Object.getPrototypeOf(PhoneInput)).apply(this, arguments)); | ||
} | ||
createClass(PhoneInput, [{ | ||
key: 'render', | ||
value: function render() { | ||
return React.createElement(TextInput, _extends({}, this.props, { type: 'tel', layout: null })); | ||
} | ||
}]); | ||
return PhoneInput; | ||
}(React.Component)) || _class$6); | ||
var _dec$3; | ||
var _class$7; | ||
var SearchInput = (_dec$3 = field(), _dec$3(_class$7 = function (_React$Component) { | ||
inherits(SearchInput, _React$Component); | ||
function SearchInput() { | ||
classCallCheck(this, SearchInput); | ||
return possibleConstructorReturn(this, (SearchInput.__proto__ || Object.getPrototypeOf(SearchInput)).apply(this, arguments)); | ||
} | ||
createClass(SearchInput, [{ | ||
key: 'render', | ||
value: function render() { | ||
return React.createElement(TextInput, _extends({}, this.props, { type: 'search', layout: null })); | ||
} | ||
}]); | ||
return SearchInput; | ||
}(React.Component)) || _class$7); | ||
var _dec$4; | ||
var _class$8; | ||
var NumberInput = (_dec$4 = field(), _dec$4(_class$8 = function (_React$Component) { | ||
inherits(NumberInput, _React$Component); | ||
function NumberInput() { | ||
var _ref; | ||
var _temp, _this, _ret; | ||
classCallCheck(this, NumberInput); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = NumberInput.__proto__ || Object.getPrototypeOf(NumberInput)).call.apply(_ref, [this].concat(args))), _this), _this.onChange = function (value) { | ||
_this.props.onChange(parseFloat(value)); | ||
}, _temp), possibleConstructorReturn(_this, _ret); | ||
} | ||
createClass(NumberInput, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _props = this.props, | ||
_props$value = _props.value, | ||
value = _props$value === undefined ? 0 : _props$value, | ||
rest = objectWithoutProperties(_props, ['value']); | ||
return React.createElement(TextInput, _extends({}, rest, { type: 'number', layout: null, value: '' + value, onChange: this.onChange })); | ||
} | ||
}]); | ||
return NumberInput; | ||
}(React.Component)) || _class$8); | ||
var _dec$5; | ||
var _class$9; | ||
var PasswordInput = (_dec$5 = field(), _dec$5(_class$9 = function (_React$Component) { | ||
inherits(PasswordInput, _React$Component); | ||
function PasswordInput() { | ||
var _ref; | ||
var _temp, _this, _ret; | ||
classCallCheck(this, PasswordInput); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = PasswordInput.__proto__ || Object.getPrototypeOf(PasswordInput)).call.apply(_ref, [this].concat(args))), _this), _this.onChange = function (event) { | ||
_this.props.onChange(event.target.value); | ||
}, _temp), possibleConstructorReturn(_this, _ret); | ||
} | ||
createClass(PasswordInput, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _props = this.props, | ||
value = _props.value, | ||
rest = objectWithoutProperties(_props, ['value']); | ||
return React.createElement('input', _extends({ type: 'password' }, rest, { value: value || '', onChange: this.onChange })); | ||
} | ||
}]); | ||
return PasswordInput; | ||
}(React.Component)) || _class$9); | ||
var _dec$6; | ||
var _class$10; | ||
var HiddenInput = (_dec$6 = field({ layout: false }), _dec$6(_class$10 = function (_React$Component) { | ||
inherits(HiddenInput, _React$Component); | ||
function HiddenInput() { | ||
var _ref; | ||
var _temp, _this, _ret; | ||
classCallCheck(this, HiddenInput); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = HiddenInput.__proto__ || Object.getPrototypeOf(HiddenInput)).call.apply(_ref, [this].concat(args))), _this), _this.onChange = function (event) { | ||
_this.props.onChange(event.target.value); | ||
}, _temp), possibleConstructorReturn(_this, _ret); | ||
} | ||
createClass(HiddenInput, [{ | ||
key: 'render', | ||
value: function render() { | ||
return React.createElement('input', { type: 'hidden', value: this.props.value, onChange: this.onChange }); | ||
} | ||
}]); | ||
return HiddenInput; | ||
}(React.Component)) || _class$10); | ||
var _dec$7; | ||
var _dec2$1; | ||
var _class$11; | ||
/* eslint react/no-unused-prop-types: off */ | ||
var Textarea = (_dec$7 = field(), _dec2$1 = trimmer(), _dec$7(_class$11 = _dec2$1(_class$11 = function (_React$Component) { | ||
inherits(Textarea, _React$Component); | ||
function Textarea() { | ||
classCallCheck(this, Textarea); | ||
return possibleConstructorReturn(this, (Textarea.__proto__ || Object.getPrototypeOf(Textarea)).apply(this, arguments)); | ||
} | ||
createClass(Textarea, [{ | ||
key: 'render', | ||
value: function render() { | ||
return React.createElement('textarea', this.props); | ||
} | ||
}]); | ||
return Textarea; | ||
}(React.Component)) || _class$11) || _class$11); | ||
var _dec$8; | ||
var _class$12; | ||
var _class2; | ||
var _temp2$1; | ||
var Checkbox = (_dec$8 = field(), _dec$8(_class$12 = (_temp2$1 = _class2 = function (_React$Component) { | ||
inherits(Checkbox, _React$Component); | ||
function Checkbox() { | ||
var _ref; | ||
var _temp, _this, _ret; | ||
classCallCheck(this, Checkbox); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = Checkbox.__proto__ || Object.getPrototypeOf(Checkbox)).call.apply(_ref, [this].concat(args))), _this), _this.onChange = function (event) { | ||
_this.props.onChange(!!event.target.checked); | ||
}, _temp), possibleConstructorReturn(_this, _ret); | ||
} | ||
createClass(Checkbox, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _props = this.props, | ||
value = _props.value, | ||
disabled = _props.disabled, | ||
rest = objectWithoutProperties(_props, ['value', 'disabled']); | ||
var _ref2 = this.context.APFProps || {}, | ||
checked = _ref2.checked, | ||
label = _ref2.label; | ||
var isChecked = (value !== undefined ? value : checked) === true; | ||
var input = React.createElement('input', _extends({}, rest, { | ||
type: 'checkbox', | ||
checked: isChecked, | ||
onChange: this.onChange, | ||
disabled: disabled | ||
})); | ||
if (!label) return input; | ||
return React.createElement( | ||
'label', | ||
{ disabled: disabled }, | ||
input, | ||
label && React.createElement( | ||
'span', | ||
null, | ||
label | ||
) | ||
); | ||
} | ||
}]); | ||
return Checkbox; | ||
}(React.Component), _class2.contextTypes = { | ||
APFProps: PropTypes.object | ||
}, _temp2$1)) || _class$12); | ||
var _dec$9; | ||
var _class$13; | ||
var Slider = (_dec$9 = field(), _dec$9(_class$13 = function (_React$Component) { | ||
inherits(Slider, _React$Component); | ||
function Slider() { | ||
var _ref; | ||
var _temp, _this, _ret; | ||
classCallCheck(this, Slider); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = Slider.__proto__ || Object.getPrototypeOf(Slider)).call.apply(_ref, [this].concat(args))), _this), _this.onChange = function (event) { | ||
_this.props.onChange(parseFloat(event.target.value)); | ||
}, _temp), possibleConstructorReturn(_this, _ret); | ||
} | ||
createClass(Slider, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _props = this.props, | ||
_props$min = _props.min, | ||
min = _props$min === undefined ? 0 : _props$min, | ||
_props$max = _props.max, | ||
max = _props$max === undefined ? 10 : _props$max, | ||
_props$step = _props.step, | ||
step = _props$step === undefined ? 1 : _props$step, | ||
_props$value = _props.value, | ||
value = _props$value === undefined ? 0 : _props$value, | ||
rest = objectWithoutProperties(_props, ['min', 'max', 'step', 'value']); | ||
return React.createElement('input', _extends({}, rest, { | ||
type: 'range', | ||
min: min, | ||
max: max, | ||
step: step, | ||
value: value, | ||
onChange: this.onChange, | ||
onMouseUp: this.onChange | ||
})); | ||
} | ||
}]); | ||
return Slider; | ||
}(React.Component)) || _class$13); | ||
/** | ||
* So, the select options come in all sorts of shapes and sizes | ||
* and the role of this module is to normalize all this madness | ||
* and turn it into simplified `value -> label` list for the actual | ||
* inputs to consume | ||
* | ||
* Supported options: | ||
* 1. Array<{ label: string, value: any }> | ||
* 2. { [string(value)]: string (label) } | ||
* 3. Array<{ name: string, ....}> | ||
* 4. Array<string> | ||
* 5. Array<number> | ||
* 6. Array<any> (JSON.stringified to create a label) | ||
* | ||
* NOTE: in cases 3, 4, 5, 6 the individual entries of the arrays | ||
* are treated as the actual values that will be sent in the | ||
* `onChange` handler | ||
* | ||
* NOTE: if the input receives the `multiple` prop, then this input | ||
* will assume values as lists | ||
* | ||
* NOTE: in case of a `multiple` select, if no items is selected, | ||
* then the `onChange` event will receive *an empty list* as the value | ||
*/ | ||
var optionizer = (function () { | ||
return function (Input) { | ||
return function (_React$Component) { | ||
inherits(Optionizer, _React$Component); | ||
function Optionizer() { | ||
var _ref; | ||
var _temp, _this, _ret; | ||
classCallCheck(this, Optionizer); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = Optionizer.__proto__ || Object.getPrototypeOf(Optionizer)).call.apply(_ref, [this].concat(args))), _this), _this.state = { options: [] }, _this.originalOptions = [], _this.onChange = function (value) { | ||
var _this$props = _this.props, | ||
onChange = _this$props.onChange, | ||
multiple = _this$props.multiple; | ||
if (multiple) { | ||
onChange((Array.isArray(value) ? value : []).map(function (value) { | ||
return _this.pseudoToOriginalValue(value); | ||
})); | ||
} else { | ||
onChange(_this.pseudoToOriginalValue(value)); | ||
} | ||
}, _temp), possibleConstructorReturn(_this, _ret); | ||
} | ||
createClass(Optionizer, [{ | ||
key: 'componentWillMount', | ||
value: function componentWillMount() { | ||
this.componentWillReceiveProps(this.props); | ||
} | ||
}, { | ||
key: 'componentWillReceiveProps', | ||
value: function componentWillReceiveProps(props) { | ||
if (props.options !== this.originalOptions) { | ||
this.setState({ options: this.buildPseudoOptions(props) }); | ||
this.originalOptions = props.options; | ||
} | ||
} | ||
}, { | ||
key: 'buildPseudoOptions', | ||
value: function buildPseudoOptions(props) { | ||
return this.normalizedOptions(props).map(function (option, index) { | ||
if ((typeof option === 'undefined' ? 'undefined' : _typeof(option)) === 'object' && typeof option.label === 'string' && typeof option.value === 'string') { | ||
return { label: option.label, value: option.value, disabled: option.disabled }; | ||
} else if ((typeof option === 'undefined' ? 'undefined' : _typeof(option)) === 'object' && typeof option.name === 'string') { | ||
return { label: option.name, value: 'v-' + index, disabled: option.disabled }; | ||
} else if (typeof option === 'string') { | ||
return { label: option, value: option }; | ||
} | ||
return { label: JSON.stringify(option).substr(0, 32), value: 'v-' + index }; | ||
}); | ||
} | ||
}, { | ||
key: 'normalizedOptions', | ||
value: function normalizedOptions(props) { | ||
var _ref2 = props || this.props, | ||
_ref2$options = _ref2.options, | ||
originalOptions = _ref2$options === undefined ? [] : _ref2$options; | ||
return Array.isArray(originalOptions) ? originalOptions : Object.keys(originalOptions).map(function (value) { | ||
return { value: value, label: originalOptions[value] }; | ||
}); | ||
} | ||
}, { | ||
key: 'pseudoToOriginalValue', | ||
value: function pseudoToOriginalValue(value) { | ||
if (!this.originalOptions) return value; // autocompleter mode | ||
var options = this.normalizedOptions(); | ||
var option = this.state.options.reduce(function (current, option, index) { | ||
return option.value === value ? options[index] : current; | ||
}, null); | ||
return option && option.label && option.value !== undefined ? option.value : option; | ||
} | ||
}, { | ||
key: 'originalToPseudoValue', | ||
value: function originalToPseudoValue(value) { | ||
if (!this.originalOptions) return value; // autocompleter mode | ||
var options = this.normalizedOptions(); | ||
var currentOption = options.find(function (option) { | ||
return option.label && option.value === value || option === value; | ||
}); | ||
var currentIndex = currentOption ? options.indexOf(currentOption) : -1; | ||
var pseudoEntry = this.state.options[currentIndex]; | ||
var pseudoValue = pseudoEntry && pseudoEntry.value; | ||
return pseudoValue; | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var _this2 = this; | ||
var _props = this.props, | ||
value = _props.value, | ||
multiple = _props.multiple; | ||
var pseudoValue = multiple ? (Array.isArray(value) ? value : []).map(function (value) { | ||
return _this2.originalToPseudoValue(value); | ||
}) : this.originalToPseudoValue(value); | ||
return React.createElement(Input, _extends({}, this.props, { | ||
value: pseudoValue, | ||
options: this.state.options, | ||
onChange: this.onChange | ||
})); | ||
} | ||
}]); | ||
return Optionizer; | ||
}(React.Component); | ||
}; | ||
}); | ||
var _dec$10; | ||
var _dec2$2; | ||
var _class$14; | ||
var Select = (_dec$10 = field(), _dec2$2 = optionizer(), _dec$10(_class$14 = _dec2$2(_class$14 = function (_React$Component) { | ||
inherits(Select, _React$Component); | ||
function Select() { | ||
var _ref; | ||
var _temp, _this, _ret; | ||
classCallCheck(this, Select); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = Select.__proto__ || Object.getPrototypeOf(Select)).call.apply(_ref, [this].concat(args))), _this), _this.onChange = function (event) { | ||
var _this$props = _this.props, | ||
multiple = _this$props.multiple, | ||
onChange = _this$props.onChange; | ||
if (multiple) { | ||
var _options = _this.selectRef.querySelectorAll('option'); | ||
var values = []; | ||
_options.forEach(function (option) { | ||
if (option.selected) { | ||
values.push(option.value); | ||
} | ||
}); | ||
onChange(values); | ||
} else { | ||
onChange(event.target.value); | ||
} | ||
}, _this.saveRef = function (element) { | ||
_this.selectRef = element; | ||
}, _temp), possibleConstructorReturn(_this, _ret); | ||
} | ||
createClass(Select, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _props = this.props, | ||
value = _props.value, | ||
_props$options = _props.options, | ||
options = _props$options === undefined ? [] : _props$options, | ||
rest = objectWithoutProperties(_props, ['value', 'options']); | ||
return React.createElement( | ||
'select', | ||
_extends({}, rest, { value: value, onChange: this.onChange, ref: this.saveRef }), | ||
options.map(function (_ref2, i) { | ||
var label = _ref2.label, | ||
value = _ref2.value, | ||
disabled = _ref2.disabled; | ||
return React.createElement( | ||
'option', | ||
{ key: i, value: value, disabled: disabled }, | ||
label | ||
); | ||
}) | ||
); | ||
} | ||
}]); | ||
return Select; | ||
}(React.Component)) || _class$14) || _class$14); | ||
var _dec$11; | ||
var _dec2$3; | ||
var _class$15; | ||
var Radios = (_dec$11 = field(), _dec2$3 = optionizer(), _dec$11(_class$15 = _dec2$3(_class$15 = function (_React$Component) { | ||
inherits(Radios, _React$Component); | ||
function Radios() { | ||
var _ref; | ||
var _temp, _this, _ret; | ||
classCallCheck(this, Radios); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = Radios.__proto__ || Object.getPrototypeOf(Radios)).call.apply(_ref, [this].concat(args))), _this), _this.onChange = function (event) { | ||
_this.props.onChange(event.target.value); | ||
}, _temp), possibleConstructorReturn(_this, _ret); | ||
} | ||
createClass(Radios, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _this2 = this; | ||
var _props = this.props, | ||
currentValue = _props.value, | ||
_props$options = _props.options, | ||
options = _props$options === undefined ? [] : _props$options, | ||
name = _props.name; | ||
return React.createElement( | ||
'div', | ||
null, | ||
options.map(function (_ref2, i) { | ||
var label = _ref2.label, | ||
value = _ref2.value, | ||
disabled = _ref2.disabled; | ||
return React.createElement( | ||
'label', | ||
{ key: i, disabled: disabled }, | ||
React.createElement('input', { | ||
type: 'radio', | ||
name: name, | ||
value: value, | ||
onChange: _this2.onChange, | ||
checked: value === currentValue | ||
}), | ||
React.createElement( | ||
'span', | ||
null, | ||
label | ||
) | ||
); | ||
}) | ||
); | ||
} | ||
}]); | ||
return Radios; | ||
}(React.Component)) || _class$15) || _class$15); | ||
exports.field = field; | ||
exports.Form = Form; | ||
exports.Error = ValidationError; | ||
exports.LayoutProvider = LayoutProvider; | ||
exports.ValidatorProvider = ValidatorProvider; | ||
exports.TextInput = TextInput; | ||
exports.EmailInput = EmailInput; | ||
exports.PhoneInput = PhoneInput; | ||
exports.SearchInput = SearchInput; | ||
exports.NumberInput = NumberInput; | ||
exports.PasswordInput = PasswordInput; | ||
exports.HiddenInput = HiddenInput; | ||
exports.Textarea = Textarea; | ||
exports.Checkbox = Checkbox; | ||
exports.Slider = Slider; | ||
exports.Select = Select; | ||
exports.Radios = Radios; | ||
exports.trimmer = trimmer; | ||
exports.optionizer = optionizer; | ||
exports.config = config; | ||
exports['default'] = config; |
{ | ||
"name": "a-plus-forms", | ||
"version": "0.6.2", | ||
"version": "0.7.0", | ||
"description": "A+ forms. Would use again", | ||
@@ -12,3 +12,3 @@ "files": [ | ||
"lint": "eslint src test && flow", | ||
"build": "NODE_ENV=production babel src --out-dir dist/", | ||
"build": "NODE_ENV=production rollup -c", | ||
"precommit": "lint-staged", | ||
@@ -42,2 +42,3 @@ "prepush": "npm run test", | ||
"babel-eslint": "^8.0.2", | ||
"babel-plugin-external-helpers": "^6.22.0", | ||
"babel-plugin-transform-builtin-extend": "^1.1.2", | ||
@@ -53,3 +54,2 @@ "babel-plugin-transform-decorators-legacy": "^1.3.4", | ||
"enzyme-adapter-react-16": "^1.0.1", | ||
"eslint": "^4.9.0", | ||
"eslint-config-shortlyster": "^2.2.0", | ||
@@ -67,2 +67,4 @@ "eslint-plugin-flowtype": "^2.34.1", | ||
"react-test-renderer": "^16.0.0", | ||
"rollup": "^0.52.0", | ||
"rollup-plugin-babel": "^3.0.2", | ||
"sinon": "^4.1.2", | ||
@@ -69,0 +71,0 @@ "sinon-chai": "^2.11.0" |
289
README.md
@@ -7,269 +7,60 @@ # A+ Forms | ||
Let me introduce you to the `A+ forms`, a react forms library that makes forms | ||
creation a civilized and enjoyable process. | ||
Sick of overly complex form libraries in React? The ones that promise simplicity, | ||
but you end up bashing your head against the wall in couple of weeks? Well, allow | ||
me introduce you to the `A+ forms`, a react forms library that you would use again. | ||
## Features | ||
## What's The Big Idea? | ||
There are two principles `A+ forms` are build on top: | ||
Remember how nice and easy forms were when HTML was static? You'd just have a | ||
`form` tag, and a couple of `input` tags, and it would simply send your data | ||
where it supposed to go. Well, `A+ forms` gives you exactly that type of | ||
development experience back: | ||
1) _zero learning curve_, all interfaces are 100% familiar and act as expected | ||
2) _just give me data_, all routine processes are taken care of, the forms just | ||
send the user's input as an object via the `onSubmit` prop. | ||
```js | ||
import { Form, TextInput, PasswordInput } from 'a-plus-forms'; | ||
If you want to more, here is more: | ||
const signIn = ({ username, password }) => { | ||
axios.post('/signin', { username, password }); | ||
}; | ||
3) `~7.5k` in size and zero deps | ||
4) used in production for ages and scales to complex forms nicely | ||
5) easy custom layouts support | ||
6) easy custom validators support | ||
7) easy custom inputs support | ||
8) easy compound inputs support | ||
9) allows to re-wrap existing inputs into new specialized ones | ||
## Usage & Examples | ||
``` | ||
npm install a-plus-forms | ||
yarn add a-plus-forms | ||
``` | ||
### The basic example | ||
First thing to understand about `A+ forms` is that the inputs in this system | ||
are abstractions. Those inputs convey the structure of the form, and, depending | ||
on a context, they can render different desirable results. | ||
```js | ||
import { Form, EmailInput, PasswordInput } from 'a-plus-forms'; | ||
<Form onSubmit={signInAction}> | ||
<EmailInput name="username" label="Username" /> | ||
<Form onSubmit={signIn}> | ||
<TextInput name="username" label="Username" /> | ||
<PasswordInput name="password" label="Password" /> | ||
<button>Sign In</button> | ||
<button type="submit">Sign In</button> | ||
</Form> | ||
``` | ||
In this example, the `signInAction` will receive an object that looks like this: | ||
The goal of the `A+ forms` is to be as close to the original HTML forms API and | ||
declarative coding style that everyone knows and loves. The idea is to give | ||
developers the stability of well known interfaces paired with covering the needs | ||
of the modern front end development practices. | ||
```js | ||
{ | ||
username: '...', | ||
password: '...' | ||
} | ||
``` | ||
## Features? Yeah, we have 'em. | ||
### Input Field Layouts | ||
* Simple, standard looking, and stable API (minimal learning curve) | ||
* A robust and predictable layouts and styling system | ||
* Custom inputs support, yup with nested fields | ||
* Custom validation (including async validation) | ||
* Easy integration with the server side (including server side validation) | ||
* Built for 100% testability in the apps | ||
* Small weight, no deps | ||
Inputs in `A+ forms` are simply standard interfaces, to style them according | ||
to your needs we use the concept of layouts. Which are basically dumb components | ||
that consistently render all inputs in a form. For example: | ||
## Documentation? Youbetcha! | ||
```js | ||
const MyLayout = ({ label, error, input }) => | ||
<div className="my-input"> | ||
<label>{label}</label> | ||
<div className="input-container">{input}</div> | ||
{error ? <small className="error">{error}</small> : null} | ||
</div>; | ||
``` | ||
* [Installation & Configuration](/MadRabbit/a-plus-forms/wiki/Installation-&-Configuration) | ||
* [Layouts & Styling](/MadRabbit/a-plus-forms/wiki/Layouts-&-Styling) | ||
* [React & Redux Wiring](/MadRabbit/a-plus-forms/wiki/React-&-Redux-Wiring) | ||
* [Communicating With Backends](/MadRabbit/a-plus-forms/wiki/Handling-Backend-Requests) | ||
* [Validation & Errors Handling](/MadRabbit/a-plus-forms/wiki/Validation-&-Errors-Handling) | ||
* [Custom Inputs Guide](/MadRabbit/a-plus-forms/wiki/Custom-Inputs-Guide) | ||
* [Testing Best Practices](/MadRabbit/a-plus-forms/wiki/Testing-Best-Practices) | ||
In this case, `label` is the label one specifies in the input props. `error` is | ||
a validation error string, and `input` is the actual input field element. | ||
## Extensions? Totes! | ||
Now that you have a layout, you have options: | ||
* [Boostrap bindings](https://github.com/MadRabbit/a-plus-forms-bootstrap) | ||
* [JSON schemas based validator](https://github.com/MadRabbit/a-plus-forms-json-validator) | ||
```js | ||
// set this layout as a default layout globally | ||
import { config } from 'a-plus-forms'; | ||
config.defaultLayout = MyLayout; | ||
Tell me more! [@nemshilov](https://twitter.com/nemshilov) | ||
// specify the layout as a default via a layout provider | ||
import { LayoutProvider } from 'a-plus-forms'; | ||
<App> | ||
<LayoutProvider layout={MyLayout}> | ||
// content.... | ||
</LayoutProvider> | ||
</App> | ||
// render a specific form with this layout | ||
<Form onSubmit={handler} layout={MyLayout}> | ||
// all inputs here will use that layout | ||
</Form> | ||
// render an individual input with this layout | ||
<EmailInput name="username" layout={MyLayout} /> | ||
``` | ||
You also can render any input without any layout decoration whatsoever, which | ||
is useful when you start making compound. | ||
```js | ||
<TextInput name="something" layout={null} /> | ||
// will render simply this | ||
<input type="text" name="something" /> | ||
``` | ||
### A basic react/redux wiring | ||
```js | ||
import { connect } from 'react-redux'; | ||
import { Form, EmailInput, PasswordInput } from 'a-plus-forms'; | ||
import { signIn } from './actions'; | ||
const dispatchToProps = dispatch => ({ | ||
signIn({ username, password }) { | ||
return dispatch(signIn({ username, password })); | ||
} | ||
}); | ||
const SignInForm = ({ signIn }) => | ||
<Form onSubmit={signIn}> | ||
<EmailInput name="username" label="Username" /> | ||
<PasswordInput name="password" label="Password" /> | ||
<button>Sign In</button> | ||
</Form>; | ||
export default connect(null, dispatchToProps)(SignInForm); | ||
``` | ||
__NOTE__: if the `signIn` action returns a `Promise` the form will automatically | ||
mark the form as disabled for the duration of the server request. | ||
### Validation Story | ||
`A+ forms` have several validation options. A very simple one is to just pass | ||
a function into the `schema` prop: | ||
```js | ||
const validate = (data) => { | ||
if (!data.username) return { username: 'is required' }; | ||
}; | ||
const SignInForm = ({ signIn }) => | ||
<Form onSubmit={signIn} schema={validate}> | ||
<EmailInput name="username" label="Username" /> | ||
<PasswordInput name="password" label="Password" /> | ||
</Form>; | ||
``` | ||
Whenever the validation function returns data, the form will automatically | ||
pass the errors into the respective fields. All the pristine/dirty states are | ||
automatically taken care of. | ||
One also can create custom re-usable validators and pass them around via a | ||
`ValidatorProvider` component: | ||
```js | ||
import { ValidatorProvider } from 'a-plus-forms'; | ||
class CustomValidator { | ||
errorsFor(data) { | ||
if (!data.username) { | ||
return { username: 'is required' }; | ||
} | ||
} | ||
} | ||
<ValidatorProvider validator={CustomValidator}> | ||
<SignInForm signIn={signIn} /> | ||
</ValidatorProvider> | ||
``` | ||
Async validators are also supported out of the box: | ||
```js | ||
class CustomValidator { | ||
async errorsFor({ username }) { | ||
const taken = await api.get('/username-check', { username }); | ||
if (taken) { | ||
return { username: 'is already taken' }; | ||
} | ||
} | ||
} | ||
``` | ||
For a reference implementation of custom validators, please see the | ||
[JSON Schema validator plugin](https://github.com/MadRabbit/a-plus-forms-json-validator) | ||
### Custom Inputs | ||
Where `A+ forms` really shine is the ease of creation of new custom inputs. A | ||
very simple use case would look somewhat like this: | ||
```js | ||
import { field } from 'a-plus-forms'; | ||
const MyCustomInput = ({ value, onChange }) => | ||
<input type="text" value={value} onChange={onChange} />; | ||
export default field(MyCustomInput); | ||
``` | ||
Essentially, the `field` decorator is where the magic happens, it takes care of | ||
all the state, errors and layouts management for you. The only think you need | ||
to provide back a _controlled_ input that understands `value` and `onChange`. | ||
You can also re-use/re-wrap existing inputs to build extra functionality into | ||
the fields: | ||
```js | ||
import { field, TextInput } from 'a-plus-forms'; | ||
const PhoneNumberInput = props => { | ||
const { onChange, ...rest } = props; | ||
const format = userInput => toPhoneNumber(userInput); | ||
return ( | ||
<TextInput | ||
{...rest} | ||
layout={null} // <- render the input only | ||
onChange={value => onChange(format(value))} | ||
/> | ||
); | ||
} | ||
``` | ||
For more examples, please see the collection of the [built in inputs](src/inputs) | ||
### Compound/Nested Inputs | ||
One can easily combine several inputs into one compound input if needed. | ||
```js | ||
import { field, TextInput } from 'a-plus-forms'; | ||
const AddressInput = field({ nested: true })( | ||
() => ( | ||
<div> | ||
<TextInput name="country" layout={false} /> | ||
<TextInput name="state" layout={false} /> | ||
<TextInput name="city" layout={false} /> | ||
</div> | ||
) | ||
); | ||
const ProfileForm = ({ onSubmit }) => | ||
<Form onSubmit={onSubmit}> | ||
<AddressInput name="address" /> | ||
</Form>; | ||
``` | ||
When the user submits the form, the `onSubmit` function will receive a structure | ||
that looks like so: | ||
```js | ||
{ | ||
address: { | ||
country: '...', | ||
state: '...', | ||
city: '...' | ||
} | ||
} | ||
``` | ||
## Copyright & License | ||
@@ -276,0 +67,0 @@ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
52605
30
3
1328
70
1