UNPKG

21.6 kBJavaScriptView Raw
1import _regeneratorRuntime from "@babel/runtime/regenerator";
2
3function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
4
5function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
6
7function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
8
9function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
10
11function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
12
13function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
14
15function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
16
17function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
18
19function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
20
21function _defineProperty(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; }
22
23function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
24
25function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
26
27function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
28
29function _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); } }
30
31function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
32
33function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
34
35function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
36
37function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
38
39function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
40
41function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
42
43function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
44
45function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
46
47/* eslint-disable no-case-declarations */
48import React from 'react';
49import { mapObject, set, isEqual, flatMap } from './utilities';
50import { List, Nested } from './components';
51
52var FormState = /*#__PURE__*/function (_React$PureComponent) {
53 _inherits(FormState, _React$PureComponent);
54
55 var _super = _createSuper(FormState);
56
57 function FormState() {
58 var _this;
59
60 _classCallCheck(this, FormState);
61
62 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
63 args[_key] = arguments[_key];
64 }
65
66 _this = _super.call.apply(_super, [this].concat(args));
67 _this.state = createFormState(_this.props.initialValues, _this.props.externalErrors);
68 _this.mounted = false;
69 _this.fieldsWithHandlers = new WeakMap();
70
71 _this.reset = function () {
72 return new Promise(function (resolve) {
73 _this.setState(function (_state, props) {
74 return createFormState(props.initialValues, props.externalErrors);
75 }, function () {
76 return resolve();
77 });
78 });
79 };
80
81 _this.submit = /*#__PURE__*/function () {
82 var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(event) {
83 var _this$props, onSubmit, validateOnSubmit, _assertThisInitialize, formData, clientErrors, errors;
84
85 return _regeneratorRuntime.wrap(function _callee$(_context) {
86 while (1) {
87 switch (_context.prev = _context.next) {
88 case 0:
89 _this$props = _this.props, onSubmit = _this$props.onSubmit, validateOnSubmit = _this$props.validateOnSubmit;
90 _assertThisInitialize = _assertThisInitialized(_this), formData = _assertThisInitialize.formData;
91
92 if (_this.mounted) {
93 _context.next = 4;
94 break;
95 }
96
97 return _context.abrupt("return");
98
99 case 4:
100 if (event && event.preventDefault && !event.defaultPrevented) {
101 event.preventDefault();
102 }
103
104 if (!(onSubmit == null)) {
105 _context.next = 7;
106 break;
107 }
108
109 return _context.abrupt("return");
110
111 case 7:
112 _this.setState({
113 submitting: true
114 });
115
116 if (!validateOnSubmit) {
117 _context.next = 15;
118 break;
119 }
120
121 _context.next = 11;
122 return _this.validateForm();
123
124 case 11:
125 clientErrors = _this.clientErrors;
126
127 if (!(clientErrors.length > 0)) {
128 _context.next = 15;
129 break;
130 }
131
132 _this.setState({
133 submitting: false,
134 errors: clientErrors
135 });
136
137 return _context.abrupt("return");
138
139 case 15:
140 _context.next = 17;
141 return onSubmit(formData);
142
143 case 17:
144 _context.t0 = _context.sent;
145
146 if (_context.t0) {
147 _context.next = 20;
148 break;
149 }
150
151 _context.t0 = [];
152
153 case 20:
154 errors = _context.t0;
155
156 if (_this.mounted) {
157 _context.next = 23;
158 break;
159 }
160
161 return _context.abrupt("return");
162
163 case 23:
164 if (errors.length > 0) {
165 _this.updateRemoteErrors(errors);
166
167 _this.setState({
168 submitting: false
169 });
170 } else {
171 _this.setState({
172 submitting: false,
173 errors: errors
174 });
175 }
176
177 case 24:
178 case "end":
179 return _context.stop();
180 }
181 }
182 }, _callee);
183 }));
184
185 return function (_x) {
186 return _ref.apply(this, arguments);
187 };
188 }();
189
190 _this.fieldWithHandlers = function (field, fieldPath) {
191 if (_this.fieldsWithHandlers.has(field)) {
192 return _this.fieldsWithHandlers.get(field);
193 }
194
195 var result = _objectSpread(_objectSpread({}, field), {}, {
196 name: String(fieldPath),
197 onChange: _this.updateField.bind(_assertThisInitialized(_this), fieldPath),
198 onBlur: _this.blurField.bind(_assertThisInitialized(_this), fieldPath)
199 });
200
201 _this.fieldsWithHandlers.set(field, result);
202
203 return result;
204 };
205
206 return _this;
207 }
208
209 _createClass(FormState, [{
210 key: "componentDidMount",
211 value: function componentDidMount() {
212 this.mounted = true;
213 }
214 }, {
215 key: "componentWillUnmount",
216 value: function componentWillUnmount() {
217 this.mounted = false;
218 }
219 }, {
220 key: "render",
221 value: function render() {
222 var children = this.props.children;
223 var submitting = this.state.submitting;
224 var submit = this.submit,
225 reset = this.reset,
226 formData = this.formData;
227 return children(_objectSpread(_objectSpread({}, formData), {}, {
228 submit: submit,
229 reset: reset,
230 submitting: submitting
231 }));
232 } // eslint-disable-next-line @shopify/react-prefer-private-members
233
234 }, {
235 key: "validateForm",
236 value: function validateForm() {
237 var _this2 = this;
238
239 return new Promise(function (resolve) {
240 _this2.setState(runAllValidators, function () {
241 return resolve();
242 });
243 });
244 } // eslint-disable-next-line @shopify/react-prefer-private-members
245
246 }, {
247 key: "updateField",
248 value: function updateField(fieldPath, value) {
249 var _this3 = this;
250
251 this.setState(function (_ref2) {
252 var fields = _ref2.fields,
253 dirtyFields = _ref2.dirtyFields;
254 var field = fields[fieldPath];
255 var newValue = typeof value === 'function' ? value(field.value) : value;
256 var dirty = !isEqual(newValue, field.initialValue);
257
258 var updatedField = _this3.getUpdatedField({
259 fieldPath: fieldPath,
260 field: field,
261 value: newValue,
262 dirty: dirty
263 });
264
265 return {
266 dirtyFields: _this3.getUpdatedDirtyFields({
267 fieldPath: fieldPath,
268 dirty: dirty,
269 dirtyFields: dirtyFields
270 }),
271 fields: updatedField === field ? fields : _objectSpread(_objectSpread({}, fields), {}, _defineProperty({}, fieldPath, updatedField))
272 };
273 });
274 }
275 }, {
276 key: "getUpdatedDirtyFields",
277 value: function getUpdatedDirtyFields(_ref3) {
278 var fieldPath = _ref3.fieldPath,
279 dirty = _ref3.dirty,
280 dirtyFields = _ref3.dirtyFields;
281 var dirtyFieldsSet = new Set(dirtyFields);
282
283 if (dirty) {
284 dirtyFieldsSet.add(fieldPath);
285 } else {
286 dirtyFieldsSet["delete"](fieldPath);
287 }
288
289 var newDirtyFields = Array.from(dirtyFieldsSet);
290 return dirtyFields.length === newDirtyFields.length ? dirtyFields : newDirtyFields;
291 }
292 }, {
293 key: "getUpdatedField",
294 value: function getUpdatedField(_ref4) {
295 var fieldPath = _ref4.fieldPath,
296 field = _ref4.field,
297 value = _ref4.value,
298 dirty = _ref4.dirty;
299 // We only want to update errors as the user types if they already have an error.
300 // https://polaris.shopify.com/patterns/error-messages#section-form-validation
301 var skipValidation = field.error == null;
302 var error = skipValidation ? field.error : this.validateFieldValue(fieldPath, {
303 value: value,
304 dirty: dirty
305 });
306
307 if (value === field.value && error === field.error) {
308 return field;
309 }
310
311 return _objectSpread(_objectSpread({}, field), {}, {
312 value: value,
313 dirty: dirty,
314 error: error
315 });
316 }
317 }, {
318 key: "blurField",
319 value: function blurField(fieldPath) {
320 var fields = this.state.fields;
321 var field = fields[fieldPath];
322 var error = this.validateFieldValue(fieldPath, field);
323
324 if (error == null) {
325 return;
326 }
327
328 this.setState(function (state) {
329 return {
330 fields: _objectSpread(_objectSpread({}, state.fields), {}, _defineProperty({}, fieldPath, _objectSpread(_objectSpread({}, state.fields[fieldPath]), {}, {
331 error: error
332 })))
333 };
334 });
335 }
336 }, {
337 key: "validateFieldValue",
338 value: function validateFieldValue(fieldPath, _ref5) {
339 var value = _ref5.value,
340 dirty = _ref5.dirty;
341
342 if (!dirty) {
343 return;
344 }
345
346 var _this$props$validator = this.props.validators,
347 validators = _this$props$validator === void 0 ? {} : _this$props$validator;
348 var fields = this.state.fields;
349 return runValidator(validators[fieldPath], value, fields);
350 }
351 }, {
352 key: "updateRemoteErrors",
353 value: function updateRemoteErrors(errors) {
354 this.setState(function (_ref6) {
355 var fields = _ref6.fields,
356 externalErrors = _ref6.externalErrors;
357 return {
358 errors: errors,
359 fields: fieldsWithErrors(fields, [].concat(_toConsumableArray(errors), _toConsumableArray(externalErrors)))
360 };
361 });
362 }
363 }, {
364 key: "formData",
365 get: function get() {
366 var errors = this.state.errors;
367 var _this$props$externalE = this.props.externalErrors,
368 externalErrors = _this$props$externalE === void 0 ? [] : _this$props$externalE;
369 var fields = this.fields,
370 dirty = this.dirty,
371 valid = this.valid;
372 return {
373 dirty: dirty,
374 valid: valid,
375 errors: [].concat(_toConsumableArray(errors), _toConsumableArray(externalErrors)),
376 fields: fields
377 };
378 }
379 }, {
380 key: "dirty",
381 get: function get() {
382 return this.state.dirtyFields.length > 0;
383 }
384 }, {
385 key: "valid",
386 get: function get() {
387 var _this$state = this.state,
388 errors = _this$state.errors,
389 externalErrors = _this$state.externalErrors;
390 return !this.hasClientErrors && errors.length === 0 && externalErrors.length === 0;
391 }
392 }, {
393 key: "hasClientErrors",
394 get: function get() {
395 var fields = this.state.fields;
396 return Object.keys(fields).some(function (fieldPath) {
397 var field = fields[fieldPath];
398 return field.error != null;
399 });
400 }
401 }, {
402 key: "clientErrors",
403 get: function get() {
404 var fields = this.state.fields;
405 return flatMap(Object.values(fields), function (_ref7) {
406 var error = _ref7.error;
407 return collectErrors(error);
408 });
409 }
410 }, {
411 key: "fields",
412 get: function get() {
413 var fields = this.state.fields;
414 var fieldDescriptors = mapObject(fields, this.fieldWithHandlers);
415 return fieldDescriptors;
416 }
417 }], [{
418 key: "getDerivedStateFromProps",
419 value: function getDerivedStateFromProps(newProps, oldState) {
420 var initialValues = newProps.initialValues,
421 onInitialValuesChange = newProps.onInitialValuesChange,
422 _newProps$externalErr = newProps.externalErrors,
423 externalErrors = _newProps$externalErr === void 0 ? [] : _newProps$externalErr;
424 var externalErrorsChanged = !isEqual(externalErrors, oldState.externalErrors);
425 var updatedExternalErrors = externalErrorsChanged ? {
426 externalErrors: externalErrors,
427 fields: fieldsWithErrors(oldState.fields, [].concat(_toConsumableArray(externalErrors), _toConsumableArray(oldState.errors)))
428 } : null;
429
430 switch (onInitialValuesChange) {
431 case 'ignore':
432 return updatedExternalErrors;
433
434 case 'reset-where-changed':
435 return reconcileFormState(initialValues, oldState, externalErrors);
436
437 case 'reset-all':
438 default:
439 var oldInitialValues = initialValuesFromFields(oldState.fields);
440 var valuesMatch = isEqual(oldInitialValues, initialValues);
441
442 if (valuesMatch) {
443 return updatedExternalErrors;
444 }
445
446 return createFormState(initialValues, externalErrors);
447 }
448 }
449 }]);
450
451 return FormState;
452}(React.PureComponent);
453
454FormState.List = List;
455FormState.Nested = Nested;
456export { FormState as default };
457
458function fieldsWithErrors(fields, errors) {
459 var errorDictionary = errors.reduce(function (accumulator, _ref8) {
460 var field = _ref8.field,
461 message = _ref8.message;
462
463 if (field == null) {
464 return accumulator;
465 }
466
467 return set(accumulator, field, message);
468 }, {});
469 return mapObject(fields, function (field, path) {
470 if (!errorDictionary[path]) {
471 return field;
472 }
473
474 return _objectSpread(_objectSpread({}, field), {}, {
475 error: errorDictionary[path]
476 });
477 });
478}
479
480function reconcileFormState(values, oldState) {
481 var externalErrors = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
482 var oldFields = oldState.fields;
483 var dirtyFields = new Set(oldState.dirtyFields);
484 var fields = mapObject(values, function (value, key) {
485 var oldField = oldFields[key];
486
487 if (isEqual(value, oldField.initialValue)) {
488 return oldField;
489 }
490
491 dirtyFields["delete"](key);
492 return {
493 value: value,
494 initialValue: value,
495 dirty: false
496 };
497 });
498 return _objectSpread(_objectSpread({}, oldState), {}, {
499 dirtyFields: Array.from(dirtyFields),
500 fields: fieldsWithErrors(fields, externalErrors)
501 });
502}
503
504function createFormState(values) {
505 var externalErrors = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
506 var fields = mapObject(values, function (value) {
507 return {
508 value: value,
509 initialValue: value,
510 dirty: false
511 };
512 });
513 return {
514 dirtyFields: [],
515 errors: [],
516 submitting: false,
517 externalErrors: externalErrors,
518 fields: fieldsWithErrors(fields, externalErrors)
519 };
520}
521
522function initialValuesFromFields(fields) {
523 return mapObject(fields, function (_ref9) {
524 var initialValue = _ref9.initialValue;
525 return initialValue;
526 });
527}
528
529function runValidator() {
530 var validate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
531 var value = arguments.length > 1 ? arguments[1] : undefined;
532 var fields = arguments.length > 2 ? arguments[2] : undefined;
533
534 if (typeof validate === 'function') {
535 return validate(value, fields);
536 }
537
538 if (!Array.isArray(validate)) {
539 return;
540 }
541
542 var errors = validate.map(function (validator) {
543 return validator(value, fields);
544 }).filter(function (input) {
545 return input != null;
546 });
547
548 if (errors.length === 0) {
549 return;
550 }
551
552 return errors;
553}
554
555function runAllValidators(state, props) {
556 var fields = state.fields;
557 var validators = props.validators;
558
559 if (!validators) {
560 return null;
561 }
562
563 var updatedFields = mapObject(fields, function (field, path) {
564 return _objectSpread(_objectSpread({}, field), {}, {
565 error: runValidator(validators[path], field.value, fields)
566 });
567 });
568 return _objectSpread(_objectSpread({}, state), {}, {
569 fields: updatedFields
570 });
571}
572
573function collectErrors(message) {
574 if (!message) {
575 return [];
576 }
577
578 if (typeof message === 'string') {
579 return [{
580 message: message
581 }];
582 }
583
584 if (Array.isArray(message)) {
585 return flatMap(message, function (itemError) {
586 return collectErrors(itemError);
587 });
588 }
589
590 return flatMap(Object.values(message), function (nestedError) {
591 return collectErrors(nestedError);
592 });
593}
\No newline at end of file