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
@@ -1,7 +1,7 @@
1
1
  import _extends from '@babel/runtime/helpers/esm/extends';
2
2
  import _objectSpread from '@babel/runtime/helpers/esm/objectSpread2';
3
3
  import _objectWithoutProperties from '@babel/runtime/helpers/esm/objectWithoutProperties';
4
- import React, { useMemo, useState, createContext, useContext, forwardRef, useRef, useEffect } from 'react';
4
+ import React, { useSyncExternalStore, useRef, useCallback, useEffect, createContext, useContext, forwardRef, useMemo, memo } from 'react';
5
5
  import _defineProperty from '@babel/runtime/helpers/esm/defineProperty';
6
6
 
7
7
  const isObject = o => o && typeof o === 'object' && !Array.isArray(o);
@@ -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 (_unused) {
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 {
@@ -213,7 +223,8 @@ let validityMap;
213
223
  pattern: 'withPatternMismatchMessage',
214
224
  min: 'withRangeUnderflowMessage',
215
225
  max: 'withRangeOverflowMessage',
216
- step: 'withStepMismatchMessage'
226
+ step: 'withStepMismatchMessage',
227
+ type: 'withTypeMismatchMessage'
217
228
  };
218
229
  validityMap = {
219
230
  tooShort: 'minLength',
@@ -222,7 +233,8 @@ let validityMap;
222
233
  patternMismatch: 'pattern',
223
234
  rangeOverflow: 'max',
224
235
  rangeUnderflow: 'min',
225
- stepMismatch: 'step'
236
+ stepMismatch: 'step',
237
+ typeMismatch: 'type'
226
238
  };
227
239
  }
228
240
  class GValidator {
@@ -253,6 +265,10 @@ class GValidator {
253
265
  }
254
266
  }
255
267
  }
268
+ hasConstraint(constraint) {
269
+ var _this$track;
270
+ return ((_this$track = this.track) === null || _this$track === void 0 ? void 0 : _this$track.includes(constraint)) || false;
271
+ }
256
272
  withRequiredMessage(message) {
257
273
  return this.__addConstraintValidationHandler('valueMissing', message);
258
274
  }
