gform-react 2.0.1 → 2.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/gform-react.development.js +314 -213
- package/dist/cjs/gform-react.development.js.map +1 -1
- package/dist/cjs/gform-react.production.js +1 -1
- package/dist/cjs/gform-react.production.js.map +1 -1
- package/dist/esm/GForm.production.js +1 -1
- package/dist/esm/GForm.production.js.map +1 -1
- package/dist/esm/GInput.production.js +1 -1
- package/dist/esm/GInput.production.js.map +1 -1
- package/dist/esm/GValidator.production.js +1 -1
- package/dist/esm/GValidator.production.js.map +1 -1
- package/dist/esm/index.development.js +320 -218
- package/dist/esm/index.development.js.map +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/shared.production.js +1 -1
- package/dist/esm/shared.production.js.map +1 -1
- package/dist/index.d.ts +13 -13
- package/native/dist/cjs/gform-react-native.development.js +254 -264
- package/native/dist/cjs/gform-react-native.development.js.map +1 -1
- package/native/dist/cjs/gform-react-native.production.js +1 -1
- package/native/dist/cjs/gform-react-native.production.js.map +1 -1
- package/native/dist/esm/RNGForm.production.js +1 -1
- package/native/dist/esm/RNGForm.production.js.map +1 -1
- package/native/dist/esm/RNGInput.production.js +1 -1
- package/native/dist/esm/RNGInput.production.js.map +1 -1
- package/native/dist/esm/index.development.js +260 -268
- package/native/dist/esm/index.development.js.map +1 -1
- package/native/dist/esm/shared.production.js +1 -1
- package/native/dist/esm/shared.production.js.map +1 -1
- package/native/dist/index.d.ts +4 -8
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var _extends = require('@babel/runtime/helpers/extends');
|
|
4
4
|
var React = require('react');
|
|
5
|
-
|
|
5
|
+
require('@babel/runtime/helpers/defineProperty');
|
|
6
6
|
var reactNative = require('react-native');
|
|
7
7
|
|
|
8
8
|
const isObject = o => o && typeof o === 'object' && !Array.isArray(o);
|
|
@@ -29,7 +29,7 @@ const _buildFormInitialValues = (rows = []) => {
|
|
|
29
29
|
const inputConfigs = _findInputs(row);
|
|
30
30
|
inputConfigs.forEach(config => {
|
|
31
31
|
if (fields[config.formKey]) {
|
|
32
|
-
console.warn(`[Duplicate Keys] - field with key '${config.formKey}' has already been defined.`);
|
|
32
|
+
console.warn(`DEV ONLY - [Duplicate Keys] - field with key '${config.formKey}' has already been defined.`);
|
|
33
33
|
}
|
|
34
34
|
const {
|
|
35
35
|
required = false,
|
|
@@ -73,10 +73,7 @@ const _buildFormInitialValues = (rows = []) => {
|
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
return {
|
|
76
|
-
|
|
77
|
-
fields,
|
|
78
|
-
loading: false
|
|
79
|
-
},
|
|
76
|
+
fields: fields,
|
|
80
77
|
key: generateId()
|
|
81
78
|
};
|
|
82
79
|
};
|
|
@@ -93,20 +90,33 @@ const _findInputs = (root, total = []) => {
|
|
|
93
90
|
}
|
|
94
91
|
return _findInputs((_root$props2 = root.props) === null || _root$props2 === void 0 ? void 0 : _root$props2.children, total);
|
|
95
92
|
};
|
|
96
|
-
const _findValidityKey = validity => {
|
|
93
|
+
const _findValidityKey = (validity, exclude = []) => {
|
|
97
94
|
for (const key in validity) {
|
|
95
|
+
if (exclude.includes(key)) continue;
|
|
98
96
|
if (key !== 'valid' && validity[key]) {
|
|
99
97
|
return key;
|
|
100
98
|
}
|
|
101
99
|
}
|
|
102
100
|
};
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
const _checkTypeMismatch = input => {
|
|
102
|
+
var _input$value;
|
|
103
|
+
const value = (_input$value = input.value) === null || _input$value === void 0 ? void 0 : _input$value.toString().trim();
|
|
104
|
+
if (!value) return false;
|
|
105
|
+
switch (input.type) {
|
|
106
|
+
case 'email':
|
|
107
|
+
return !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
|
108
|
+
case 'url':
|
|
109
|
+
try {
|
|
110
|
+
new URL(value);
|
|
111
|
+
return false;
|
|
112
|
+
} catch {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
case 'tel':
|
|
116
|
+
return !/^\+?[0-9\s\-().]{7,}$/.test(value);
|
|
117
|
+
default:
|
|
106
118
|
return false;
|
|
107
|
-
}
|
|
108
119
|
}
|
|
109
|
-
return true;
|
|
110
120
|
};
|
|
111
121
|
const _toRawData = (fields, options = {}) => {
|
|
112
122
|
const data = {};
|
|
@@ -206,7 +216,8 @@ let validityMap;
|
|
|
206
216
|
pattern: 'withPatternMismatchMessage',
|
|
207
217
|
min: 'withRangeUnderflowMessage',
|
|
208
218
|
max: 'withRangeOverflowMessage',
|
|
209
|
-
step: 'withStepMismatchMessage'
|
|
219
|
+
step: 'withStepMismatchMessage',
|
|
220
|
+
type: 'withTypeMismatchMessage'
|
|
210
221
|
};
|
|
211
222
|
validityMap = {
|
|
212
223
|
tooShort: 'minLength',
|
|
@@ -215,134 +226,27 @@ let validityMap;
|
|
|
215
226
|
patternMismatch: 'pattern',
|
|
216
227
|
rangeOverflow: 'max',
|
|
217
228
|
rangeUnderflow: 'min',
|
|
218
|
-
stepMismatch: 'step'
|
|
229
|
+
stepMismatch: 'step',
|
|
230
|
+
typeMismatch: 'type'
|
|
219
231
|
};
|
|
220
232
|
}
|
|
221
|
-
class GValidator {
|
|
222
|
-
get handlers() {
|
|
223
|
-
return this._handlers;
|
|
224
|
-
}
|
|
225
|
-
get constraintHandlers() {
|
|
226
|
-
return this._constraintHandlers;
|
|
227
|
-
}
|
|
228
|
-
get asyncHandlers() {
|
|
229
|
-
return this._asyncHandlers;
|
|
230
|
-
}
|
|
231
|
-
constructor(baseValidator) {
|
|
232
|
-
_defineProperty(this, "_handlers", void 0);
|
|
233
|
-
_defineProperty(this, "_constraintHandlers", void 0);
|
|
234
|
-
_defineProperty(this, "_asyncHandlers", void 0);
|
|
235
|
-
_defineProperty(this, "track", void 0);
|
|
236
|
-
const baseHandlers = (baseValidator === null || baseValidator === void 0 ? void 0 : baseValidator.handlers) || [];
|
|
237
|
-
const baseConstraintHandlers = (baseValidator === null || baseValidator === void 0 ? void 0 : baseValidator.constraintHandlers) || [];
|
|
238
|
-
const baseHandlersAsync = (baseValidator === null || baseValidator === void 0 ? void 0 : baseValidator.asyncHandlers) || [];
|
|
239
|
-
this._handlers = [].concat(baseHandlers);
|
|
240
|
-
this._constraintHandlers = [].concat(baseConstraintHandlers);
|
|
241
|
-
this._asyncHandlers = [].concat(baseHandlersAsync);
|
|
242
|
-
{
|
|
243
|
-
this.track = [];
|
|
244
|
-
if (baseValidator !== null && baseValidator !== void 0 && baseValidator.track) {
|
|
245
|
-
this.track = this.track.concat(baseValidator.track);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
withRequiredMessage(message) {
|
|
250
|
-
return this.__addConstraintValidationHandler('valueMissing', message);
|
|
251
|
-
}
|
|
252
|
-
withMaxLengthMessage(message) {
|
|
253
|
-
return this.__addConstraintValidationHandler('tooLong', message);
|
|
254
|
-
}
|
|
255
|
-
withMinLengthMessage(message) {
|
|
256
|
-
return this.__addConstraintValidationHandler('tooShort', message);
|
|
257
|
-
}
|
|
258
|
-
withPatternMismatchMessage(message) {
|
|
259
|
-
return this.__addConstraintValidationHandler('patternMismatch', message);
|
|
260
|
-
}
|
|
261
|
-
withBadInputMessage(message) {
|
|
262
|
-
return this.__addConstraintValidationHandler('badInput', message);
|
|
263
|
-
}
|
|
264
|
-
withRangeUnderflowMessage(message) {
|
|
265
|
-
return this.__addConstraintValidationHandler('rangeUnderflow', message);
|
|
266
|
-
}
|
|
267
|
-
withRangeOverflowMessage(message) {
|
|
268
|
-
return this.__addConstraintValidationHandler('rangeOverflow', message);
|
|
269
|
-
}
|
|
270
|
-
withTypeMismatchMessage(message) {
|
|
271
|
-
return this.__addConstraintValidationHandler('typeMismatch', message);
|
|
272
|
-
}
|
|
273
|
-
withStepMismatchMessage(message) {
|
|
274
|
-
return this.__addConstraintValidationHandler('stepMismatch', message);
|
|
275
|
-
}
|
|
276
|
-
withCustomValidation(handler) {
|
|
277
|
-
this._handlers.push(handler);
|
|
278
|
-
return this;
|
|
279
|
-
}
|
|
280
|
-
withCustomValidationAsync(handler) {
|
|
281
|
-
this._asyncHandlers.push(handler);
|
|
282
|
-
return this;
|
|
283
|
-
}
|
|
284
|
-
__addConstraintValidationHandler(validityKey, message) {
|
|
285
|
-
if (this.track) {
|
|
286
|
-
if (this.track.includes(validityKey)) {
|
|
287
|
-
console.warn(`[Duplicate Handlers] - handler for '${validityKey}' has already been defined`);
|
|
288
|
-
}
|
|
289
|
-
this.track.push(validityKey);
|
|
290
|
-
}
|
|
291
|
-
this._constraintHandlers.push((input, key) => {
|
|
292
|
-
{
|
|
293
|
-
if (validityKey && validityMap[validityKey] && typeof input[validityMap[validityKey]] === 'undefined') {
|
|
294
|
-
console.warn(`[Missing Prop] - the input '${input.formKey}' has registered validator for the violation '${validityKey}' but the input hasn't described the constraint '${validityMap[validityKey]}'.\nadd '${validityMap[validityKey]}' to the input props.\nexample:\n<GInput formKey='${input.formKey}' ${validityMap[validityKey]}={...} />\n\nor either remove '.${handlersMap[validityMap[validityKey]]}(...)' validation`);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
if (key === validityKey) {
|
|
298
|
-
input.errorText = typeof message === 'string' ? message : message(input);
|
|
299
|
-
return true;
|
|
300
|
-
}
|
|
301
|
-
return false;
|
|
302
|
-
});
|
|
303
|
-
return this;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
233
|
|
|
307
|
-
const
|
|
308
|
-
const initialValues = React.useMemo(() => {
|
|
309
|
-
const values = _buildFormInitialValues(typeof children === 'function' ? children({}) : children);
|
|
310
|
-
{
|
|
311
|
-
Object.keys(values.state.fields).forEach(key => {
|
|
312
|
-
const input = values.state.fields[key];
|
|
313
|
-
const validator = validators[key];
|
|
314
|
-
if (validator instanceof GValidator) {
|
|
315
|
-
var _validator$track;
|
|
316
|
-
const validityKeys = (_validator$track = validator.track) === null || _validator$track === void 0 ? void 0 : _validator$track.filter(key => validityMap[key]);
|
|
317
|
-
validityKeys === null || validityKeys === void 0 || validityKeys.forEach(vKey => {
|
|
318
|
-
if (typeof input[validityMap[vKey]] === 'undefined') {
|
|
319
|
-
console.warn(`[Missing Prop] - the input '${input.formKey}' has registered validator for the violation '${vKey}' but the input hasn't described the constraint '${validityMap[vKey]}'.\nadd '${validityMap[vKey]}' to the input props.\nexample:\n<GInput formKey='${input.formKey}' ${validityMap[vKey]}={...} />\n\nor either remove '.${handlersMap[validityMap[vKey]]}(...)' validation`);
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
Object.entries(validityMap).forEach(([validityKey, constraint]) => {
|
|
323
|
-
var _validator$track2;
|
|
324
|
-
if (typeof input[constraint] !== 'undefined' && !((_validator$track2 = validator.track) !== null && _validator$track2 !== void 0 && _validator$track2.some(trackKey => validityKey === trackKey))) {
|
|
325
|
-
console.warn(`[Missing Validator] - the input '${input.formKey}' has described the constraint '${constraint}' but the input hasn't registered a validator to handle it.\nregister a handler '${handlersMap[constraint]}' for the input validator to handle the '${validityKey}' violation.\nexample:\ncosnt validators = {\n\t${input.formKey}: new GValidator().${handlersMap[constraint]}(...)\n}`);
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
return values;
|
|
332
|
-
}, []);
|
|
333
|
-
const [state, setState] = React.useState(initialValues.state);
|
|
234
|
+
const useFormHandlers = (getState, setState, validators = {}, optimized = false) => {
|
|
334
235
|
const _viHandler = (input, e) => {
|
|
335
236
|
if (!input) return;
|
|
336
237
|
const element = e && e.target;
|
|
238
|
+
const hasInitialValue = !input.dirty && input.value && !input.touched;
|
|
239
|
+
if (!element && !hasInitialValue) return;
|
|
337
240
|
if (typeof document !== 'undefined' && (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) {
|
|
338
241
|
if (!input.checkValidity) input.checkValidity = () => element.checkValidity();
|
|
339
|
-
if (
|
|
242
|
+
if (hasInitialValue) {
|
|
340
243
|
_checkInputManually(input);
|
|
341
244
|
_dispatchChanges(input, input.formKey);
|
|
342
245
|
return;
|
|
343
246
|
}
|
|
344
247
|
element.setCustomValidity('');
|
|
345
|
-
const
|
|
248
|
+
const exclude = input.type && (input.pattern || hasCustomValidation(input)) ? ['typeMismatch'] : [];
|
|
249
|
+
const validityKey = _findValidityKey(element.validity, exclude);
|
|
346
250
|
_validateInput(input, validityKey, v => element.setCustomValidity(v));
|
|
347
251
|
if (!validityKey && input.error) {
|
|
348
252
|
element.setCustomValidity(input.errorText || 'error');
|
|
@@ -355,36 +259,38 @@ const useForm = (children, validators = {}, optimized = false) => {
|
|
|
355
259
|
}
|
|
356
260
|
};
|
|
357
261
|
const _checkInputManually = input => {
|
|
262
|
+
const exclude = input.type && (input.pattern || hasCustomValidation(input)) ? ['typeMismatch'] : [];
|
|
358
263
|
let validityKey = _findValidityKey({
|
|
359
264
|
valueMissing: input.required && !input.value || false,
|
|
265
|
+
typeMismatch: _checkTypeMismatch(input),
|
|
360
266
|
tooShort: input.minLength && input.value.toString().length < input.minLength || false,
|
|
361
267
|
tooLong: input.maxLength && input.value.toString().length > input.maxLength || false,
|
|
362
268
|
patternMismatch: input.pattern && _checkResult(input.pattern, input.value) || false,
|
|
363
269
|
rangeUnderflow: input.min && Number(input.value) < Number(input.min) || false,
|
|
364
270
|
rangeOverflow: input.max && Number(input.value) > Number(input.max) || false
|
|
365
|
-
});
|
|
271
|
+
}, exclude);
|
|
366
272
|
if (!validityKey && input.error) {
|
|
367
273
|
validityKey = 'customError';
|
|
368
274
|
}
|
|
369
275
|
_validateInput(input, validityKey);
|
|
370
276
|
return !input.error;
|
|
371
277
|
};
|
|
372
|
-
const _updateInputHandler = (
|
|
373
|
-
|
|
374
|
-
const input = _updateInput(key, value);
|
|
278
|
+
const _updateInputHandler = (input, e, unknown) => {
|
|
279
|
+
input.value = _extractValue(e, unknown);
|
|
375
280
|
_viHandler(input, e);
|
|
376
281
|
};
|
|
377
282
|
const _validateInput = (input, validityKey, setValidity) => {
|
|
378
283
|
const inputValidator = validators[input.validatorKey || input.formKey] || validators['*'];
|
|
379
|
-
|
|
284
|
+
{
|
|
285
|
+
if (validityKey && !(inputValidator !== null && inputValidator !== void 0 && inputValidator.hasConstraint(validityKey))) {
|
|
286
|
+
if (validityKey === 'typeMismatch') console.warn(`DEV ONLY - [Missing Validator] - the input '${input.formKey}' has described the constraint '${validityMap[validityKey]}' however, a correspond validator / custom validation / pattern validator are missing.\nadd '${handlersMap[validityMap[validityKey]]}' or 'withCustomValidation' or '${handlersMap[validityMap.patternMismatch]}' to the input validator.\nexample:\nconst validators: GValidators = {\n\temail: new GValidator().withPatternMismatchMessage('pattern mismatch'),\n\t...\n}\n\nor either remove the constraint '${validityMap[validityKey]}' from the input props`);else console.warn(`DEV ONLY - [Missing Validator] - the input '${input.formKey}' has described the constraint '${validityMap[validityKey]}' however, a correspond validator is missing.\nadd '${handlersMap[validityMap[validityKey]]}' to the input validator.\nexample:\nconst validators: GValidators = {\n\temail: new GValidator().withPatternMismatchMessage('pattern mismatch'),\n\t...\n}\n\nor either remove the constraint '${validityMap[validityKey]}' from the input props`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (inputValidator) {
|
|
290
|
+
__validateInput(input, inputValidator, validityKey, setValidity);
|
|
291
|
+
}
|
|
380
292
|
input.touched = true;
|
|
381
293
|
};
|
|
382
|
-
const _updateInput = (key, value) => {
|
|
383
|
-
const input = state.fields[key];
|
|
384
|
-
input.value = value;
|
|
385
|
-
input.dirty = true;
|
|
386
|
-
return input;
|
|
387
|
-
};
|
|
388
294
|
const _dispatchChanges = (changes, key) => setState(prev => {
|
|
389
295
|
if (key) {
|
|
390
296
|
return {
|
|
@@ -404,13 +310,14 @@ const useForm = (children, validators = {}, optimized = false) => {
|
|
|
404
310
|
};
|
|
405
311
|
});
|
|
406
312
|
const __validateInput = (input, inputValidator, validityKey, setValidity) => {
|
|
313
|
+
const fields = getState().fields;
|
|
407
314
|
for (const index in inputValidator.constraintHandlers) {
|
|
408
315
|
const result = inputValidator.constraintHandlers[index](input, validityKey);
|
|
409
316
|
input.error = _checkResult(result, input.value);
|
|
410
317
|
if (input.error) return;
|
|
411
318
|
}
|
|
412
319
|
for (const index in inputValidator.handlers) {
|
|
413
|
-
const result = inputValidator.handlers[index](input,
|
|
320
|
+
const result = inputValidator.handlers[index](input, fields);
|
|
414
321
|
input.error = _checkResult(result, input.value);
|
|
415
322
|
if (input.error) return;
|
|
416
323
|
}
|
|
@@ -420,7 +327,7 @@ const useForm = (children, validators = {}, optimized = false) => {
|
|
|
420
327
|
_debounce(input.debounce || 300, `${input.gid}-async`).then(() => {
|
|
421
328
|
const validateAsync = async () => {
|
|
422
329
|
for (const index in inputValidator.asyncHandlers) {
|
|
423
|
-
const result = await inputValidator.asyncHandlers[index](input,
|
|
330
|
+
const result = await inputValidator.asyncHandlers[index](input, fields);
|
|
424
331
|
input.error = _checkResult(result, input.value);
|
|
425
332
|
if (input.error) break;
|
|
426
333
|
}
|
|
@@ -429,138 +336,206 @@ const useForm = (children, validators = {}, optimized = false) => {
|
|
|
429
336
|
error: input.error,
|
|
430
337
|
errorText: input.errorText
|
|
431
338
|
}, input.formKey);
|
|
432
|
-
|
|
339
|
+
if (setValidity) {
|
|
340
|
+
setValidity(input.errorText);
|
|
341
|
+
}
|
|
433
342
|
};
|
|
434
343
|
validateAsync();
|
|
435
344
|
});
|
|
436
345
|
}
|
|
437
346
|
};
|
|
347
|
+
const hasCustomValidation = input => {
|
|
348
|
+
const validator = validators[input.validatorKey || input.formKey] || validators['*'];
|
|
349
|
+
return validator && (validator.asyncHandlers.length > 0 || validator.handlers.length > 0);
|
|
350
|
+
};
|
|
438
351
|
return {
|
|
439
|
-
state,
|
|
440
352
|
_updateInputHandler,
|
|
441
353
|
_viHandler,
|
|
442
354
|
_dispatchChanges,
|
|
443
355
|
optimized,
|
|
444
|
-
key: initialValues.key,
|
|
445
356
|
_createInputChecker: _checkInputManually
|
|
446
357
|
};
|
|
447
358
|
};
|
|
448
359
|
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
360
|
+
const GFormContext = React.createContext({});
|
|
361
|
+
const GFormContextProvider = ({
|
|
362
|
+
children,
|
|
363
|
+
initialState,
|
|
364
|
+
validators,
|
|
365
|
+
optimized
|
|
366
|
+
}) => {
|
|
367
|
+
const stateRef = React.useRef(initialState);
|
|
368
|
+
const listeners = React.useRef(new Set());
|
|
369
|
+
const setState = React.useCallback(updater => {
|
|
370
|
+
stateRef.current = typeof updater === 'function' ? updater(stateRef.current) : updater;
|
|
371
|
+
listeners.current.forEach(l => l());
|
|
372
|
+
}, []);
|
|
373
|
+
const handlers = useFormHandlers(() => stateRef.current, setState, validators, optimized);
|
|
374
|
+
const getState = React.useCallback(() => stateRef.current, []);
|
|
375
|
+
const subscribe = React.useCallback(listener => {
|
|
376
|
+
listeners.current.add(listener);
|
|
377
|
+
return () => listeners.current.delete(listener);
|
|
378
|
+
}, []);
|
|
379
|
+
React.useEffect(() => {
|
|
380
|
+
for (const fieldKey in initialState.fields) {
|
|
381
|
+
initialState.fields[fieldKey].dispatchChanges = changes => handlers._dispatchChanges(changes, fieldKey);
|
|
382
|
+
}
|
|
383
|
+
}, []);
|
|
384
|
+
const store = React.useRef({
|
|
385
|
+
getState,
|
|
386
|
+
setState,
|
|
387
|
+
subscribe,
|
|
388
|
+
handlers
|
|
389
|
+
});
|
|
390
|
+
return React.createElement(GFormContext.Provider, {
|
|
391
|
+
value: store.current
|
|
392
|
+
}, children);
|
|
393
|
+
};
|
|
394
|
+
const useFormStore = () => {
|
|
395
|
+
const store = React.useContext(GFormContext);
|
|
396
|
+
if (!store) throw new Error('useGFormStore must be used within `GForm` component');
|
|
397
|
+
return store;
|
|
398
|
+
};
|
|
399
|
+
const useFormSelector = selector => {
|
|
400
|
+
const store = useFormStore();
|
|
401
|
+
return React.useSyncExternalStore(store.subscribe, () => selector(store.getState()), () => selector(store.getState()));
|
|
402
|
+
};
|
|
403
|
+
function createSelector(selectors, combiner) {
|
|
404
|
+
let lastArgs = [];
|
|
405
|
+
let lastResult;
|
|
406
|
+
return state => {
|
|
407
|
+
const args = selectors.map(fn => fn(state));
|
|
408
|
+
if (lastArgs.length === args.length && args.every((val, i) => val === lastArgs[i])) {
|
|
409
|
+
return lastResult;
|
|
410
|
+
}
|
|
411
|
+
lastArgs = args;
|
|
412
|
+
lastResult = combiner(...args);
|
|
413
|
+
return lastResult;
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const selectFields = [state => state.fields];
|
|
418
|
+
const selectFirstInvalidField = createSelector(selectFields, fields => {
|
|
419
|
+
for (const f in fields) {
|
|
420
|
+
if (fields[f].error) {
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return false;
|
|
425
|
+
});
|
|
426
|
+
const makeSelectFields = (keys = []) => createSelector(selectFields, fields => {
|
|
427
|
+
const selected = keys.map(key => fields[key]).filter(Boolean);
|
|
428
|
+
return selected.length ? selected : null;
|
|
460
429
|
});
|
|
461
|
-
const useGenericFormContext = () => React.useContext(gFormContext);
|
|
462
|
-
const GFormContextProvider = gFormContext.Provider;
|
|
463
430
|
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
toURLSearchParams: _toURLSearchParams,
|
|
490
|
-
checkValidity: () => {
|
|
491
|
-
for (const i in state.fields) {
|
|
492
|
-
const valid = state.fields[i].checkValidity();
|
|
493
|
-
if (!valid) {
|
|
494
|
-
return false;
|
|
495
|
-
}
|
|
431
|
+
const FormRenderer = React.forwardRef(({
|
|
432
|
+
stateRef,
|
|
433
|
+
children,
|
|
434
|
+
onInit,
|
|
435
|
+
el: El,
|
|
436
|
+
...rest
|
|
437
|
+
}, ref) => {
|
|
438
|
+
const {
|
|
439
|
+
getState,
|
|
440
|
+
handlers
|
|
441
|
+
} = useFormStore();
|
|
442
|
+
const isFormInvalid = useFormSelector(selectFirstInvalidField);
|
|
443
|
+
const getFormState = React.useCallback(() => {
|
|
444
|
+
const fields = getState().fields;
|
|
445
|
+
const formState = {
|
|
446
|
+
...fields,
|
|
447
|
+
isValid: !isFormInvalid,
|
|
448
|
+
isInvalid: isFormInvalid,
|
|
449
|
+
toRawData: options => _toRawData(fields, options),
|
|
450
|
+
toURLSearchParams: _toURLSearchParams,
|
|
451
|
+
checkValidity: () => {
|
|
452
|
+
for (const i in fields) {
|
|
453
|
+
const valid = fields[i].checkValidity();
|
|
454
|
+
if (!valid) {
|
|
455
|
+
return false;
|
|
496
456
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
})
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
457
|
+
}
|
|
458
|
+
return true;
|
|
459
|
+
},
|
|
460
|
+
dispatchChanges: changes => handlers._dispatchChanges({
|
|
461
|
+
fields: _merge({}, fields, changes)
|
|
462
|
+
})
|
|
463
|
+
};
|
|
464
|
+
if (stateRef) stateRef.current = formState;
|
|
465
|
+
return formState;
|
|
466
|
+
}, [isFormInvalid]);
|
|
467
|
+
const formComponent = React.useMemo(() => {
|
|
468
|
+
const state = getFormState();
|
|
469
|
+
const formChildren = typeof children === 'function' ? children(state) : children;
|
|
470
|
+
return React.createElement(El, _extends({}, rest, {
|
|
471
|
+
ref: ref
|
|
472
|
+
}), formChildren);
|
|
473
|
+
}, [getFormState, children]);
|
|
474
|
+
React.useEffect(() => {
|
|
475
|
+
const state = getFormState();
|
|
476
|
+
if (onInit) {
|
|
477
|
+
const changes = onInit(state);
|
|
478
|
+
if (changes) {
|
|
479
|
+
const _handler = _c => handlers._dispatchChanges({
|
|
480
|
+
fields: _merge({}, state, _c)
|
|
513
481
|
});
|
|
514
|
-
|
|
515
|
-
|
|
482
|
+
if (changes instanceof Promise) {
|
|
483
|
+
changes.then(_handler);
|
|
484
|
+
} else _handler(changes);
|
|
516
485
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
486
|
+
}
|
|
487
|
+
const dispatchers = {};
|
|
488
|
+
const fields = getState().fields;
|
|
489
|
+
for (const fieldKey in fields) {
|
|
490
|
+
dispatchers[fieldKey] = {
|
|
491
|
+
dispatchChanges: changes => handlers._dispatchChanges(changes, fieldKey),
|
|
492
|
+
checkValidity: () => {
|
|
493
|
+
const result = handlers._createInputChecker(state[fieldKey]);
|
|
494
|
+
handlers._dispatchChanges(state[fieldKey], fieldKey);
|
|
495
|
+
return result;
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
const field = fields[fieldKey];
|
|
499
|
+
if (!field.value) continue;
|
|
500
|
+
handlers._viHandler(field);
|
|
501
|
+
}
|
|
502
|
+
handlers._dispatchChanges({
|
|
503
|
+
fields: _merge(dispatchers, state)
|
|
504
|
+
});
|
|
505
|
+
}, [getFormState]);
|
|
506
|
+
return formComponent;
|
|
507
|
+
});
|
|
508
|
+
const RNGForm = React.forwardRef(({
|
|
509
|
+
children,
|
|
510
|
+
validators,
|
|
511
|
+
...props
|
|
512
|
+
}, ref) => {
|
|
513
|
+
const initialState = React.useMemo(() => {
|
|
514
|
+
return _buildFormInitialValues(typeof children === 'function' ? children({}) : children);
|
|
515
|
+
}, [children]);
|
|
516
|
+
return React.createElement(GFormContextProvider, {
|
|
517
|
+
key: initialState.key,
|
|
518
|
+
initialState: initialState,
|
|
519
|
+
validators: validators
|
|
520
|
+
}, React.createElement(FormRenderer, _extends({
|
|
521
|
+
ref: ref
|
|
522
|
+
}, props), children));
|
|
523
|
+
});
|
|
541
524
|
|
|
542
|
-
const
|
|
525
|
+
const _RNGInput = React.forwardRef(({
|
|
543
526
|
formKey,
|
|
544
527
|
element,
|
|
545
528
|
type,
|
|
546
|
-
validatorKey,
|
|
547
529
|
fetch,
|
|
548
|
-
fetchDeps
|
|
530
|
+
fetchDeps,
|
|
531
|
+
debounce = 300,
|
|
549
532
|
defaultValue,
|
|
533
|
+
validatorKey,
|
|
550
534
|
value,
|
|
551
|
-
debounce = 300,
|
|
552
535
|
...rest
|
|
553
536
|
}, ref) => {
|
|
554
|
-
const
|
|
555
|
-
|
|
556
|
-
fields
|
|
557
|
-
},
|
|
558
|
-
_updateInputHandler,
|
|
559
|
-
_dispatchChanges,
|
|
560
|
-
_viHandler,
|
|
561
|
-
_createInputChecker
|
|
562
|
-
} = useGenericFormContext();
|
|
563
|
-
const inputState = fields[formKey];
|
|
537
|
+
const inputState = useFormSelector(state => state.fields[formKey]);
|
|
538
|
+
const store = useFormStore();
|
|
564
539
|
const _element = React.useMemo(() => {
|
|
565
540
|
const value = inputState.value || '';
|
|
566
541
|
const _props = {
|
|
@@ -569,41 +544,56 @@ const RNGInput = React.forwardRef(({
|
|
|
569
544
|
inputMode: type,
|
|
570
545
|
ref
|
|
571
546
|
};
|
|
572
|
-
_props.onEndEditing = e => {
|
|
573
|
-
_viHandler(inputState);
|
|
574
|
-
rest.onEndEditing
|
|
547
|
+
_props.onEndEditing = rest.onEndEditing ? e => {
|
|
548
|
+
store.handlers._viHandler(inputState);
|
|
549
|
+
rest.onEndEditing(e);
|
|
550
|
+
} : () => {
|
|
551
|
+
store.handlers._viHandler(inputState);
|
|
575
552
|
};
|
|
576
|
-
_props.onChangeText = e => {
|
|
577
|
-
_updateInputHandler(
|
|
553
|
+
_props.onChangeText = rest.onChangeText ? e => {
|
|
554
|
+
store.handlers._updateInputHandler(inputState, undefined, {
|
|
555
|
+
value: e
|
|
556
|
+
});
|
|
557
|
+
rest.onChangeText(e);
|
|
558
|
+
} : e => {
|
|
559
|
+
store.handlers._updateInputHandler(inputState, undefined, {
|
|
578
560
|
value: e
|
|
579
561
|
});
|
|
580
|
-
rest.onChangeText && rest.onChangeText(e);
|
|
581
562
|
};
|
|
563
|
+
if (!inputState.touched) {
|
|
564
|
+
_props.onFocus = rest.onFocus ? e => {
|
|
565
|
+
rest.onFocus(e);
|
|
566
|
+
inputState.dispatchChanges({
|
|
567
|
+
touched: true
|
|
568
|
+
});
|
|
569
|
+
} : () => {
|
|
570
|
+
inputState.dispatchChanges({
|
|
571
|
+
touched: true
|
|
572
|
+
});
|
|
573
|
+
};
|
|
574
|
+
}
|
|
582
575
|
if (element) {
|
|
583
576
|
return element(inputState, _props);
|
|
584
577
|
}
|
|
585
578
|
return React.createElement(reactNative.TextInput, _props);
|
|
586
579
|
}, [inputState, element]);
|
|
587
|
-
const _fetchDeps =
|
|
588
|
-
React.
|
|
589
|
-
inputState.checkValidity = () => {
|
|
590
|
-
const result = _createInputChecker(inputState);
|
|
591
|
-
_dispatchChanges(inputState, formKey);
|
|
592
|
-
return result;
|
|
593
|
-
};
|
|
594
|
-
inputState.dispatchChanges = changes => _dispatchChanges(changes, formKey);
|
|
595
|
-
_dispatchChanges(inputState, formKey);
|
|
596
|
-
}, []);
|
|
580
|
+
const _fetchDeps = useFormSelector(makeSelectFields(fetchDeps));
|
|
581
|
+
const stableFetchDeps = React.useMemo(() => JSON.stringify(_fetchDeps), [_fetchDeps]);
|
|
597
582
|
React.useEffect(() => {
|
|
598
583
|
if (fetch) {
|
|
599
584
|
_debounce(debounce, `${inputState.gid}-fetch`).then(() => {
|
|
600
|
-
const res = fetch(inputState, fields);
|
|
601
|
-
res instanceof Promise
|
|
585
|
+
const res = fetch(inputState, store.getState().fields);
|
|
586
|
+
if (res instanceof Promise) {
|
|
587
|
+
res.then(state => state && store.handlers._dispatchChanges(state, formKey));
|
|
588
|
+
} else if (res) {
|
|
589
|
+
store.handlers._dispatchChanges(res, formKey);
|
|
590
|
+
}
|
|
602
591
|
});
|
|
603
592
|
}
|
|
604
|
-
},
|
|
593
|
+
}, [stableFetchDeps]);
|
|
605
594
|
return _element;
|
|
606
595
|
});
|
|
596
|
+
const RNGInput = React.memo(_RNGInput);
|
|
607
597
|
|
|
608
598
|
exports.RNGForm = RNGForm;
|
|
609
599
|
exports.RNGInput = RNGInput;
|