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.
Files changed (30) hide show
  1. package/dist/cjs/gform-react.development.js +314 -213
  2. package/dist/cjs/gform-react.development.js.map +1 -1
  3. package/dist/cjs/gform-react.production.js +1 -1
  4. package/dist/cjs/gform-react.production.js.map +1 -1
  5. package/dist/esm/GForm.production.js +1 -1
  6. package/dist/esm/GForm.production.js.map +1 -1
  7. package/dist/esm/GInput.production.js +1 -1
  8. package/dist/esm/GInput.production.js.map +1 -1
  9. package/dist/esm/GValidator.production.js +1 -1
  10. package/dist/esm/GValidator.production.js.map +1 -1
  11. package/dist/esm/index.development.js +320 -218
  12. package/dist/esm/index.development.js.map +1 -1
  13. package/dist/esm/index.js +2 -1
  14. package/dist/esm/shared.production.js +1 -1
  15. package/dist/esm/shared.production.js.map +1 -1
  16. package/dist/index.d.ts +13 -13
  17. package/native/dist/cjs/gform-react-native.development.js +254 -264
  18. package/native/dist/cjs/gform-react-native.development.js.map +1 -1
  19. package/native/dist/cjs/gform-react-native.production.js +1 -1
  20. package/native/dist/cjs/gform-react-native.production.js.map +1 -1
  21. package/native/dist/esm/RNGForm.production.js +1 -1
  22. package/native/dist/esm/RNGForm.production.js.map +1 -1
  23. package/native/dist/esm/RNGInput.production.js +1 -1
  24. package/native/dist/esm/RNGInput.production.js.map +1 -1
  25. package/native/dist/esm/index.development.js +260 -268
  26. package/native/dist/esm/index.development.js.map +1 -1
  27. package/native/dist/esm/shared.production.js +1 -1
  28. package/native/dist/esm/shared.production.js.map +1 -1
  29. package/native/dist/index.d.ts +4 -8
  30. package/package.json +2 -2
@@ -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,
@@ -72,10 +72,7 @@ const _buildFormInitialValues = (rows = []) => {
72
72
  });
73
73
  }
74
74
  return {
75
- state: {
76
- fields,
77
- loading: false
78
- },
75
+ fields: fields,
79
76
  key: generateId()
80
77
  };
81
78
  };
@@ -92,13 +89,34 @@ const _findInputs = (root, total = []) => {
92
89
  }
93
90
  return _findInputs((_root$props2 = root.props) === null || _root$props2 === void 0 ? void 0 : _root$props2.children, total);
94
91
  };
