rehance-forms
Version:
Form utilities for React
1,354 lines (1,326 loc) • 67.8 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var React = require('react');
var events = require('events');
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
t[p[i]] = s[p[i]];
return t;
}
/**
* Returns true if the given value is a function.
*/
/**
* Returns true if the given value is a function.
*/
/**
* Capitalize the first letter of the given string and return it.
*/
/**
* Generate and return a random value between the 2 numbers.
*/
function randomRange(min, max) {
return min + Math.round(Math.random() * (max - min));
}
/**
* Wraps the given handler function with a memoization/cache layer that enables a function
* to only be bound once for the given key.
*/
function memoize(func) {
var cache = {};
return function (key) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
if (!cache[key]) {
cache[key] = function () { return func.apply(void 0, args); };
}
return cache[key];
};
}
var isArray = Array.isArray;
var keyList = Object.keys;
var hasProp = Object.prototype.hasOwnProperty;
function equal(a, b) {
if (a === b) {
return true;
}
if (a && b && typeof a == "object" && typeof b == "object") {
var arrA = isArray(a), arrB = isArray(b), i = void 0, length_1, key = void 0;
if (arrA && arrB) {
length_1 = a.length;
if (length_1 != b.length) {
return false;
}
for (i = length_1; i-- !== 0;) {
if (!equal(a[i], b[i])) {
return false;
}
}
return true;
}
if (arrA != arrB) {
return false;
}
var dateA = a instanceof Date, dateB = b instanceof Date;
if (dateA != dateB) {
return false;
}
if (dateA && dateB) {
return a.getTime() == b.getTime();
}
var regexpA = a instanceof RegExp, regexpB = b instanceof RegExp;
if (regexpA != regexpB) {
return false;
}
if (regexpA && regexpB) {
return a.toString() == b.toString();
}
var keys = keyList(a);
length_1 = keys.length;
if (length_1 !== keyList(b).length) {
return false;
}
for (i = length_1; i-- !== 0;) {
if (!hasProp.call(b, keys[i])) {
return false;
}
}
for (i = length_1; i-- !== 0;) {
key = keys[i];
if (!equal(a[key], b[key])) {
return false;
}
}
return true;
}
return a !== a && b !== b;
}
var events$1 = new events.EventEmitter();
events$1.setMaxListeners(Infinity);
(function (FormEventSignal) {
FormEventSignal[FormEventSignal["SubmitForm"] = 0] = "SubmitForm";
FormEventSignal[FormEventSignal["ScopeUpdate"] = 1] = "ScopeUpdate";
FormEventSignal[FormEventSignal["FieldCreated"] = 2] = "FieldCreated";
FormEventSignal[FormEventSignal["FieldUpdate"] = 3] = "FieldUpdate";
FormEventSignal[FormEventSignal["FieldDestroyed"] = 4] = "FieldDestroyed";
})(exports.FormEventSignal || (exports.FormEventSignal = {}));
var EventBus = /** @class */ (function () {
function EventBus() {
var _this = this;
this._id = "eventbus_" + randomRange(100000000, 999999999);
/**
* Trigger the bus using the given event context.
*/
this.trigger = function (ev) {
events$1.emit(_this._id, ev);
};
/**
* Add a subscriber to the event bus, the returned function will automatically remove
* the given subscriber.
*/
this.listen = function (subscriber) {
events$1.addListener(_this._id, subscriber);
return function () {
events$1.removeListener(_this._id, subscriber);
};
};
}
return EventBus;
}());
var FieldContext = /** @class */ (function () {
function FieldContext(initialValue) {
this.error = null;
this.touched = false;
this._initialValue = initialValue;
if (initialValue && typeof initialValue === "object") {
this.value = (Array.isArray(initialValue) ? initialValue.slice(0) : __assign({}, initialValue));
}
else {
this.value = initialValue;
}
}
Object.defineProperty(FieldContext.prototype, "valid", {
/**
* Returns true if the field does not have an error.
*/
get: function () {
return !this.error;
},
enumerable: true,
configurable: true
});
Object.defineProperty(FieldContext.prototype, "changed", {
/**
* Returns true if the value for the field has changed.
*/
get: function () {
return !equal(this._initialValue, this.value);
},
enumerable: true,
configurable: true
});
/**
* Resets the value for the current field back to its initial value.
*/
FieldContext.prototype.reset = function () {
this.value = this._initialValue;
};
/**
* Clears the value for the current field.
*/
FieldContext.prototype.clear = function () {
this.value = null;
};
/**
* Replaces the `initialValue` of the field with the current `value`.
*/
FieldContext.prototype.commit = function () {
this._initialValue = this.value;
};
return FieldContext;
}());
var _a;
var FormScopeConsumer = (_a = React.createContext(null), _a.Consumer);
var FormScopeProvider = _a.Provider;
var BaseContext = /** @class */ (function () {
function BaseContext(parentScope) {
if (parentScope === void 0) { parentScope = null; }
this._parent = parentScope;
var id = randomRange(100000000, 999999999);
if (parentScope) {
this._events = parentScope._events;
this._id = parentScope.id + "." + id;
}
else {
this._events = new EventBus();
this._id = "" + id;
}
}
Object.defineProperty(BaseContext.prototype, "root", {
/**
* Returns the top level or root scope of the hierarchy that this scope belongs to.
* Essentially, this will return the form scope.
*/
get: function () {
// tslint:disable-next-line:no-this-assignment
var scope = this;
while (scope.parent) {
scope = scope.parent;
}
return scope;
},
enumerable: true,
configurable: true
});
Object.defineProperty(BaseContext.prototype, "parent", {
/**
* Returns the parent scope of this scope or null if no parent scope exists and this
* is the top level scope.
*/
get: function () {
return this._parent;
},
enumerable: true,
configurable: true
});
Object.defineProperty(BaseContext.prototype, "events", {
/**
* Returns the event bus that this scope is using. If this scope is nested inside
* of another scope, it will use the parent scope's event bus (all the way up the
* scope tree to the root scope).
*/
get: function () {
return this._events;
},
enumerable: true,
configurable: true
});
Object.defineProperty(BaseContext.prototype, "id", {
/**
* Returns the ID for the scope. The scope ID is a combination of its own internal
* ID and the IDs of its parents.
*/
get: function () {
return this._id;
},
enumerable: true,
configurable: true
});
/**
* Returns true if the scope is the parent (or ancestor) of the given scope.
*/
BaseContext.prototype.isAncestorOf = function (scope) {
return scope.id.indexOf(this._id) === 0;
};
/**
* Returns true if the scope is the child (or descendent) of the given scope.
*/
BaseContext.prototype.isDescendentOf = function (scope) {
return scope.isAncestorOf(this);
};
/**
* Subscribe to all events occurring within the hierarchy that this scope belongs to.
*/
BaseContext.prototype.listen = function (sub) {
return this._events.listen(sub);
};
/**
* Triggers an update that will be broadcasted to all scopes within the hierarchy that
* this scope belongs to.
*/
BaseContext.prototype.broadcast = function (signal, field) {
this._events.trigger({ scope: this, signal: signal, field: field });
return this;
};
/**
* Submits the form that the scope belongs to.
*/
BaseContext.prototype.submit = function () {
this.broadcast(exports.FormEventSignal.SubmitForm);
return this;
};
return BaseContext;
}());
var ScopeContext = /** @class */ (function (_super) {
__extends(ScopeContext, _super);
function ScopeContext(initialValues, parentScope) {
if (initialValues === void 0) { initialValues = {}; }
if (parentScope === void 0) { parentScope = null; }
var _this = _super.call(this, parentScope) || this;
_this._initialValues = initialValues;
_this.children = {};
return _this;
}
Object.defineProperty(ScopeContext.prototype, "initialValues", {
/**
* Returns the initial values for the scope.
*/
get: function () {
return this._initialValues;
},
enumerable: true,
configurable: true
});
/**
* Returns a child scope or field of this scope. Returns null if a
* valid child cannot be found.
*/
ScopeContext.prototype.getChild = function (name) {
return this.children[name] || null;
};
/**
* Register a child scope or field to this scope.
*/
ScopeContext.prototype.setChild = function (name, child) {
this.children[name] = child;
return this;
};
/**
* Unregister a child scope or field from this scope.
*/
ScopeContext.prototype.clearChild = function (name) {
delete this.children[name];
return this;
};
Object.defineProperty(ScopeContext.prototype, "value", {
/**
* Builds and returns a map of key/value pairs with the data managed by this
* scope, and the child scopes.
*/
get: function () {
var values = {};
for (var key in this.children) {
values[key] = this.children[key].value;
}
return values;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ScopeContext.prototype, "error", {
/**
* Returns the errors for this scope and its descendents.
*/
get: function () {
return this.getErrors(Object.keys(this.children));
},
enumerable: true,
configurable: true
});
/**
* Returns the errors for the requested fields or null if no errors were found in
* the specified fields.
*/
ScopeContext.prototype.getErrors = function (fields) {
var output = {};
var hasErrors = false;
for (var _i = 0, fields_1 = fields; _i < fields_1.length; _i++) {
var key = fields_1[_i];
var error = this.children[key].error;
if (error) {
hasErrors = true;
output[key] = error;
}
}
return (hasErrors ? output : null);
};
/**
* Returns an existing field or creates a new field context is one does not
* exist. The field is automatically added as a child to the scope.
*/
ScopeContext.prototype.field = function (name) {
if (!this.children[name]) {
var initialValue = this.initialValues[name];
this.children[name] = new FieldContext(initialValue);
}
if (!(this.children[name] instanceof FieldContext)) {
console.warn("\"" + name + " is not a FieldContext type child of scope! Returning an empty FieldContext object instead.");
return new FieldContext(this.initialValues[name]);
}
return this.children[name];
};
Object.defineProperty(ScopeContext.prototype, "valid", {
/**
* Returns true if none of the fields in the current scope have an error.
*/
get: function () {
for (var key in this.children) {
if (!this.children[key].valid) {
return false;
}
}
return true;
},
enumerable: true,
configurable: true
});
/**
* Returns true if all of the specified children of this scope are considered valid.
*/
ScopeContext.prototype.areValid = function (fields) {
for (var _i = 0, fields_2 = fields; _i < fields_2.length; _i++) {
var key = fields_2[_i];
var child = this.children[key];
if (child && !child.valid) {
return false;
}
}
return true;
};
Object.defineProperty(ScopeContext.prototype, "changed", {
/**
* Returns true if any of the fields have in the current scope have changed.
*/
get: function () {
for (var key in this.children) {
if (this.children[key].changed) {
return true;
}
}
return false;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ScopeContext.prototype, "touched", {
/**
* Returns true if any of the fields have in the current scope have been touched.
*/
get: function () {
for (var key in this.children) {
if (this.children[key].touched) {
return true;
}
}
return false;
},
enumerable: true,
configurable: true
});
/**
* Returns true if any of the specified children of this scope have changed.
*/
ScopeContext.prototype.hasChanges = function (fields) {
for (var _i = 0, fields_3 = fields; _i < fields_3.length; _i++) {
var key = fields_3[_i];
var child = this.children[key];
if (child && !child.changed) {
return false;
}
}
return true;
};
/**
* Resets the values of the all fields and scopes with in the current scope
* back to their initial values.
*/
ScopeContext.prototype.reset = function () {
for (var key in this.children) {
this.children[key].reset();
}
return this;
};
/**
* Clears the values of all fields and scopes within the current scope.
*/
ScopeContext.prototype.clear = function () {
for (var key in this.children) {
this.children[key].clear();
}
return this;
};
/**
* Convenience method for getting a single value from the scope.
*/
ScopeContext.prototype.get = function (field, fallback) {
if (fallback === void 0) { fallback = undefined; }
return (this.children[field] !== undefined ? this.children[field].value : fallback);
};
/**
* Adopts the current values for each field as the new initial value, setting the
* changed state for the scope to `false`. If you want to commit changes for a full
* form from a sub scope, use `scope.root.commit()`. Once you've committed the changes
* you'll want to run the `.update()` method to broadcast the update.
*/
ScopeContext.prototype.commit = function () {
for (var key in this.children) {
this.children[key].commit();
}
return this;
};
/**
* Convenience method that broadcasts the scope update event.
*/
ScopeContext.prototype.update = function () {
return this.broadcast(exports.FormEventSignal.ScopeUpdate);
};
return ScopeContext;
}(BaseContext));
function getChildScopeValue(scope) {
return scope.value;
}
var ListScopeContext = /** @class */ (function (_super) {
__extends(ListScopeContext, _super);
function ListScopeContext(initialValues, parentScope) {
if (initialValues === void 0) { initialValues = []; }
if (parentScope === void 0) { parentScope = null; }
var _this = _super.call(this, parentScope) || this;
_this.children = [];
_this._initialValues = initialValues;
_this.children = initialValues.map(function (value) { return new ScopeContext(value, _this); });
return _this;
}
Object.defineProperty(ListScopeContext.prototype, "initialValues", {
/**
* Returns the initial values for the list scope.
*/
get: function () {
return this._initialValues;
},
enumerable: true,
configurable: true
});
/**
* Adds a new child context to the list scope.
*/
ListScopeContext.prototype.addChildScope = function (values) {
if (values === void 0) { values = {}; }
this.children.push(new ScopeContext(values, this));
return this;
};
/**
* Splices a specific child by index.
*/
ListScopeContext.prototype.removeChildScope = function (index) {
if (index < 0 || index >= this.children.length) {
return;
}
this.children.splice(index, 1);
return this;
};
Object.defineProperty(ListScopeContext.prototype, "value", {
/**
* Builds and returns an array with the data managed by this scope,
* and the child scopes.
*/
get: function () {
return this.children.map(getChildScopeValue);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ListScopeContext.prototype, "error", {
/**
* Returns the errors for all of the scopes within this list scope as an array or
* null if no errors are found in any of the nested scopes.
*/
get: function () {
var output = [];
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
var error = child.error;
if (error) {
output.push(error);
}
}
return (output.length > 0 ? output : null);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ListScopeContext.prototype, "valid", {
/**
* Returns true if none of the fields in the current scope have an error.
*/
get: function () {
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
if (!child.valid) {
return false;
}
}
return true;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ListScopeContext.prototype, "changed", {
/**
* Returns true if any of the fields have in the current scope have changed.
*/
get: function () {
if (this.children.length !== this._initialValues.length) {
return true;
}
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
if (child.changed) {
return true;
}
}
return false;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ListScopeContext.prototype, "touched", {
/**
* Returns true if any of the fields have in the current scope have been touched.
*/
get: function () {
for (var key in this.children) {
if (this.children[key].touched) {
return true;
}
}
return false;
},
enumerable: true,
configurable: true
});
/**
* Resets the values of the all fields and scopes with in the current scope
* back to their initial values.
*/
ListScopeContext.prototype.reset = function () {
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
child.reset();
}
return this;
};
/**
* Clears the values of all fields and scopes within the current scope.
*/
ListScopeContext.prototype.clear = function () {
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
child.clear();
}
return this;
};
/**
* Adopts the current values for each field as the new initial value, setting the
* changed state for the scope to `false`. If you want to commit changes for a full
* form from a sub scope, use `scope.root.commit()`. Once you've committed the changes
* you'll want to run the `.update()` method to broadcast the update.
*/
ListScopeContext.prototype.commit = function () {
for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
var child = _a[_i];
child.commit();
}
return this;
};
/**
* Convenience method that broadcasts the scope update event.
*/
ListScopeContext.prototype.update = function () {
return this.broadcast(exports.FormEventSignal.ScopeUpdate);
};
return ListScopeContext;
}(BaseContext));
/**
* Creates a higher-order component that binds the given component to the FormScopeConsumer
* that provides access to the form scope context API.
*/
function withFormScope(Component$$1) {
var Result = React.forwardRef(function (props, ref) {
return (React.createElement(FormScopeConsumer, null, function (context) { return React.createElement(Component$$1, __assign({}, props, { formScope: context, ref: ref })); }));
});
Result.displayName = (Component$$1.displayName || Component$$1.name);
return Result;
}
var _AddCollectionItem = /** @class */ (function (_super) {
__extends(_AddCollectionItem, _super);
function _AddCollectionItem() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/**
* Handle the button click that will add a new item to the array item within
* the scope.
*/
_this.handleClick = function (ev) {
ev.preventDefault();
if (_this.isDisabled()) {
return;
}
var formScope = _this.props.formScope;
var scopeChild = formScope.getChild(_this.props.to);
if (!scopeChild) {
console.warn("AddCollectionItem cannot add an item to the \"" + _this.props.to + "\" field becaue it does not exist.");
return;
}
if (scopeChild instanceof FieldContext) {
var arr = scopeChild.value;
if (Array.isArray(arr)) {
arr.push(_this.props.values);
formScope.broadcast(exports.FormEventSignal.FieldUpdate, _this.props.to);
}
}
else if (scopeChild instanceof ListScopeContext) {
scopeChild.addChildScope(_this.props.values);
formScope.broadcast(exports.FormEventSignal.FieldUpdate, _this.props.to);
}
else {
console.warn("AddCollectionItem cannot add an item to \"" + _this.props.to + "\" because the target is not an array field or CollectionScope.");
}
if (_this.props.onClick) {
_this.props.onClick(ev, formScope);
}
};
return _this;
}
/**
* Checks if the component should be considered disabled or not.
*/
_AddCollectionItem.prototype.isDisabled = function () {
return (typeof this.props.disabled === "function" ? this.props.disabled(this.props.formScope) : this.props.disabled);
};
/**
* Render the button.
*/
_AddCollectionItem.prototype.render = function () {
return (React.createElement("button", { type: "button", style: this.props.style, className: this.props.className, disabled: this.isDisabled(), onClick: this.handleClick }, this.props.children));
};
_AddCollectionItem.displayName = "AddCollectionItem";
return _AddCollectionItem;
}(React.Component));
var AddCollectionItem = withFormScope(_AddCollectionItem);
function bindAsField(Component$$1) {
var _a;
var name = Component$$1.displayName || Component$$1.name;
return withFormScope((_a = /** @class */ (function (_super) {
__extends(class_1, _super);
function class_1() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/**
* Handle updates from the current scope that this field belongs to.
*/
_this.handleScopeEvents = function (ev) {
var scope = _this.props.formScope;
if (!ev.field && ev.scope.isAncestorOf(scope) || ev.field === _this.props.name) {
_this.forceUpdate();
}
};
/**
* Update the state for the form field.
*/
_this.update = function (nextState) {
if ("value" in nextState) {
_this.fieldState.value = nextState.value;
}
if ("error" in nextState) {
_this.fieldState.error = nextState.error || null;
}
if ("touched" in nextState) {
_this.fieldState.touched = !!nextState.touched;
}
_this.triggerUpdate();
};
/**
* Trigger a scope update for the field.
*/
_this.triggerUpdate = function () {
_this.props.formScope.broadcast(exports.FormEventSignal.FieldUpdate, _this.props.name);
};
return _this;
}
/**
* Register the field with the parent scope and add a subscriber to
* the scope updates.
*/
class_1.prototype.componentWillMount = function () {
var scope = this.props.formScope;
this.fieldState = scope.field(this.props.name);
scope.broadcast(exports.FormEventSignal.FieldCreated, this.props.name);
this.unsubscribe = scope.listen(this.handleScopeEvents);
};
/**
* Unregister the field from the parent scope and unsubscribe from scope events.
*/
class_1.prototype.componentWillUnmount = function () {
if (this.props.keepChangesOnUnmount) {
this.props.formScope.clearChild(this.props.name);
}
this.unsubscribe();
this.props.formScope.broadcast(exports.FormEventSignal.FieldDestroyed, this.props.name);
};
/**
* Render the child component for the field.
*/
class_1.prototype.render = function () {
var _a = this.props, formScope = _a.formScope, props = __rest(_a, ["formScope"]);
var field = {
value: this.fieldState.value,
error: this.fieldState.error,
touched: this.fieldState.touched,
changed: this.fieldState.changed,
update: this.update,
};
return (React.createElement(Component$$1, __assign({}, props, { scope: formScope, field: field }), this.props.children));
};
return class_1;
}(React.PureComponent)), _a.displayName = "Field(" + name + ")", _a));
}
var _Button = /** @class */ (function (_super) {
__extends(_Button, _super);
function _Button() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/**
* Handles click events for the button.
*/
_this.handleClick = function (ev) {
if (_this.props.onClickWithScope) {
_this.props.onClickWithScope(ev, _this.props.formScope);
}
if (_this.props.onClick) {
_this.props.onClick(ev);
}
};
return _this;
}
/**
* Subscribe to scope changes and update the button when any
* value on the scope has changed.
*/
_Button.prototype.componentWillMount = function () {
var _this = this;
this.unsubscribe = this.props.formScope.listen(function () { return _this.forceUpdate(); });
};
/**
* Unsubscribe from scope updates.
*/
_Button.prototype.componentWillUnmount = function () {
this.unsubscribe();
};
/**
* Determines if the button should be disabled.
*/
_Button.prototype.isDisabled = function () {
var _a = this.props, formScope = _a.formScope, disabled = _a.disabled, disabledOnError = _a.disabledOnError, disabledUntilChanged = _a.disabledUntilChanged;
// bail immediately if the standard disabled prop is (or returns) true
if ((disabled === true) ||
(typeof disabled === "function" && disabled(formScope))) {
return true;
}
// check for form errors if the disabledOnError prop is set
if (disabledOnError && ((Array.isArray(disabledOnError) && formScope.getErrors(disabledOnError)) ||
!formScope.valid)) {
return true;
}
// finally, check for changes on the form when the disabledUntilChange prop is set
if (disabledUntilChanged && ((Array.isArray(disabledUntilChanged) && formScope.hasChanges(disabledUntilChanged)) ||
!formScope.changed)) {
return true;
}
return false;
};
/**
* Render the button.
*/
_Button.prototype.render = function () {
var _a = this.props, formScope = _a.formScope, disabled = _a.disabled, disabledOnError = _a.disabledOnError, disabledUntilChanged = _a.disabledUntilChanged, onClickWithScope = _a.onClickWithScope, children = _a.children, props = __rest(_a, ["formScope", "disabled", "disabledOnError", "disabledUntilChanged", "onClickWithScope", "children"]);
return (React.createElement("button", __assign({}, props, { onClick: this.handleClick, disabled: this.isDisabled() }), children));
};
_Button.displayName = "Button";
_Button.defaultProps = {
disabled: false,
};
return _Button;
}(React.PureComponent));
var Button = withFormScope(_Button);
var ClearButton = /** @class */ (function (_super) {
__extends(ClearButton, _super);
function ClearButton() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/**
* Handle the button click.
*/
_this.handleClick = function (ev, scope) {
scope.clear();
};
return _this;
}
/**
* Render the button.
*/
ClearButton.prototype.render = function () {
var _a = this.props, children = _a.children, props = __rest(_a, ["children"]);
return (React.createElement(Button, __assign({}, props, { onClickWithScope: this.handleClick }), children));
};
ClearButton.defaultProps = {
disabled: function (scope) {
var values = scope.value;
return Object.keys(values).length === 0;
},
};
return ClearButton;
}(React.PureComponent));
/**
* The collection scope allows for an array of objects to be mapped to a
* subform of fields.
*/
var _CollectionScope = /** @class */ (function (_super) {
__extends(_CollectionScope, _super);
function _CollectionScope() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/**
* Triggers a re-render, only when the array has had an update triggered.
*/
_this.handleScopeEvent = function (ev) {
var isSameOrAncestorScope = ev.scope.isAncestorOf(_this.scope);
if ((!ev.field || ev.field === _this.props.name) && isSameOrAncestorScope) {
// this.keyOffset += 1;
_this.forceUpdate();
if (_this.props.onChange) {
_this.props.onChange(_this.scope);
}
}
};
/**
* Handles the removal of a single item.
*/
_this.bindItemRemoval = memoize(function (index) {
_this.scope.removeChildScope(index);
_this.scope.broadcast(exports.FormEventSignal.FieldUpdate, _this.props.name);
if (_this.props.onRemove) {
_this.props.onRemove(index, _this.scope);
}
});
/**
* Renders a scoped form for each item in the array.
*/
_this.renderItemScoped = function (scope, idx) {
return (React.createElement(FormScopeProvider, { key: scope.id, value: scope }, _this.props.children({
index: idx,
scope: scope,
total: _this.scope.children.length,
removeItem: _this.bindItemRemoval("remove_" + idx, idx),
})));
};
return _this;
}
/**
* Subscribe to changes to the parent array for this scope.
*/
_CollectionScope.prototype.componentWillMount = function () {
var _a = this.props, formScope = _a.formScope, name = _a.name;
var values = formScope.initialValues[name] || [];
if (!Array.isArray(values)) {
console.warn("The value for " + this.props.name + " is not an array! Value will be overridden with an array by CollectionScope.");
values = [];
}
this.scope = new ListScopeContext(values, formScope);
formScope.setChild(this.props.name, this.scope);
this.unsubscribe = formScope.listen(this.handleScopeEvent);
};
/**
* Unsubscribe from changes to the parent array for this scope.
*/
_CollectionScope.prototype.componentWillUnmount = function () {
this.unsubscribe();
this.props.formScope.clearChild(this.props.name);
};
/**
* Renders the collection scopes.
*/
_CollectionScope.prototype.render = function () {
return (React.createElement(React.Fragment, null, this.scope.children.map(this.renderItemScoped)));
};
_CollectionScope.displayName = "CollectionScope";
return _CollectionScope;
}(React.Component));
var CollectionScope = withFormScope(_CollectionScope);
var _Subscriber = /** @class */ (function (_super) {
__extends(_Subscriber, _super);
function _Subscriber(props, context) {
var _this = _super.call(this, props, context) || this;
/**
* Determine whether the subscriber should force and update.
*/
_this.handleScopeEvents = function (ev) {
if (_this.shouldUpdate(ev)) {
_this.forceUpdate();
}
};
_this.bindUpdateCheck(props.field);
return _this;
}
/**
* Generate the update predicate when props are changed.
*/
_Subscriber.prototype.componentWillReceiveProps = function (nextProps) {
if (this.props.field !== nextProps.field) {
this.bindUpdateCheck(nextProps.field);
}
};
/**
* When the component mounts, subscribe to scope updates.
*/
_Subscriber.prototype.componentWillMount = function () {
this.unsubscribe = this.props.formScope.listen(this.handleScopeEvents);
};
/**
* When the component unmounts, unsubscribe from scope updates.
*/
_Subscriber.prototype.componentWillUnmount = function () {
this.unsubscribe();
};
/**
* Generate the update predicate.
*/
_Subscriber.prototype.bindUpdateCheck = function (fieldProp) {
if (typeof fieldProp === "function") {
this.shouldUpdate = fieldProp;
}
else if (Array.isArray(fieldProp)) {
this.shouldUpdate = function (ev) {
return !ev.field || fieldProp.indexOf(ev.field) !== -1;
};
}
else if (typeof fieldProp === "string") {
this.shouldUpdate = function (ev) {
return !ev.field || ev.field === fieldProp;
};
}
else {
this.shouldUpdate = function () { return true; };
}
};
/**
* Render the component.
*/
_Subscriber.prototype.render = function () {
return (React.createElement(React.Fragment, null, this.props.children(this.props.formScope)));
};
_Subscriber.displayName = "Subscriber";
return _Subscriber;
}(React.Component));
var Subscriber = withFormScope(_Subscriber);
var ErrorOutput = /** @class */ (function (_super) {
__extends(ErrorOutput, _super);
function ErrorOutput() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/**
* Render the content error output content.
*/
_this.renderContent = function (scope) {
var _a = _this.props, name = _a.name, alwaysShow = _a.alwaysShow, props = __rest(_a, ["name", "alwaysShow"]);
var field = scope.field(name);
if (!!field.error && (field.touched || alwaysShow)) {
return React.createElement("span", __assign({}, props), field.error);
}
return React.createElement(React.Fragment, null);
};
return _this;
}
/**
* Render the component.
*/
ErrorOutput.prototype.render = function () {
return (React.createElement(Subscriber, { field: this.props.name }, this.renderContent));
};
return ErrorOutput;
}(React.PureComponent));
var Form = /** @class */ (function (_super) {
__extends(Form, _super);
function Form(props, context) {
var _this = _super.call(this, props, context) || this;
/**
* Handle scope updates for the the top level scope created by this component,
* including the call to submit the form.
*/
_this.handleScopeEvent = function (ev) {
if (ev.signal === exports.FormEventSignal.SubmitForm) {
_this.handleSubmit();
}
else if (_this.props.onEvent) {
_this.props.onEvent(ev, _this.formScope);
}
};
/**
* The actual function that handles that calls the submit handler prop.
*/
_this.handleSubmit = function () {
if (_this.props.onSubmit) {
var values = (_this.props.mergeInitialStateOnSubmit ? __assign({}, _this.props.initialValues, _this.formScope.value) : _this.formScope.value);
_this.props.onSubmit(values, _this.formScope);
}
};
/**
* Invoke the onSubmit prop with the current form values when submitted.
*/
_this.handleFormSubmit = function (ev) {
ev.preventDefault();
_this.handleSubmit();
};
var initialValues = {};
if (typeof props.initialValues !== "object" ||
Array.isArray(props.initialValues)) {
console.warn("You must provide an object type for the initialValues prop! Reverting to an empty object.");
}
else {
initialValues = __assign({}, props.initialValues);
}
_this.formScope = new ScopeContext(initialValues);
_this.formScope.listen(_this.handleScopeEvent);
return _this;
}
/**
* Provide the "onMount" prop (if any) with the form scope object for the form.
*/
Form.prototype.componentWillMount = function () {
if (this.props.onMount) {
this.props.onMount(this.formScope);
}
};
/**
* Render the form and its contents.
*/
Form.prototype.render = function () {
var Tag = this.props.tag;
var props = {
className: this.props.className,
style: this.props.style,
};
if (Tag === "form") {
props.noValidate = true;
props.onSubmit = this.handleFormSubmit;
}
return (React.createElement(Tag, __assign({}, props),
React.createElement(FormScopeProvider, { value: this.formScope }, typeof this.props.children === "function" ?
this.props.children(this.formScope) :
this.props.children)));
};
Form.defaultProps = {
tag: "form",
initialValues: {},
};
return Form;
}(React.Component));
var HTMLFieldComponent = /** @class */ (function (_super) {
__extends(HTMLFieldComponent, _super);
function HTMLFieldComponent(props, context) {
var _this = _super.call(this, props, context) || this;
/**
* Trigger an update for this field.
*/
_this.triggerUpdate = function () {
_this.props.formScope.broadcast(exports.FormEventSignal.FieldUpdate, _this.props.name);
};
/**
* Process incoming scope events.
*/
_this.handleScopeEvent = function (ev) {
if ((!ev.field || ev.field === _this.props.name) &&
ev.scope === _this.props.formScope) {
_this.forceUpdate();
}
};
if (!props.formScope) {
console.error("You have tried to add a form element outside of a form scope! Wrap this element in a <Form> component.");
}
// this is to make sure we don't break inheritance
_this.bindRef = _this.bindRef.bind(_this);
_this.handleScopeEvent = _this.handleScopeEvent.bind(_this);
_this.handleFocus = _this.handleFocus.bind(_this);
_this.handleBlur = _this.handleBlur.bind(_this);
_this.handleChange = _this.handleChange.bind(_this);
_this.validateSelf = _this.validateSelf.bind(_this);
return _this;
}
/**
* Inform the scope that this field exists and subscribe to scope events.
*/
HTMLFieldComponent.prototype.componentWillMount = function () {
this.field = this.props.formScope.field(this.props.name);
this.props.formScope.broadcast(exports.FormEventSignal.FieldCreated, this.props.name);
this.unsubscribe = this.props.formScope.listen(this.handleScopeEvent);
};
/**
* Unsubscribe from scope events and clear the field context.
*/
HTMLFieldComponent.prototype.componentWillUnmount = function () {
this.unsubscribe();
if (!this.props.keepChangesOnUnmount) {
this.props.formScope.clearChild(this.props.name);
}
this.props.formScope.broadcast(exports.FormEventSignal.FieldDestroyed, this.props.name);
};
Object.defineProperty(HTMLFieldComponent.prototype, "value", {
/**
* Returns the formatted value for the field.
*/
get: function () {
return this.format(this.field.value, false);
},
enumerable: true,
configurable: true
});
Object.defineProperty(HTMLFieldComponent.prototype, "classes", {
/**
* Generates and return the class names for the element.
*/
get: function () {
var className = this.props.className;
if (typeof className !== "function") {
return className;
}
return className(this.field);
},
enumerable: true,
configurable: true
});
/**
* Format input using a given formatter prop.
*/
HTMLFieldComponent.prototype.format = function (value, output) {
return (this.props.format ?
this.props.format(value, output) :
value);
};
/**
* Bind the element reference for the field.
*/
HTMLFieldComponent.prototype.bindRef = function (el) {
var firstBind = !this.element;
this.element = el;
// we trigger validation on first bind because element is not available
// before this to check validation state
if (firstBind) {
this.field.error = this.validateSelf();
this.triggerUpdate();
}
};
/**
* Handle focus events for this field.
*/
HTMLFieldComponent.prototype.handleFocus = function (ev) {
if (this.props.onFocus) {
this.props.onFocus(ev, this.props.formScope);
}
};
/**
* Handle blur events for this field.
*/
HTMLFieldComponent.prototype.handleBlur = function (ev) {
this.field.error = this.validateSelf();
this.field.touched = true;
this.triggerUpdate();
if (this.props.onBlur) {
this.props.onBlur(ev, this.props.formScope);
}
};
/**
* Handle change events for this field.
*/
HTMLFieldComponent.prototype.handleChange = function (ev) {
var value = this.format(ev.target.value, true);
if (this.props.validateOnChange) {
this.field.error = this.validateSelf();
}
this.field.value = value;
this.triggerUpdate();
if (this.props.onChange) {
this.props.onChange(ev, this.props.formScope);
}
};
/**
* Validates the field and return either the error message or null.
*/
HTMLFieldComponent.prototype.validateSelf = function () {
var element = this.element;
var _a = this.props, name = _a.name, validate = _a.validate, formScope = _a.formScope;
var result = (!!validate ?
validate(name, formScope.value) :
element.validationMessage);
return result || null;
};
return HTMLFieldComponent;
}(React.PureComponent));
var InputComponent = /** @class */ (function (_super) {
__extends(InputComponent, _super);
function InputComponent() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/**
* Handle changes to th