@@ -291,14 +307,14 @@ class GValidator {
291
307
  __addConstraintValidationHandler(validityKey, message) {
292
308
  if (this.track) {
293
309
  if (this.track.includes(validityKey)) {
294
- console.warn(`[Duplicate Handlers] - handler for '${validityKey}' has already been defined`);
310
+ console.warn(`DEV ONLY - [Duplicate Handlers] - handler for '${validityKey}' has already been defined`);
295
311
  }
296
312
  this.track.push(validityKey);
297
313
  }
298
314
  this._constraintHandlers.push((input, key) => {
299
315
  {
300
316
  if (validityKey && validityMap[validityKey] && typeof input[validityMap[validityKey]] === 'undefined') {
301
- 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`);
317
+ 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`);
302
318
  }
303
319
  }
304
320
  if (key === validityKey) {
@@ -311,45 +327,22 @@ class GValidator {
311
327
  }
312
328
  }
313
329
 
314
- const useForm = (children, validators = {}, optimized = false) => {
315
- const initialValues = useMemo(() => {
316
- const values = _buildFormInitialValues(typeof children === 'function' ? children({}) : children);
317
- {
318
- Object.keys(values.state.fields).forEach(key => {
319
- const input = values.state.fields[key];
320
- const validator = validators[key];
321
- if (validator instanceof GValidator) {
322
- var _validator$track;
323
- const validityKeys = (_validator$track = validator.track) === null || _validator$track === void 0 ? void 0 : _validator$track.filter(key => validityMap[key]);
324
- validityKeys === null || validityKeys === void 0 || validityKeys.forEach(vKey => {
325
- if (typeof input[validityMap[vKey]] === 'undefined') {
326
- 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`);
327
- }
328
- });
329
- Object.entries(validityMap).forEach(([validityKey, constraint]) => {
330
- var _validator$track2;
331
- if (typeof input[constraint] !== 'undefined' && !((_validator$track2 = validator.track) !== null && _validator$track2 !== void 0 && _validator$track2.some(trackKey => validityKey === trackKey))) {
332
- 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}`);
333
- }
334
- });
335
- }
336
- });
337
- }
338
- return values;
339
- }, []);
340
- const [state, setState] = useState(initialValues.state);
330
+ const useFormHandlers = (getState, setState, validators = {}, optimized = false) => {
341
331
  const _viHandler = (input, e) => {
342
332
  if (!input) return;
343
333
  const element = e && e.target;
334
+ const hasInitialValue = !input.dirty && input.value && !input.touched;
335
+ if (!element && !hasInitialValue) return;
344
336
  if (typeof document !== 'undefined' && (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) {
345
337
  if (!input.checkValidity) input.checkValidity = () => element.checkValidity();
346
- if (!input.dirty && input.value) {
338
+ if (hasInitialValue) {
347
339
  _checkInputManually(input);
348
340
  _dispatchChanges(input, input.formKey);
349
341
  return;
350
342
  }
351
343
  element.setCustomValidity('');
352
- const validityKey = _findValidityKey(element.validity);
344
+ const exclude = input.type && (input.pattern || hasCustomValidation(input)) ? ['typeMismatch'] : [];
345
+ const validityKey = _findValidityKey(element.validity, exclude);
353
346
  _validateInput(input, validityKey, v => element.setCustomValidity(v));
354
347
  if (!validityKey && input.error) {
355
348
  element.setCustomValidity(input.errorText || 'error');
@@ -362,36 +355,38 @@ const useForm = (children, validators = {}, optimized = false) => {
362
355
  }
363
356
  };
364
357
  const _checkInputManually = input => {
358
+ const exclude = input.type && (input.pattern || hasCustomValidation(input)) ? ['typeMismatch'] : [];
365
359
  let validityKey = _findValidityKey({
366
360
  valueMissing: input.required && !input.value || false,
361
+ typeMismatch: _checkTypeMismatch(input),
367
362
  tooShort: input.minLength && input.value.toString().length < input.minLength || false,
368
363
  tooLong: input.maxLength && input.value.toString().length > input.maxLength || false,
369
364
  patternMismatch: input.pattern && _checkResult(input.pattern, input.value) || false,
370
365
  rangeUnderflow: input.min && Number(input.value) < Number(input.min) || false,
371
366
  rangeOverflow: input.max && Number(input.value) > Number(input.max) || false
372
- });
367
+ }, exclude);
373
368
  if (!validityKey && input.error) {
374
369
  validityKey = 'customError';
375
370
  }
376
371
  _validateInput(input, validityKey);
377
372
  return !input.error;
378
373
  };
379
- const _updateInputHandler = (key, e, unknown) => {
380
- const value = _extractValue(e, unknown);
381
- const input = _updateInput(key, value);
374
+ const _updateInputHandler = (input, e, unknown) => {
375
+ input.value = _extractValue(e, unknown);
382
376
  _viHandler(input, e);
383
377
  };
384
378
  const _validateInput = (input, validityKey, setValidity) => {
385
379
  const inputValidator = validators[input.validatorKey || input.formKey] || validators['*'];
386
- inputValidator && __validateInput(input, inputValidator, validityKey, setValidity);
380
+ {
381
+ if (validityKey && !(inputValidator !== null && inputValidator !== void 0 && inputValidator.hasConstraint(validityKey))) {
382
+ 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`);
383
+ }
384
+ }
385
+ if (inputValidator) {
386
+ __validateInput(input, inputValidator, validityKey, setValidity);
387
+ }
387
388
  input.touched = true;
388
389
  };
389
- const _updateInput = (key, value) => {
390
- const input = state.fields[key];
391
- input.value = value;
392
- input.dirty = true;
393
- return input;
394
- };
395
390
  const _dispatchChanges = (changes, key) => setState(prev => {
396
391
  if (key) {
397
392
  return _objectSpread(_objectSpread({}, prev), {}, {
@@ -403,13 +398,14 @@ const useForm = (children, validators = {}, optimized = false) => {
403
398
  return _objectSpread(_objectSpread({}, prev), changes);
404
399
  });
405
400
  const __validateInput = (input, inputValidator, validityKey, setValidity) => {
401
+ const fields = getState().fields;
406
402
  for (const index in inputValidator.constraintHandlers) {
407
403
  const result = inputValidator.constraintHandlers[index](input, validityKey);
408
404
  input.error = _checkResult(result, input.value);
409
405
  if (input.error) return;
410
406
  }
411
407
  for (const index in inputValidator.handlers) {
412
- const result = inputValidator.handlers[index](input, state.fields);
408
+ const result = inputValidator.handlers[index](input, fields);
413
409
  input.error = _checkResult(result, input.value);
414
410
  if (input.error) return;
415
411
  }
@@ -419,7 +415,7 @@ const useForm = (children, validators = {}, optimized = false) => {
419
415
  _debounce(input.debounce || 300, `${input.gid}-async`).then(() => {
420
416
  const validateAsync = async () => {
421
417
  for (const index in inputValidator.asyncHandlers) {
422
- const result = await inputValidator.asyncHandlers[index](input, state.fields);
418
+ const result = await inputValidator.asyncHandlers[index](input, fields);
423
419
  input.error = _checkResult(result, input.value);
424
420
  if (input.error) break;
425
421
  }
@@ -428,187 +424,269 @@ const useForm = (children, validators = {}, optimized = false) => {
428
424
  error: input.error,
429
425
  errorText: input.errorText
430
426
  }, input.formKey);
431
- setValidity && setValidity(input.errorText);
427
+ if (setValidity) {
428
+ setValidity(input.errorText);
429
+ }
432
430
  };
433
431
  validateAsync();
434
432
  });
435
433
  }
436
434
  };
435
+ const hasCustomValidation = input => {
436
+ const validator = validators[input.validatorKey || input.formKey] || validators['*'];
437
+ return validator && (validator.asyncHandlers.length > 0 || validator.handlers.length > 0);
438
+ };
437
439
  return {
438
- state,
439
440
  _updateInputHandler,
440
441
  _viHandler,
441
442
  _dispatchChanges,
442
443
  optimized,
443
- key: initialValues.key,
444
444
  _createInputChecker: _checkInputManually
445
445
  };
446
446
  };
447
447
 
448
- const gFormContext = createContext({
449
- state: {
450
- fields: {},
451
- loading: false
452
- },
453
- _updateInputHandler: () => null,
454
- _viHandler: () => null,
455
- _dispatchChanges: () => null,
456
- _createInputChecker: () => false,
457
- optimized: false,
458
- key: ''
448
+ const GFormContext = createContext({});
449
+ const GFormContextProvider = ({
450
+ children,
451
+ initialState,
452
+ validators,
453
+ optimized
454
+ }) => {
455
+ const stateRef = useRef(initialState);
456
+ const listeners = useRef(new Set());
457
+ const setState = useCallback(updater => {
458
+ stateRef.current = typeof updater === 'function' ? updater(stateRef.current) : updater;
459
+ listeners.current.forEach(l => l());
460
+ }, []);
461
+ const handlers = useFormHandlers(() => stateRef.current, setState, validators, optimized);
462
+ const getState = useCallback(() => stateRef.current, []);
463
+ const subscribe = useCallback(listener => {
464
+ listeners.current.add(listener);
465
+ return () => listeners.current.delete(listener);
466
+ }, []);
467
+ useEffect(() => {
468
+ for (const fieldKey in initialState.fields) {
469
+ initialState.fields[fieldKey].dispatchChanges = changes => handlers._dispatchChanges(changes, fieldKey);
470
+ }
471
+ }, []);
472
+ const store = useRef({
473
+ getState,
474
+ setState,
475
+ subscribe,
476
+ handlers
477
+ });
478
+ return React.createElement(GFormContext.Provider, {
479
+ value: store.current
480
+ }, children);
481
+ };
482
+ const useFormStore = () => {
483
+ const store = useContext(GFormContext);
484
+ if (!store) throw new Error('useGFormStore must be used within `GForm` component');
485
+ return store;
486
+ };
487
+ const useFormSelector = selector => {
488
+ const store = useFormStore();
489
+ return useSyncExternalStore(store.subscribe, () => selector(store.getState()), () => selector(store.getState()));
490
+ };
491
+ function createSelector(selectors, combiner) {
492
+ let lastArgs = [];
493
+ let lastResult;
494
+ return state => {
495
+ const args = selectors.map(fn => fn(state));
496
+ if (lastArgs.length === args.length && args.every((val, i) => val === lastArgs[i])) {
497
+ return lastResult;
498
+ }
499
+ lastArgs = args;
500
+ lastResult = combiner(...args);
501
+ return lastResult;
502
+ };
503
+ }
504
+
505
+ const selectFields = [state => state.fields];
506
+ const selectFirstInvalidField = createSelector(selectFields, fields => {
507
+ for (const f in fields) {
508
+ if (fields[f].error) {
509
+ return true;
510
+ }
511
+ }
512
+ return false;
513
+ });
514
+ const makeSelectFields = (keys = []) => createSelector(selectFields, fields => {
515
+ const selected = keys.map(key => fields[key]).filter(Boolean);
516
+ return selected.length ? selected : null;
459
517
  });
460
- const useGenericFormContext = () => useContext(gFormContext);
461
- const GFormContextProvider = gFormContext.Provider;
462
518
 
463
- const _excluded$1 = ["loader", "stateRef", "onSubmit", "onChange", "onPaste", "children", "validators", "onInit", "optimized"];
464
- const GForm = (() => {
465
- return forwardRef((_ref, ref) => {
466
- let {
467
- loader = React.createElement("div", null, "loading"),
468
- stateRef,
469
- onSubmit,
470
- onChange,
471
- onPaste,
472
- children,
473
- validators,
474
- onInit,
475
- optimized
476
- } = _ref,
477
- rest = _objectWithoutProperties(_ref, _excluded$1);
478
- const formRef = useRef(null);
479
- const values = useForm(children, validators, optimized);
480
- const {
481
- state,
482
- _updateInputHandler,
483
- _viHandler,
484
- _dispatchChanges,
485
- key
486
- } = values;
487
- const refHandler = element => {
488
- if (ref) {
489
- if (typeof ref === 'function') {
490
- ref(element);
491
- } else {
492
- ref.current = element;
493
- }
519
+ const _excluded$1 = ["stateRef", "onSubmit", "onChange", "onPaste", "onKeyDown", "onKeyUp", "children", "onInit"],
520
+ _excluded2 = ["children", "validators", "optimized"];
521
+ const FormRenderer = forwardRef((_ref, ref) => {
522
+ let {
523
+ stateRef,
524
+ onSubmit,
525
+ onChange,
526
+ onPaste,
527
+ onKeyDown,
528
+ onKeyUp,
529
+ children,
530
+ onInit
531
+ } = _ref,
532
+ rest = _objectWithoutProperties(_ref, _excluded$1);
533
+ const formRef = useRef(null);
534
+ const {
535
+ getState,
536
+ handlers
537
+ } = useFormStore();
538
+ const isFormInvalid = useFormSelector(selectFirstInvalidField);
539
+ const refHandler = useCallback(element => {
540
+ if (ref) {
541
+ if (typeof ref === 'function') {
542
+ ref(element);
543
+ } else {
544
+ ref.current = element;
545
+ }
546
+ }
547
+ formRef.current = element;
548
+ }, [ref]);
549
+ const getFormState = useCallback(() => {
550
+ const fields = getState().fields;
551
+ const formState = _objectSpread(_objectSpread({}, fields), {}, {
552
+ isValid: !isFormInvalid,
553
+ isInvalid: isFormInvalid,
554
+ toRawData: options => _toRawData(fields, options),
555
+ toFormData: () => _toFormData(formRef.current),
556
+ toURLSearchParams: _toURLSearchParams,
557
+ checkValidity: function () {
558
+ this.isValid = formRef.current && formRef.current.checkValidity() || false;
559
+ this.isInvalid = !this.isValid;
560
+ return this.isValid;
561
+ },
562
+ dispatchChanges: changes => handlers._dispatchChanges({
563
+ fields: _merge({}, fields, changes)
564
+ })
565
+ });
566
+ if (stateRef) stateRef.current = formState;
567
+ return formState;
568
+ }, [isFormInvalid]);
569
+ const formComponent = useMemo(() => {
570
+ const state = getFormState();
571
+ const formChildren = typeof children === 'function' ? children(state) : children;
572
+ const _onSubmit = e => {
573
+ const state = getFormState();
574
+ if (state.isValid && onSubmit) {
575
+ onSubmit(state, e);
494
576
  }
495
- formRef.current = element;
496
577
  };
497
- const formState = useMemo(() => {
498
- const _isFormValid = _checkIfFormIsValid(state.fields);
499
- const formState = _objectSpread(_objectSpread({}, state.fields), {}, {
500
- isValid: _isFormValid,
501
- isInvalid: !_isFormValid,
502
- loading: state.loading,
503
- toRawData: options => _toRawData(state.fields, options),
504
- toFormData: () => _toFormData(formRef.current),
505
- toURLSearchParams: _toURLSearchParams,
506
- checkValidity: function () {
507
- this.isValid = formRef.current && formRef.current.checkValidity() || false;
508
- this.isInvalid = !this.isValid;
509
- return this.isValid;
510
- },
511
- setLoading: p => _dispatchChanges({
512
- loading: typeof p === 'function' ? p(state.loading) : p
513
- }),
514
- dispatchChanges: changes => _dispatchChanges({
515
- fields: _merge({}, state.fields, changes)
516
- })
517
- });
518
- if (stateRef) stateRef.current = formState;
519
- return formState;
520
- }, [state.fields]);
521
- const formComponent = useMemo(() => {
522
- const formChildren = typeof children === 'function' ? children(formState) : children;
523
- const _onSubmit = e => {
524
- if (formState.isValid && onSubmit) {
525
- onSubmit(formState, e);
526
- }
527
- };
528
- let _onPaste;
529
- if (onPaste) {
530
- _onPaste = e => onPaste(formState, e);
578
+ let _onPaste, _onChange, _onKeyDown, _onKeyUp;
579
+ if (onPaste) {
580
+ _onPaste = e => onPaste(state, e);
581
+ }
582
+ if (onKeyDown) {
583
+ _onKeyDown = e => onKeyDown(state, e);
584
+ }
585
+ if (onKeyUp) {
586
+ _onKeyUp = e => onKeyUp(state, e);
587
+ }
588
+ if (handlers.optimized) {
589
+ if (onChange) {
590
+ _onChange = (e, unknown) => {
591
+ handlers._updateInputHandler(state[e.target.name], e, unknown);
592
+ onChange(state, e);
593
+ };
594
+ } else {
595
+ _onChange = (e, unknown) => {
596
+ handlers._updateInputHandler(state[e.target.name], e, unknown);
597
+ };
531
598
  }
532
- return optimized ? React.createElement("form", _extends({}, rest, {
599
+ return React.createElement("form", _extends({}, rest, {
533
600
  ref: refHandler,
534
601
  onPaste: _onPaste,
535
- onBlur: e => {
536
- _viHandler(state.fields[e.target.name], e);
537
- },
602
+ onKeyDown: _onKeyDown,
603
+ onKeyUp: _onKeyUp,
604
+ onBlur: e => handlers._viHandler(state[e.target.name], e),
538
605
  onInvalid: e => {
539
606
  e.preventDefault();
540
- _viHandler(state.fields[e.target.name], e);
541
- },
542
- onChange: (e, unknown) => {
543
- _updateInputHandler(e.target.name, e, unknown);
544
- onChange && onChange(formState, e);
607
+ handlers._viHandler(state[e.target.name], e);
545
608
  },
609
+ onChange: _onChange,
546
610
  onSubmit: _onSubmit
547
- }), formChildren) : React.createElement("form", _extends({}, rest, {
548
- onChange: e => onChange && onChange(formState, e),
549
- ref: refHandler,
550
- onSubmit: _onSubmit,
551
- onPaste: _onPaste
552
611
  }), formChildren);
553
- }, [formState, children]);
554
- useEffect(() => {
555
- if (!hasSubmitter(formRef.current)) {
556
- 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`);
557
- }
558
- if (onInit) {
559
- const _handler = _c => _dispatchChanges({
560
- fields: _merge({}, state.fields, _c)
612
+ }
613
+ if (onChange) {
614
+ _onChange = e => onChange(state, e);
615
+ }
616
+ return React.createElement("form", _extends({}, rest, {
617
+ ref: refHandler,
618
+ onSubmit: _onSubmit,
619
+ onChange: _onChange,
620
+ onPaste: _onPaste,
621
+ onKeyDown: _onKeyDown,
622
+ onKeyUp: _onKeyUp
623
+ }), formChildren);
624
+ }, [children, getFormState]);
625
+ useEffect(() => {
626
+ const state = getFormState();
627
+ if (!hasSubmitter(formRef.current)) {
628
+ 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`);
629
+ }
630
+ if (onInit) {
631
+ const changes = onInit(state);
632
+ if (changes) {
633
+ const _handler = _c => handlers._dispatchChanges({
634
+ fields: _merge({}, state, _c)
561
635
  });
562
- const changes = onInit(formState);
563
- changes instanceof Promise ? changes.then(_handler) : _handler(changes);
636
+ if (changes instanceof Promise) {
637
+ changes.then(_handler);
638
+ } else _handler(changes);
564
639
  }
565
- const dipatchers = {};
566
- Object.values(state.fields).forEach(field => {
567
- dipatchers[field.formKey] = {
568
- dispatchChanges: changes => _dispatchChanges(changes, field.formKey)
569
- };
570
- if (!field.value) return;
571
- _viHandler(field);
572
- });
573
- _dispatchChanges({
574
- fields: _merge(dipatchers, state.fields)
575
- });
576
- }, []);
577
- return React.createElement(GFormContextProvider, {
578
- value: values,
579
- key: key
580
- }, state.loading ? loader : formComponent);
581
- });
582
- })();
640
+ }
641
+ const fields = getState().fields;
642
+ for (const fieldKey in fields) {
643
+ const field = fields[fieldKey];
644
+ if (!field.value) continue;
645
+ handlers._viHandler(field);
646
+ }
647
+ }, [getFormState]);
648
+ return formComponent;
649
+ });
650
+ const GForm = forwardRef((_ref2, ref) => {
651
+ let {
652
+ children,
653
+ validators,
654
+ optimized
655
+ } = _ref2,
656
+ props = _objectWithoutProperties(_ref2, _excluded2);
657
+ const initialState = useMemo(() => {
658
+ return _buildFormInitialValues(typeof children === 'function' ? children({}) : children);
659
+ }, [children]);
660
+ return React.createElement(GFormContextProvider, {
661
+ key: initialState.key,
662
+ initialState: initialState,
663
+ validators: validators,
664
+ optimized: optimized
665
+ }, React.createElement(FormRenderer, _extends({
666
+ ref: ref
667
+ }, props), children));
668
+ });
583
669
 
584
- const _excluded = ["formKey", "element", "title", "type", "validatorKey", "fetch", "fetchDeps", "optimized", "defaultChecked", "defaultValue", "checked", "value", "debounce"];
585
- const GInput = forwardRef((_ref, ref) => {
670
+ const _excluded = ["formKey", "element", "title", "type", "fetch", "fetchDeps", "optimized", "debounce", "defaultChecked", "defaultValue", "checked", "validatorKey", "value"];
671
+ const _GInput = forwardRef((_ref, ref) => {
586
672
  let {
587
673
  formKey,
588
674
  element,
589
675
  title,
590
- type,
591
- validatorKey,
676
+ type = 'text',
592
677
  fetch,
593
- fetchDeps = [],
678
+ fetchDeps,
594
679
  optimized,
680
+ debounce = 300,
595
681
  defaultChecked,
596
682
  defaultValue,
597
683
  checked,
598
- value,
599
- debounce = 300
684
+ validatorKey,
685
+ value
600
686
  } = _ref,
601
687
  rest = _objectWithoutProperties(_ref, _excluded);
602
- const {
603
- state: {
604
- fields
605
- },
606
- _updateInputHandler,
607
- _dispatchChanges,
608
- optimized: formOptimized,
609
- _viHandler
610
- } = useGenericFormContext();
611
- const inputState = fields[formKey];
688
+ const inputState = useFormSelector(state => state.fields[formKey]);
689
+ const store = useFormStore();
612
690
  const _element = useMemo(() => {
613
691
  let value, checked;
614
692
  if (type === 'checkbox') checked = inputState.value || false;else value = inputState.value || '';
@@ -622,38 +700,62 @@ const GInput = forwardRef((_ref, ref) => {
622
700
  'aria-required': inputState.required,
623
701
  title: title || inputState.errorText
624
702
  });
625
- if (!formOptimized || !optimized) {
626
- _props.onBlur = e => {
627
- _viHandler(inputState, e);
628
- rest.onBlur && rest.onBlur(e);
703
+ if (!store.handlers.optimized || !optimized) {
704
+ _props.onBlur = rest.onBlur ? e => {
705
+ store.handlers._viHandler(inputState, e);
706
+ rest.onBlur(e);
707
+ } : e => {
708
+ store.handlers._viHandler(inputState, e);
629
709
  };
630
- _props.onInvalid = e => {
710
+ _props.onInvalid = rest.onInvalid ? e => {
711
+ e.preventDefault();
712
+ store.handlers._viHandler(inputState, e);
713
+ rest.onInvalid(e);
714
+ } : e => {
631
715
  e.preventDefault();
632
- _viHandler(inputState, e);
633
- rest.onInvalid && rest.onInvalid(e);
716
+ store.handlers._viHandler(inputState, e);
634
717
  };
635
- _props.onChange = (e, unknown) => {
636
- _updateInputHandler(formKey, e, unknown);
637
- rest.onChange && rest.onChange(e);
718
+ _props.onChange = rest.onChange ? (e, unknown) => {
719
+ store.handlers._updateInputHandler(inputState, e, unknown);
720
+ rest.onChange(e);
721
+ } : (e, unknown) => {
722
+ store.handlers._updateInputHandler(inputState, e, unknown);
638
723
  };
724
+ if (!inputState.touched) {
725
+ _props.onFocus = rest.onFocus ? e => {
726
+ rest.onFocus(e);
727
+ inputState.dispatchChanges({
728
+ touched: true
729
+ });
730
+ } : () => {
731
+ inputState.dispatchChanges({
732
+ touched: true
733
+ });
734
+ };
735
+ }
639
736
  }
640
737
  if (element) {
641
738
  return element(inputState, _props);
642
739
  }
643
740
  return React.createElement("input", _props);
644
741
  }, [inputState, element]);
645
- const _fetchDeps = useMemo(() => fetchDeps.map(key => fields[key].value), [fields]);
742
+ const _fetchDeps = useFormSelector(makeSelectFields(fetchDeps));
743
+ const stableFetchDeps = useMemo(() => JSON.stringify(_fetchDeps), [_fetchDeps]);
646
744
  useEffect(() => {
647
745
  if (fetch) {
648
- inputState.dispatchChanges = changes => _dispatchChanges(changes, formKey);
649
746
  _debounce(debounce, `${inputState.gid}-fetch`).then(() => {
650
- const res = fetch(inputState, fields);
651
- res instanceof Promise ? res.then(state => state && _dispatchChanges(state, formKey)) : res && _dispatchChanges(res, formKey);
747
+ const res = fetch(inputState, store.getState().fields);
748
+ if (res instanceof Promise) {
749
+ res.then(state => state && store.handlers._dispatchChanges(state, formKey));
750
+ } else if (res) {
751
+ store.handlers._dispatchChanges(res, formKey);
752
+ }
652
753
  });
653
754
  }
654
- }, _fetchDeps);
755
+ }, [stableFetchDeps]);
655
756
  return _element;
656
757
  });
758
+ const GInput = memo(_GInput);
657
759
 
658
- export { GForm, GInput, GValidator };
760
+ export { GForm, GInput, GValidator, useFormSelector };
659
761
  //# sourceMappingURL=index.development.js.map