95
- const _findValidityKey = validity => {
92
+ const _findValidityKey = (validity, exclude = []) => {
96
93
  for (const key in validity) {
94
+ if (exclude.includes(key)) continue;
97
95
  if (key !== 'valid' && validity[key]) {
98
96
  return key;
99
97
  }
100
98
  }
101
99
  };
100
+ const _checkTypeMismatch = input => {
101
+ var _input$value;
102
+ const value = (_input$value = input.value) === null || _input$value === void 0 ? void 0 : _input$value.toString().trim();
103
+ if (!value) return false;
104
+ switch (input.type) {
105
+ case 'email':
106
+ return !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
107
+ case 'url':
108
+ try {
109
+ new URL(value);
110
+ return false;
111
+ } catch {
112
+ return true;
113
+ }
114
+ case 'tel':
115
+ return !/^\+?[0-9\s\-().]{7,}$/.test(value);
116
+ default:
117
+ return false;
118
+ }
119
+ };
102
120
  const hasSubmitter = form => {
103
121
  if (!form) return false;
104
122
  for (const element of form) {
@@ -106,14 +124,6 @@ const hasSubmitter = form => {
106
124
  }
107
125
  return false;
108
126
  };
109
- const _checkIfFormIsValid = fields => {
110
- for (const f in fields) {
111
- if (fields[f].error) {
112
- return false;
113
- }
114
- }
115
- return true;
116
- };
117
127
  const _toRawData = (fields, options = {}) => {
118
128
  const data = {};
119
129
  const {
@@ -216,7 +226,8 @@ let validityMap;
216
226
  pattern: 'withPatternMismatchMessage',
217
227
  min: 'withRangeUnderflowMessage',
218
228
  max: 'withRangeOverflowMessage',
219
- step: 'withStepMismatchMessage'
229
+ step: 'withStepMismatchMessage',
230
+ type: 'withTypeMismatchMessage'
220
231
  };
221
232
  validityMap = {
222
233
  tooShort: 'minLength',
@@ -225,7 +236,8 @@ let validityMap;
225
236
  patternMismatch: 'pattern',
226
237
  rangeOverflow: 'max',
227
238
  rangeUnderflow: 'min',
228
- stepMismatch: 'step'
239
+ stepMismatch: 'step',
240
+ typeMismatch: 'type'
229
241
  };
230
242
  }
231
243
  class GValidator {
@@ -256,6 +268,10 @@ class GValidator {
256
268
  }
257
269
  }
258
270
  }
271
+ hasConstraint(constraint) {
272
+ var _this$track;
273
+ return ((_this$track = this.track) === null || _this$track === void 0 ? void 0 : _this$track.includes(constraint)) || false;
274
+ }
259
275
  withRequiredMessage(message) {
260
276
  return this.__addConstraintValidationHandler('valueMissing', message);
261
277
  }
@@ -294,14 +310,14 @@ class GValidator {
294
310
  __addConstraintValidationHandler(validityKey, message) {
295
311
  if (this.track) {
296
312
  if (this.track.includes(validityKey)) {
297
- console.warn(`[Duplicate Handlers] - handler for '${validityKey}' has already been defined`);
313
+ console.warn(`DEV ONLY - [Duplicate Handlers] - handler for '${validityKey}' has already been defined`);
298
314
  }
299
315
  this.track.push(validityKey);
300
316
  }
301
317
  this._constraintHandlers.push((input, key) => {
302
318
  {
303
319
  if (validityKey && validityMap[validityKey] && typeof input[validityMap[validityKey]] === 'undefined') {
304
- 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`);
320
+ 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`);
305
321
  }
306
322
  }
307
323
  if (key === validityKey) {
@@ -314,45 +330,22 @@ class GValidator {
314
330
  }
315
331
  }
316
332
 
317
- const useForm = (children, validators = {}, optimized = false) => {
318
- const initialValues = React.useMemo(() => {
319
- const values = _buildFormInitialValues(typeof children === 'function' ? children({}) : children);
320
- {
321
- Object.keys(values.state.fields).forEach(key => {
322
- const input = values.state.fields[key];
323
- const validator = validators[key];
324
- if (validator instanceof GValidator) {
325
- var _validator$track;
326
- const validityKeys = (_validator$track = validator.track) === null || _validator$track === void 0 ? void 0 : _validator$track.filter(key => validityMap[key]);
327
- validityKeys === null || validityKeys === void 0 || validityKeys.forEach(vKey => {
328
- if (typeof input[validityMap[vKey]] === 'undefined') {
329
- 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`);
330
- }
331
- });
332
- Object.entries(validityMap).forEach(([validityKey, constraint]) => {
333
- var _validator$track2;
334
- if (typeof input[constraint] !== 'undefined' && !((_validator$track2 = validator.track) !== null && _validator$track2 !== void 0 && _validator$track2.some(trackKey => validityKey === trackKey))) {
335
- 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}`);
336
- }
337
- });
338
- }
339
- });
340
- }
341
- return values;
342
- }, []);
343
- const [state, setState] = React.useState(initialValues.state);
333
+ const useFormHandlers = (getState, setState, validators = {}, optimized = false) => {
344
334
  const _viHandler = (input, e) => {
345
335
  if (!input) return;
346
336
  const element = e && e.target;
337
+ const hasInitialValue = !input.dirty && input.value && !input.touched;
338
+ if (!element && !hasInitialValue) return;
347
339
  if (typeof document !== 'undefined' && (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) {
348
340
  if (!input.checkValidity) input.checkValidity = () => element.checkValidity();
349
- if (!input.dirty && input.value) {
341
+ if (hasInitialValue) {
350
342
  _checkInputManually(input);
351
343
  _dispatchChanges(input, input.formKey);
352
344
  return;
353
345
  }
354
346
  element.setCustomValidity('');
355
- const validityKey = _findValidityKey(element.validity);
347
+ const exclude = input.type && (input.pattern || hasCustomValidation(input)) ? ['typeMismatch'] : [];
348
+ const validityKey = _findValidityKey(element.validity, exclude);
356
349
  _validateInput(input, validityKey, v => element.setCustomValidity(v));
357
350
  if (!validityKey && input.error) {
358
351
  element.setCustomValidity(input.errorText || 'error');
@@ -365,36 +358,38 @@ const useForm = (children, validators = {}, optimized = false) => {
365
358
  }
366
359
  };
367
360
  const _checkInputManually = input => {
361
+ const exclude = input.type && (input.pattern || hasCustomValidation(input)) ? ['typeMismatch'] : [];
368
362
  let validityKey = _findValidityKey({
369
363
  valueMissing: input.required && !input.value || false,
364
+ typeMismatch: _checkTypeMismatch(input),
370
365
  tooShort: input.minLength && input.value.toString().length < input.minLength || false,
371
366
  tooLong: input.maxLength && input.value.toString().length > input.maxLength || false,
372
367
  patternMismatch: input.pattern && _checkResult(input.pattern, input.value) || false,
373
368
  rangeUnderflow: input.min && Number(input.value) < Number(input.min) || false,
374
369
  rangeOverflow: input.max && Number(input.value) > Number(input.max) || false
375
- });
370
+ }, exclude);
376
371
  if (!validityKey && input.error) {
377
372
  validityKey = 'customError';
378
373
  }
379
374
  _validateInput(input, validityKey);
380
375
  return !input.error;
381
376
  };
382
- const _updateInputHandler = (key, e, unknown) => {
383
- const value = _extractValue(e, unknown);
384
- const input = _updateInput(key, value);
377
+ const _updateInputHandler = (input, e, unknown) => {
378
+ input.value = _extractValue(e, unknown);
385
379
  _viHandler(input, e);
386
380
  };
387
381
  const _validateInput = (input, validityKey, setValidity) => {
388
382
  const inputValidator = validators[input.validatorKey || input.formKey] || validators['*'];
389
- inputValidator && __validateInput(input, inputValidator, validityKey, setValidity);
383
+ {
384
+ if (validityKey && !(inputValidator !== null && inputValidator !== void 0 && inputValidator.hasConstraint(validityKey))) {
385
+ 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`);
386
+ }
387
+ }
388
+ if (inputValidator) {
389
+ __validateInput(input, inputValidator, validityKey, setValidity);
390
+ }
390
391
  input.touched = true;
391
392
  };
392
- const _updateInput = (key, value) => {
393
- const input = state.fields[key];
394
- input.value = value;
395
- input.dirty = true;
396
- return input;
397
- };
398
393
  const _dispatchChanges = (changes, key) => setState(prev => {
399
394
  if (key) {
400
395
  return {
@@ -414,13 +409,14 @@ const useForm = (children, validators = {}, optimized = false) => {
414
409
  };
415
410
  });
416
411
  const __validateInput = (input, inputValidator, validityKey, setValidity) => {
412
+ const fields = getState().fields;
417
413
  for (const index in inputValidator.constraintHandlers) {
418
414
  const result = inputValidator.constraintHandlers[index](input, validityKey);
419
415
  input.error = _checkResult(result, input.value);
420
416
  if (input.error) return;
421
417
  }
422
418
  for (const index in inputValidator.handlers) {
423
- const result = inputValidator.handlers[index](input, state.fields);
419
+ const result = inputValidator.handlers[index](input, fields);
424
420
  input.error = _checkResult(result, input.value);
425
421
  if (input.error) return;
426
422
  }
@@ -430,7 +426,7 @@ const useForm = (children, validators = {}, optimized = false) => {
430
426
  _debounce(input.debounce || 300, `${input.gid}-async`).then(() => {
431
427
  const validateAsync = async () => {
432
428
  for (const index in inputValidator.asyncHandlers) {
433
- const result = await inputValidator.asyncHandlers[index](input, state.fields);
429
+ const result = await inputValidator.asyncHandlers[index](input, fields);
434
430
  input.error = _checkResult(result, input.value);
435
431
  if (input.error) break;
436
432
  }
@@ -439,184 +435,264 @@ const useForm = (children, validators = {}, optimized = false) => {
439
435
  error: input.error,
440
436
  errorText: input.errorText
441
437
  }, input.formKey);
442
- setValidity && setValidity(input.errorText);
438
+ if (setValidity) {
439
+ setValidity(input.errorText);
440
+ }
443
441
  };
444
442
  validateAsync();
445
443
  });
446
444
  }
447
445
  };
446
+ const hasCustomValidation = input => {
447
+ const validator = validators[input.validatorKey || input.formKey] || validators['*'];
448
+ return validator && (validator.asyncHandlers.length > 0 || validator.handlers.length > 0);
449
+ };
448
450
  return {
449
- state,
450
451
  _updateInputHandler,
451
452
  _viHandler,
452
453
  _dispatchChanges,
453
454
  optimized,
454
- key: initialValues.key,
455
455
  _createInputChecker: _checkInputManually
456
456
  };
457
457
  };
458
458
 
459
- const gFormContext = React.createContext({
460
- state: {
461
- fields: {},
462
- loading: false
463
- },
464
- _updateInputHandler: () => null,
465
- _viHandler: () => null,
466
- _dispatchChanges: () => null,
467
- _createInputChecker: () => false,
468
- optimized: false,
469
- key: ''
459
+ const GFormContext = React.createContext({});
460
+ const GFormContextProvider = ({
461
+ children,
462
+ initialState,
463
+ validators,
464
+ optimized
465
+ }) => {
466
+ const stateRef = React.useRef(initialState);
467
+ const listeners = React.useRef(new Set());
468
+ const setState = React.useCallback(updater => {
469
+ stateRef.current = typeof updater === 'function' ? updater(stateRef.current) : updater;
470
+ listeners.current.forEach(l => l());
471
+ }, []);
472
+ const handlers = useFormHandlers(() => stateRef.current, setState, validators, optimized);
473
+ const getState = React.useCallback(() => stateRef.current, []);
474
+ const subscribe = React.useCallback(listener => {
475
+ listeners.current.add(listener);
476
+ return () => listeners.current.delete(listener);
477
+ }, []);
478
+ React.useEffect(() => {
479
+ for (const fieldKey in initialState.fields) {
480
+ initialState.fields[fieldKey].dispatchChanges = changes => handlers._dispatchChanges(changes, fieldKey);
481
+ }
482
+ }, []);
483
+ const store = React.useRef({
484
+ getState,
485
+ setState,
486
+ subscribe,
487
+ handlers
488
+ });
489
+ return React.createElement(GFormContext.Provider, {
490
+ value: store.current
491
+ }, children);
492
+ };
493
+ const useFormStore = () => {
494
+ const store = React.useContext(GFormContext);
495
+ if (!store) throw new Error('useGFormStore must be used within `GForm` component');
496
+ return store;
497
+ };
498
+ const useFormSelector = selector => {
499
+ const store = useFormStore();
500
+ return React.useSyncExternalStore(store.subscribe, () => selector(store.getState()), () => selector(store.getState()));
501
+ };
502
+ function createSelector(selectors, combiner) {
503
+ let lastArgs = [];
504
+ let lastResult;
505
+ return state => {
506
+ const args = selectors.map(fn => fn(state));
507
+ if (lastArgs.length === args.length && args.every((val, i) => val === lastArgs[i])) {
508
+ return lastResult;
509
+ }
510
+ lastArgs = args;
511
+ lastResult = combiner(...args);
512
+ return lastResult;
513
+ };
514
+ }
515
+
516
+ const selectFields = [state => state.fields];
517
+ const selectFirstInvalidField = createSelector(selectFields, fields => {
518
+ for (const f in fields) {
519
+ if (fields[f].error) {
520
+ return true;
521
+ }
522
+ }
523
+ return false;
524
+ });
525
+ const makeSelectFields = (keys = []) => createSelector(selectFields, fields => {
526
+ const selected = keys.map(key => fields[key]).filter(Boolean);
527
+ return selected.length ? selected : null;
470
528
  });
471
- const useGenericFormContext = () => React.useContext(gFormContext);
472
- const GFormContextProvider = gFormContext.Provider;
473
529
 
474
- const GForm = (() => {
475
- return React.forwardRef(({
476
- loader = React.createElement("div", null, "loading"),
477
- stateRef,
478
- onSubmit,
479
- onChange,
480
- onPaste,
481
- children,
482
- validators,
483
- onInit,
484
- optimized,
485
- ...rest
486
- }, ref) => {
487
- const formRef = React.useRef(null);
488
- const values = useForm(children, validators, optimized);
489
- const {
490
- state,
491
- _updateInputHandler,
492
- _viHandler,
493
- _dispatchChanges,
494
- key
495
- } = values;
496
- const refHandler = element => {
497
- if (ref) {
498
- if (typeof ref === 'function') {
499
- ref(element);
500
- } else {
501
- ref.current = element;
502
- }
530
+ const FormRenderer = React.forwardRef(({
531
+ stateRef,
532
+ onSubmit,
533
+ onChange,
534
+ onPaste,
535
+ onKeyDown,
536
+ onKeyUp,
537
+ children,
538
+ onInit,
539
+ ...rest
540
+ }, ref) => {
541
+ const formRef = React.useRef(null);
542
+ const {
543
+ getState,
544
+ handlers
545
+ } = useFormStore();
546
+ const isFormInvalid = useFormSelector(selectFirstInvalidField);
547
+ const refHandler = React.useCallback(element => {
548
+ if (ref) {
549
+ if (typeof ref === 'function') {
550
+ ref(element);
551
+ } else {
552
+ ref.current = element;
553
+ }
554
+ }
555
+ formRef.current = element;
556
+ }, [ref]);
557
+ const getFormState = React.useCallback(() => {
558
+ const fields = getState().fields;
559
+ const formState = {
560
+ ...fields,
561
+ isValid: !isFormInvalid,
562
+ isInvalid: isFormInvalid,
563
+ toRawData: options => _toRawData(fields, options),
564
+ toFormData: () => _toFormData(formRef.current),
565
+ toURLSearchParams: _toURLSearchParams,
566
+ checkValidity: function () {
567
+ this.isValid = formRef.current && formRef.current.checkValidity() || false;
568
+ this.isInvalid = !this.isValid;
569
+ return this.isValid;
570
+ },
571
+ dispatchChanges: changes => handlers._dispatchChanges({
572
+ fields: _merge({}, fields, changes)
573
+ })
574
+ };
575
+ if (stateRef) stateRef.current = formState;
576
+ return formState;
577
+ }, [isFormInvalid]);
578
+ const formComponent = React.useMemo(() => {
579
+ const state = getFormState();
580
+ const formChildren = typeof children === 'function' ? children(state) : children;
581
+ const _onSubmit = e => {
582
+ const state = getFormState();
583
+ if (state.isValid && onSubmit) {
584
+ onSubmit(state, e);
503
585
  }
504
- formRef.current = element;
505
586
  };
506
- const formState = React.useMemo(() => {
507
- const _isFormValid = _checkIfFormIsValid(state.fields);
508
- const formState = {
509
- ...state.fields,
510
- isValid: _isFormValid,
511
- isInvalid: !_isFormValid,
512
- loading: state.loading,
513
- toRawData: options => _toRawData(state.fields, options),
514
- toFormData: () => _toFormData(formRef.current),
515
- toURLSearchParams: _toURLSearchParams,
516
- checkValidity: function () {
517
- this.isValid = formRef.current && formRef.current.checkValidity() || false;
518
- this.isInvalid = !this.isValid;
519
- return this.isValid;
520
- },
521
- setLoading: p => _dispatchChanges({
522
- loading: typeof p === 'function' ? p(state.loading) : p
523
- }),
524
- dispatchChanges: changes => _dispatchChanges({
525
- fields: _merge({}, state.fields, changes)
526
- })
527
- };
528
- if (stateRef) stateRef.current = formState;
529
- return formState;
530
- }, [state.fields]);
531
- const formComponent = React.useMemo(() => {
532
- const formChildren = typeof children === 'function' ? children(formState) : children;
533
- const _onSubmit = e => {
534
- if (formState.isValid && onSubmit) {
535
- onSubmit(formState, e);
536
- }
537
- };
538
- let _onPaste;
539
- if (onPaste) {
540
- _onPaste = e => onPaste(formState, e);
587
+ let _onPaste, _onChange, _onKeyDown, _onKeyUp;
588
+ if (onPaste) {
589
+ _onPaste = e => onPaste(state, e);
590
+ }
591
+ if (onKeyDown) {
592
+ _onKeyDown = e => onKeyDown(state, e);
593
+ }
594
+ if (onKeyUp) {
595
+ _onKeyUp = e => onKeyUp(state, e);
596
+ }
597
+ if (handlers.optimized) {
598
+ if (onChange) {
599
+ _onChange = (e, unknown) => {
600
+ handlers._updateInputHandler(state[e.target.name], e, unknown);
601
+ onChange(state, e);
602
+ };
603
+ } else {
604
+ _onChange = (e, unknown) => {
605
+ handlers._updateInputHandler(state[e.target.name], e, unknown);
606
+ };
541
607
  }
542
- return optimized ? React.createElement("form", _extends({}, rest, {
608
+ return React.createElement("form", _extends({}, rest, {
543
609
  ref: refHandler,
544
610
  onPaste: _onPaste,
545
- onBlur: e => {
546
- _viHandler(state.fields[e.target.name], e);
547
- },
611
+ onKeyDown: _onKeyDown,
612
+ onKeyUp: _onKeyUp,
613
+ onBlur: e => handlers._viHandler(state[e.target.name], e),
548
614
  onInvalid: e => {
549
615
  e.preventDefault();
550
- _viHandler(state.fields[e.target.name], e);
551
- },
552
- onChange: (e, unknown) => {
553
- _updateInputHandler(e.target.name, e, unknown);
554
- onChange && onChange(formState, e);
616
+ handlers._viHandler(state[e.target.name], e);
555
617
  },
618
+ onChange: _onChange,
556
619
  onSubmit: _onSubmit
557
- }), formChildren) : React.createElement("form", _extends({}, rest, {
558
- onChange: e => onChange && onChange(formState, e),
559
- ref: refHandler,
560
- onSubmit: _onSubmit,
561
- onPaste: _onPaste
562
620
  }), formChildren);
563
- }, [formState, children]);
564
- React.useEffect(() => {
565
- if (!hasSubmitter(formRef.current)) {
566
- 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`);
567
- }
568
- if (onInit) {
569
- const _handler = _c => _dispatchChanges({
570
- fields: _merge({}, state.fields, _c)
621
+ }
622
+ if (onChange) {
623
+ _onChange = e => onChange(state, e);
624
+ }
625
+ return React.createElement("form", _extends({}, rest, {
626
+ ref: refHandler,
627
+ onSubmit: _onSubmit,
628
+ onChange: _onChange,
629
+ onPaste: _onPaste,
630
+ onKeyDown: _onKeyDown,
631
+ onKeyUp: _onKeyUp
632
+ }), formChildren);
633
+ }, [children, getFormState]);
634
+ React.useEffect(() => {
635
+ const state = getFormState();
636
+ if (!hasSubmitter(formRef.current)) {
637
+ 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`);
638
+ }
639
+ if (onInit) {
640
+ const changes = onInit(state);
641
+ if (changes) {
642
+ const _handler = _c => handlers._dispatchChanges({
643
+ fields: _merge({}, state, _c)
571
644
  });
572
- const changes = onInit(formState);
573
- changes instanceof Promise ? changes.then(_handler) : _handler(changes);
645
+ if (changes instanceof Promise) {
646
+ changes.then(_handler);
647
+ } else _handler(changes);
574
648
  }
575
- const dipatchers = {};
576
- Object.values(state.fields).forEach(field => {
577
- dipatchers[field.formKey] = {
578
- dispatchChanges: changes => _dispatchChanges(changes, field.formKey)
579
- };
580
- if (!field.value) return;
581
- _viHandler(field);
582
- });
583
- _dispatchChanges({
584
- fields: _merge(dipatchers, state.fields)
585
- });
586
- }, []);
587
- return React.createElement(GFormContextProvider, {
588
- value: values,
589
- key: key
590
- }, state.loading ? loader : formComponent);
591
- });
592
- })();
649
+ }
650
+ const fields = getState().fields;
651
+ for (const fieldKey in fields) {
652
+ const field = fields[fieldKey];
653
+ if (!field.value) continue;
654
+ handlers._viHandler(field);
655
+ }
656
+ }, [getFormState]);
657
+ return formComponent;
658
+ });
659
+ const GForm = React.forwardRef(({
660
+ children,
661
+ validators,
662
+ optimized,
663
+ ...props
664
+ }, ref) => {
665
+ const initialState = React.useMemo(() => {
666
+ return _buildFormInitialValues(typeof children === 'function' ? children({}) : children);
667
+ }, [children]);
668
+ return React.createElement(GFormContextProvider, {
669
+ key: initialState.key,
670
+ initialState: initialState,
671
+ validators: validators,
672
+ optimized: optimized
673
+ }, React.createElement(FormRenderer, _extends({
674
+ ref: ref
675
+ }, props), children));
676
+ });
593
677
 
594
- const GInput = React.forwardRef(({
678
+ const _GInput = React.forwardRef(({
595
679
  formKey,
596
680
  element,
597
681
  title,
598
- type,
599
- validatorKey,
682
+ type = 'text',
600
683
  fetch,
601
- fetchDeps = [],
684
+ fetchDeps,
602
685
  optimized,
686
+ debounce = 300,
603
687
  defaultChecked,
604
688
  defaultValue,
605
689
  checked,
690
+ validatorKey,
606
691
  value,
607
- debounce = 300,
608
692
  ...rest
609
693
  }, ref) => {
610
- const {
611
- state: {
612
- fields
613
- },
614
- _updateInputHandler,
615
- _dispatchChanges,
616
- optimized: formOptimized,
617
- _viHandler
618
- } = useGenericFormContext();
619
- const inputState = fields[formKey];
694
+ const inputState = useFormSelector(state => state.fields[formKey]);
695
+ const store = useFormStore();
620
696
  const _element = React.useMemo(() => {
621
697
  let value, checked;
622
698
  if (type === 'checkbox') checked = inputState.value || false;else value = inputState.value || '';
@@ -631,40 +707,65 @@ const GInput = React.forwardRef(({
631
707
  'aria-required': inputState.required,
632
708
  title: title || inputState.errorText
633
709
  };
634
- if (!formOptimized || !optimized) {
635
- _props.onBlur = e => {
636
- _viHandler(inputState, e);
637
- rest.onBlur && rest.onBlur(e);
710
+ if (!store.handlers.optimized || !optimized) {
711
+ _props.onBlur = rest.onBlur ? e => {
712
+ store.handlers._viHandler(inputState, e);
713
+ rest.onBlur(e);
714
+ } : e => {
715
+ store.handlers._viHandler(inputState, e);
638
716
  };
639
- _props.onInvalid = e => {
717
+ _props.onInvalid = rest.onInvalid ? e => {
640
718
  e.preventDefault();
641
- _viHandler(inputState, e);
642
- rest.onInvalid && rest.onInvalid(e);
719
+ store.handlers._viHandler(inputState, e);
720
+ rest.onInvalid(e);
721
+ } : e => {
722
+ e.preventDefault();
723
+ store.handlers._viHandler(inputState, e);
643
724
  };
644
- _props.onChange = (e, unknown) => {
645
- _updateInputHandler(formKey, e, unknown);
646
- rest.onChange && rest.onChange(e);
725
+ _props.onChange = rest.onChange ? (e, unknown) => {
726
+ store.handlers._updateInputHandler(inputState, e, unknown);
727
+ rest.onChange(e);
728
+ } : (e, unknown) => {
729
+ store.handlers._updateInputHandler(inputState, e, unknown);
647
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
+ }
648
743
  }
649
744
  if (element) {
650
745
  return element(inputState, _props);
651
746
  }
652
747
  return React.createElement("input", _props);
653
748
  }, [inputState, element]);
654
- const _fetchDeps = React.useMemo(() => fetchDeps.map(key => fields[key].value), [fields]);
749
+ const _fetchDeps = useFormSelector(makeSelectFields(fetchDeps));
750
+ const stableFetchDeps = React.useMemo(() => JSON.stringify(_fetchDeps), [_fetchDeps]);
655
751
  React.useEffect(() => {
656
752
  if (fetch) {
657
- inputState.dispatchChanges = changes => _dispatchChanges(changes, formKey);
658
753
  _debounce(debounce, `${inputState.gid}-fetch`).then(() => {
659
- const res = fetch(inputState, fields);
660
- res instanceof Promise ? res.then(state => state && _dispatchChanges(state, formKey)) : res && _dispatchChanges(res, formKey);
754
+ const res = fetch(inputState, store.getState().fields);
755
+ if (res instanceof Promise) {
756
+ res.then(state => state && store.handlers._dispatchChanges(state, formKey));
757
+ } else if (res) {
758
+ store.handlers._dispatchChanges(res, formKey);
759
+ }
661
760
  });
662
761
  }
663
- }, _fetchDeps);
762
+ }, [stableFetchDeps]);
664
763
  return _element;
665
764
  });
765
+ const GInput = React.memo(_GInput);
666
766
 
667
767
  exports.GForm = GForm;
668
768
  exports.GInput = GInput;
669
769
  exports.GValidator = GValidator;
770
+ exports.useFormSelector = useFormSelector;
670
771
  //# sourceMappingURL=gform-react.development.js.map