UNPKG

rehance-forms

Version:
1,354 lines (1,326 loc) 67.8 kB
'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