gform-react 2.5.0 → 2.5.3

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.
@@ -28,7 +28,7 @@ const _buildFormInitialValues = (rows = []) => {
28
28
  const inputConfigs = _findInputs(row);
29
29
  inputConfigs.forEach(config => {
30
30
  if (fields[config.formKey]) {
31
- console.warn(`[Duplicate Keys] - field with key '${config.formKey}' has already been defined.`);
31
+ console.warn(`DEV ONLY - [Duplicate Keys] - field with key '${config.formKey}' has already been defined.`);
32
32
  }
33
33
  const {
34
34
  required = false,
@@ -89,8 +89,9 @@ const _findInputs = (root, total = []) => {
89
89
  }
90
90
  return _findInputs((_root$props2 = root.props) === null || _root$props2 === void 0 ? void 0 : _root$props2.children, total);
91
91
  };
92
- const _findValidityKey = validity => {
92
+ const _findValidityKey = (validity, exclude = []) => {
93
93
  for (const key in validity) {
94
+ if (exclude.includes(key)) continue;
94
95
  if (key !== 'valid' && validity[key]) {
95
96
  return key;
96
97
  }
@@ -123,6 +124,14 @@ const hasSubmitter = form => {
123
124
  }
124
125
  return false;
125
126
  };
127
+ const _checkIfFormIsValid = fields => {
128
+ for (const f in fields) {
129
+ if (fields[f].error) {
130
+ return false;
131
+ }
132
+ }
133
+ return true;
134
+ };
126
135
  const _toRawData = (fields, options = {}) => {
127
136
  const data = {};
128
137
  const {
@@ -225,7 +234,8 @@ let validityMap;
225
234
  pattern: 'withPatternMismatchMessage',
226
235
  min: 'withRangeUnderflowMessage',
227
236
  max: 'withRangeOverflowMessage',
228
- step: 'withStepMismatchMessage'
237
+ step: 'withStepMismatchMessage',
238
+ type: 'withTypeMismatchMessage'
229
239
  };
230
240
  validityMap = {
231
241
  tooShort: 'minLength',
@@ -234,7 +244,8 @@ let validityMap;
234
244
  patternMismatch: 'pattern',
235
245
  rangeOverflow: 'max',
236
246
  rangeUnderflow: 'min',
237
- stepMismatch: 'step'
247
+ stepMismatch: 'step',
248
+ typeMismatch: 'type'
238
249
  };
239
250
  }
240
251
  class GValidator {
@@ -265,6 +276,10 @@ class GValidator {
265
276
  }
266
277
  }
267
278
  }
279
+ hasConstraint(constraint) {
280
+ var _this$track;
281
+ return ((_this$track = this.track) === null || _this$track === void 0 ? void 0 : _this$track.includes(constraint)) || false;
282
+ }
268
283
  withRequiredMessage(message) {
269
284
  return this.__addConstraintValidationHandler('valueMissing', message);
270
285
  }
@@ -303,14 +318,14 @@ class GValidator {
303
318
  __addConstraintValidationHandler(validityKey, message) {
304
319
  if (this.track) {
305
320
  if (this.track.includes(validityKey)) {
306
- console.warn(`[Duplicate Handlers] - handler for '${validityKey}' has already been defined`);
321
+ console.warn(`DEV ONLY - [Duplicate Handlers] - handler for '${validityKey}' has already been defined`);
307
322
  }
308
323
  this.track.push(validityKey);
309
324
  }
310
325
  this._constraintHandlers.push((input, key) => {
311
326
  {
312
327
  if (validityKey && validityMap[validityKey] && typeof input[validityMap[validityKey]] === 'undefined') {
313
- 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`);
328
+ console.warn(`DEV ONLY - [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`);
314
329
  }
315
330
  }
316
331
  if (key === validityKey) {
@@ -327,15 +342,18 @@ const useFormHandlers = (getState, setState, validators = {}, optimized = false)
327
342
  const _viHandler = (input, e) => {
328
343
  if (!input) return;
329
344
  const element = e && e.target;
345
+ const hasInitialValue = !input.dirty && input.value && !input.touched;
346
+ if (!element && !hasInitialValue) return;
330
347
  if (typeof document !== 'undefined' && (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) {
331
348
  if (!input.checkValidity) input.checkValidity = () => element.checkValidity();
332
- if (!input.dirty && input.value && !input.touched) {
349
+ if (hasInitialValue) {
333
350
  _checkInputManually(input);
334
351
  _dispatchChanges(input, input.formKey);
335
352
  return;
336
353
  }
337
354
  element.setCustomValidity('');
338
- const validityKey = _findValidityKey(element.validity);
355
+ const exclude = input.type && (input.pattern || hasCustomValidation(input)) ? ['typeMismatch'] : [];
356
+ const validityKey = _findValidityKey(element.validity, exclude);
339
357
  _validateInput(input, validityKey, v => element.setCustomValidity(v));
340
358
  if (!validityKey && input.error) {
341
359
  element.setCustomValidity(input.errorText || 'error');
@@ -348,6 +366,7 @@ const useFormHandlers = (getState, setState, validators = {}, optimized = false)
348
366
  }
349
367
  };
350
368
  const _checkInputManually = input => {
369
+ const exclude = input.type && (input.pattern || hasCustomValidation(input)) ? ['typeMismatch'] : [];
351
370
  let validityKey = _findValidityKey({
352
371
  valueMissing: input.required && !input.value || false,
353
372
  typeMismatch: _checkTypeMismatch(input),
@@ -356,7 +375,7 @@ const useFormHandlers = (getState, setState, validators = {}, optimized = false)
356
375
  patternMismatch: input.pattern && _checkResult(input.pattern, input.value) || false,
357
376
  rangeUnderflow: input.min && Number(input.value) < Number(input.min) || false,
358
377
  rangeOverflow: input.max && Number(input.value) > Number(input.max) || false
359
- });
378
+ }, exclude);
360
379
  if (!validityKey && input.error) {
361
380
  validityKey = 'customError';
362
381
  }
@@ -369,6 +388,11 @@ const useFormHandlers = (getState, setState, validators = {}, optimized = false)
369
388
  };
370
389
  const _validateInput = (input, validityKey, setValidity) => {
371
390
  const inputValidator = validators[input.validatorKey || input.formKey] || validators['*'];
391
+ {
392
+ if (validityKey && !(inputValidator !== null && inputValidator !== void 0 && inputValidator.hasConstraint(validityKey))) {
393
+ 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`);
394
+ }
395
+ }
372
396
  if (inputValidator) {
373
397
  __validateInput(input, inputValidator, validityKey, setValidity);
374
398
  }
@@ -427,6 +451,10 @@ const useFormHandlers = (getState, setState, validators = {}, optimized = false)
427
451
  });
428
452
  }
429
453
  };
454
+ const hasCustomValidation = input => {
455
+ const validator = validators[input.validatorKey || input.formKey] || validators['*'];
456
+ return validator && (validator.asyncHandlers.length > 0 || validator.handlers.length > 0);
457
+ };
430
458
  return {
431
459
  _updateInputHandler,
432
460
  _viHandler,
@@ -493,20 +521,6 @@ function createSelector(selectors, combiner) {
493
521
  };
494
522
  }
495
523
 
496
- const selectFields = [state => state.fields];
497
- const selectFirstInvalidField = createSelector(selectFields, fields => {
498
- for (const f in fields) {
499
- if (fields[f].error) {
500
- return true;
501
- }
502
- }
503
- return false;
504
- });
505
- const makeSelectFields = (keys = []) => createSelector(selectFields, fields => {
506
- const selected = keys.map(key => fields[key]).filter(Boolean);
507
- return selected.length ? selected : null;
508
- });
509
-
510
524
  const FormRenderer = React.forwardRef(({
511
525
  stateRef,
512
526
  onSubmit,
@@ -523,7 +537,7 @@ const FormRenderer = React.forwardRef(({
523
537
  getState,
524
538
  handlers
525
539
  } = useFormStore();
526
- const isFormInvalid = useFormSelector(selectFirstInvalidField);
540
+ const fields = useFormSelector(state => state.fields);
527
541
  const refHandler = React.useCallback(element => {
528
542
  if (ref) {
529
543
  if (typeof ref === 'function') {
@@ -535,7 +549,7 @@ const FormRenderer = React.forwardRef(({
535
549
  formRef.current = element;
536
550
  }, [ref]);
537
551
  const getFormState = React.useCallback(() => {
538
- const fields = getState().fields;
552
+ const isFormInvalid = _checkIfFormIsValid(fields);
539
553
  const formState = {
540
554
  ...fields,
541
555
  isValid: !isFormInvalid,
@@ -554,7 +568,7 @@ const FormRenderer = React.forwardRef(({
554
568
  };
555
569
  if (stateRef) stateRef.current = formState;
556
570
  return formState;
557
- }, [isFormInvalid]);
571
+ }, [fields]);
558
572
  const formComponent = React.useMemo(() => {
559
573
  const state = getFormState();
560
574
  const formChildren = typeof children === 'function' ? children(state) : children;
@@ -614,7 +628,7 @@ const FormRenderer = React.forwardRef(({
614
628
  React.useEffect(() => {
615
629
  const state = getFormState();
616
630
  if (!hasSubmitter(formRef.current)) {
617
- console.warn(`[No Submit Button] - you have created a form without a button type=submit, this will prevent the onSubmit event from being fired.\nif you have a button with onClick event that handle the submission of the form then ignore this warning\nbut don't forget to manually invoke the checkValidity() function to check if the form is valid before perfoming any action, for example:\nif (formState.checkValidity()) { \n\t//do somthing\n}\n`);
631
+ console.warn(`DEV ONLY - [No Submit Button] - you have created a form without a button type=submit, this will prevent the onSubmit event from being fired.\nif you have a button with onClick event that handle the submission of the form then ignore this warning\nbut don't forget to manually invoke the checkValidity() function to check if the form is valid before perfoming any action, for example:\nif (formState.checkValidity()) { \n\t//do somthing\n}\n`);
618
632
  }
619
633
  if (onInit) {
620
634
  const changes = onInit(state);
@@ -655,6 +669,12 @@ const GForm = React.forwardRef(({
655
669
  }, props), children));
656
670
  });
657
671
 
672
+ const selectFields = [state => state.fields];
673
+ const makeSelectFields = (keys = []) => createSelector(selectFields, fields => {
674
+ const selected = keys.map(key => fields[key]).filter(Boolean);
675
+ return selected.length ? selected : null;
676
+ });
677
+
658
678
  const _GInput = React.forwardRef(({
659
679
  formKey,
660
680
  element,
@@ -708,6 +728,18 @@ const _GInput = React.forwardRef(({
708
728
  } : (e, unknown) => {
709
729
  store.handlers._updateInputHandler(inputState, e, unknown);
710
730
  };
731
+ if (!inputState.touched) {
732
+ _props.onFocus = rest.onFocus ? e => {
733
+ rest.onFocus(e);
734
+ inputState.dispatchChanges({
735
+ touched: true
736
+ });
737
+ } : () => {
738
+ inputState.dispatchChanges({
739
+ touched: true
740
+ });
741
+ };
742
+ }
711
743
  }
712
744
  if (element) {
713
745
  return element(inputState, _props);