New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

a-plus-forms

Package Overview
Dependencies
Maintainers
1
Versions
41
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

a-plus-forms - npm Package Compare versions

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;

8

package.json
{
"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"

@@ -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 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc