form-father 0.2.11 → 0.4.0

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/index.js CHANGED
@@ -15,6 +15,18 @@ PERFORMANCE OF THIS SOFTWARE.
15
15
  /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
16
16
 
17
17
 
18
+ function __rest(s, e) {
19
+ var t = {};
20
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
21
+ t[p] = s[p];
22
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
23
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
24
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
25
+ t[p[i]] = s[p[i]];
26
+ }
27
+ return t;
28
+ }
29
+
18
30
  function __awaiter(thisArg, _arguments, P, generator) {
19
31
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
20
32
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -363,6 +375,65 @@ registerBuiltinValidator('tel', v => /^\+7\d{10}$/.test(v), 'Неверный ф
363
375
  registerBuiltinValidator('url', v => isUrlValid(v), 'Неверный формат');
364
376
  registerBuiltinValidator('not-numbers', v => !/[0-9]/.test(v), 'Неверный формат');
365
377
 
378
+ function readFirstIssueMessage(error) {
379
+ var _a;
380
+ if (!error || typeof error !== 'object')
381
+ return undefined;
382
+ const issues = error.issues || error.errors;
383
+ if (Array.isArray(issues)) {
384
+ const firstMessage = (_a = issues.find(issue => typeof (issue === null || issue === void 0 ? void 0 : issue.message) === 'string')) === null || _a === void 0 ? void 0 : _a.message;
385
+ if (firstMessage)
386
+ return firstMessage;
387
+ }
388
+ return typeof error.message === 'string' ? error.message : undefined;
389
+ }
390
+ function resolveMessage(error, message) {
391
+ if (typeof message === 'function')
392
+ return message(error);
393
+ return message || readFirstIssueMessage(error);
394
+ }
395
+ function normalizeResult(result, message) {
396
+ if (typeof result === 'object' && result !== null)
397
+ return result;
398
+ if (result === undefined)
399
+ return true;
400
+ return result ? true : { valid: false, message };
401
+ }
402
+ function createSchemaValidator(schema, options = {}) {
403
+ return (value, $input, $form, params) => __awaiter(this, void 0, void 0, function* () {
404
+ const mappedValue = options.mapValue ? options.mapValue(value, $input, $form, params) : value;
405
+ try {
406
+ if (typeof schema === 'function') {
407
+ return normalizeResult(yield schema(mappedValue), resolveMessage(undefined, options.message));
408
+ }
409
+ if (typeof schema.safeParse === 'function') {
410
+ const result = yield schema.safeParse(mappedValue);
411
+ if (result.success)
412
+ return true;
413
+ return { valid: false, message: resolveMessage(result.error || result, options.message) };
414
+ }
415
+ if (typeof schema.parse === 'function') {
416
+ yield schema.parse(mappedValue);
417
+ return true;
418
+ }
419
+ }
420
+ catch (error) {
421
+ return { valid: false, message: resolveMessage(error, options.message) };
422
+ }
423
+ return { valid: false, message: resolveMessage(undefined, options.message) };
424
+ });
425
+ }
426
+ function registerSchemaValidator(name, schema, defaultMessage, options = {}) {
427
+ const { override } = options, adapterOptions = __rest(options, ["override"]);
428
+ registerValidator(name, createSchemaValidator(schema, adapterOptions), defaultMessage, { override });
429
+ }
430
+ function createFieldValidator(predicate, message) {
431
+ return (value, $input, $form, params) => __awaiter(this, void 0, void 0, function* () {
432
+ const valid = yield predicate(value, $input, $form, params);
433
+ return valid ? true : { valid: false, message };
434
+ });
435
+ }
436
+
366
437
  var _a;
367
438
  // --- shared cross-file state (singleton via globalThis) ---
368
439
  const FORM_GLOBAL_KEY = Symbol.for('formfather.shared');
@@ -374,7 +445,9 @@ const __shared =
374
445
  defaultParams: {},
375
446
  defaultValidationSchema: undefined,
376
447
  });
448
+ const FORM_ERROR_FIELD = '_form';
377
449
  let loaderIdCounter = 0;
450
+ const FORM_INSTANCES = new WeakMap();
378
451
  /** Изначальная дефолтная схема. */
379
452
  const INITIAL_DEFAULT_SCHEMA = {
380
453
  tel: {
@@ -427,6 +500,11 @@ class Form {
427
500
  this.$submits = [];
428
501
  this.waitResponse = false;
429
502
  this.inputs = null;
503
+ this.fieldEventHandlers = [];
504
+ this.liveValidationTimers = new WeakMap();
505
+ this.fieldValidationTokens = new WeakMap();
506
+ this.fieldValidationTokenCounter = 0;
507
+ this.errors = [];
430
508
  /** Общие настройки анимаций (можно подправить под вкус) */
431
509
  this._anim = {
432
510
  duration: 220,
@@ -445,29 +523,27 @@ class Form {
445
523
  loaderColor: '#fff',
446
524
  showLoaderButton: true,
447
525
  scrollToFirstErroredInput: true,
526
+ focusFirstErroredInput: false,
448
527
  logging: false,
449
528
  inputSelector: '.input',
450
529
  inputWrapperSelector: '.input-primary',
530
+ validateOn: 'submit',
531
+ revalidateOn: ['input', 'change'],
532
+ validationDebounce: 0,
533
+ errorContainerAttribute: 'data-error-container',
534
+ validationStateAttribute: 'data-form-father-state',
535
+ observeMutations: false,
451
536
  };
452
537
  /* Слияние параметров: глобальные параметры → пользовательские параметры */
453
538
  this.config = Object.assign({}, defaultConfig, Form.defaultParams, options);
454
539
  if (this.$el) {
455
540
  this.$submits = this.findSubmitElements();
456
- this._onSubmitClickHandler = (e) => __awaiter(this, void 0, void 0, function* () {
457
- var _a, _b, _c, _d;
458
- e.preventDefault();
459
- (_b = (_a = this.config).onBeforeValidate) === null || _b === void 0 ? void 0 : _b.call(_a, this);
460
- const isValid = yield this.validate();
461
- (_d = (_c = this.config).onAfterValidate) === null || _d === void 0 ? void 0 : _d.call(_c, isValid, this);
462
- if (isValid) {
463
- yield this.submit();
464
- }
465
- });
466
541
  if (this.isCorrectArguments()) {
467
- this.$submits.forEach($submit => {
468
- $submit.addEventListener('click', this._onSubmitClickHandler);
469
- });
542
+ this.bindSubmitHandlers();
470
543
  this.initialization();
544
+ this.bindFieldValidationEvents();
545
+ this.setupMutationObserver();
546
+ FORM_INSTANCES.set(this.$el, this);
471
547
  }
472
548
  }
473
549
  else {
@@ -477,6 +553,193 @@ class Form {
477
553
  getOptions() {
478
554
  return this.options;
479
555
  }
556
+ /**
557
+ * Инициализирует сразу несколько форм по CSS-селектору. Если форма уже инициализирована, её настройки обновятся.
558
+ *
559
+ * @param selector - CSS-селектор форм. По умолчанию `form[data-form-father]`.
560
+ * @param options - Общие параметры для найденных форм.
561
+ */
562
+ static initAll(selector = 'form[data-form-father]', options = {}) {
563
+ /* istanbul ignore next -- SSR guard: jsdom does not allow document to be unset cleanly. */
564
+ if (typeof document === 'undefined')
565
+ return [];
566
+ return Array.from(document.querySelectorAll(selector))
567
+ .filter($form => $form.tagName === 'FORM')
568
+ .map($form => {
569
+ const existing = FORM_INSTANCES.get($form);
570
+ if (existing) {
571
+ existing.updateOptions(options);
572
+ return existing;
573
+ }
574
+ return new Form($form, options);
575
+ });
576
+ }
577
+ /** Обновляет настройки конкретного инстанса формы без пересоздания обработчиков вручную. */
578
+ updateOptions(options) {
579
+ this.options = Object.assign(Object.assign({}, this.options), options);
580
+ this.config = Object.assign(Object.assign({}, this.config), options);
581
+ this.refreshControls();
582
+ this.bindSubmitHandlers();
583
+ this.bindFieldValidationEvents();
584
+ this.setupMutationObserver();
585
+ return this;
586
+ }
587
+ /** Возвращает текущий список ошибок формы. */
588
+ getErrors() {
589
+ return this.errors.map(error => (Object.assign({}, error)));
590
+ }
591
+ /** Возвращает значения формы обычным объектом. */
592
+ getValues() {
593
+ return serializeFormToJSON(this.$el);
594
+ }
595
+ normalizeValueList(value) {
596
+ return Array.isArray(value) ? value.map(String) : [String(value)];
597
+ }
598
+ /** Проставляет значения полей по `name` и уведомляет UI через `input`/`change` события. */
599
+ setValues(values) {
600
+ this.getAllFields().forEach($field => {
601
+ if (!$field.name || !Object.prototype.hasOwnProperty.call(values, $field.name))
602
+ return;
603
+ const value = values[$field.name];
604
+ if ($field instanceof HTMLInputElement && $field.type === 'file')
605
+ return;
606
+ if ($field instanceof HTMLInputElement && $field.type === 'checkbox') {
607
+ if (typeof value === 'boolean') {
608
+ $field.checked = value;
609
+ }
610
+ else {
611
+ $field.checked = this.normalizeValueList(value).includes($field.value);
612
+ }
613
+ }
614
+ else if ($field instanceof HTMLInputElement && $field.type === 'radio') {
615
+ $field.checked = String(value) === $field.value;
616
+ }
617
+ else if ($field instanceof HTMLSelectElement && $field.multiple) {
618
+ const valuesList = this.normalizeValueList(value);
619
+ Array.from($field.options).forEach(option => {
620
+ option.selected = valuesList.includes(option.value);
621
+ });
622
+ }
623
+ else {
624
+ $field.value = value == null ? '' : String(value);
625
+ }
626
+ $field.dispatchEvent(new Event('input', { bubbles: true }));
627
+ $field.dispatchEvent(new Event('change', { bubbles: true }));
628
+ });
629
+ return this;
630
+ }
631
+ /** Снимает все клиентские и серверные ошибки с формы, не меняя значения полей. */
632
+ clearErrors() {
633
+ this.errors = [];
634
+ this.getAllFields().forEach($field => {
635
+ this.hideError($field);
636
+ this.clearFieldValidationState($field);
637
+ });
638
+ this.hideErrorForm();
639
+ return this;
640
+ }
641
+ isErrorResponse(issue) {
642
+ return (!!issue &&
643
+ typeof issue === 'object' &&
644
+ typeof issue.name === 'string' &&
645
+ typeof issue['error-msg'] === 'string');
646
+ }
647
+ normalizeValidationIssues(issues, source) {
648
+ if (issues == null || issues === true)
649
+ return [];
650
+ if (issues === false)
651
+ return [{ message: 'Некорректные данные формы', source }];
652
+ if (typeof issues === 'string')
653
+ return [{ message: issues, source }];
654
+ if (Array.isArray(issues)) {
655
+ return issues.flatMap(issue => this.normalizeValidationIssues(issue, source));
656
+ }
657
+ if (this.isErrorResponse(issues)) {
658
+ return [
659
+ {
660
+ field: issues.name,
661
+ rule: source,
662
+ message: issues['error-msg'],
663
+ source,
664
+ },
665
+ ];
666
+ }
667
+ if (typeof issues === 'object' && 'message' in issues && typeof issues.message === 'string') {
668
+ return [Object.assign(Object.assign({}, issues), { source: issues.source || source })];
669
+ }
670
+ if (typeof issues === 'object') {
671
+ return Object.entries(issues).map(([field, message]) => ({
672
+ field,
673
+ rule: source,
674
+ message,
675
+ source,
676
+ }));
677
+ }
678
+ return [];
679
+ }
680
+ applyValidationIssue(issue, source) {
681
+ const issueSource = issue.source || source;
682
+ const rule = issue.rule || issueSource;
683
+ const field = issue.field || FORM_ERROR_FIELD;
684
+ if (field === FORM_ERROR_FIELD) {
685
+ this.setErrorRecord(FORM_ERROR_FIELD, rule, issue.message, issueSource);
686
+ this.showErrorForm(issue.message);
687
+ return null;
688
+ }
689
+ const $field = this.findFieldByName(field);
690
+ if (!$field) {
691
+ this.setErrorRecord(field, rule, issue.message, issueSource);
692
+ console.warn(`Не найдено поле с именем: ${field}, для вывода ошибки: ${issue.message}`);
693
+ return null;
694
+ }
695
+ this.showFieldError($field, issue.message, issueSource, rule);
696
+ return $field;
697
+ }
698
+ /** Показывает несколько ошибок формы или полей: из backend map/array, `ErrorResponse[]` или form validators. */
699
+ setErrors(issues, source = 'server') {
700
+ this.normalizeValidationIssues(issues, source).forEach(issue => {
701
+ this.applyValidationIssue(issue, source);
702
+ });
703
+ return this;
704
+ }
705
+ validateFormValidators() {
706
+ return __awaiter(this, void 0, void 0, function* () {
707
+ const validators = this.config.formValidators
708
+ ? Array.isArray(this.config.formValidators)
709
+ ? this.config.formValidators
710
+ : [this.config.formValidators]
711
+ : [];
712
+ const erroredInputs = [];
713
+ let ok = true;
714
+ for (const validator of validators) {
715
+ const result = yield validator({
716
+ form: this,
717
+ formElement: this.$el,
718
+ values: this.getValues(),
719
+ errors: this.getErrors(),
720
+ });
721
+ const issues = this.normalizeValidationIssues(result, 'client');
722
+ if (issues.length === 0)
723
+ continue;
724
+ ok = false;
725
+ issues.forEach(issue => {
726
+ const $field = this.applyValidationIssue(issue, 'client');
727
+ if ($field)
728
+ erroredInputs.push($field);
729
+ });
730
+ }
731
+ return { ok, erroredInputs };
732
+ });
733
+ }
734
+ /** Сбрасывает форму через native `reset()` и опционально очищает ошибки. */
735
+ reset(options = {}) {
736
+ this.$el.reset();
737
+ this.refreshControls();
738
+ if (options.clearErrors !== false) {
739
+ this.clearErrors();
740
+ }
741
+ return this;
742
+ }
480
743
  /**
481
744
  * Обновляет параметры по умолчанию для настроек формы. Метод объединяет переданные параметры с уже существующими
482
745
  * параметрами по умолчанию.
@@ -487,21 +750,123 @@ class Form {
487
750
  static setDefaultParams(params) {
488
751
  this.defaultParams = Object.assign(Object.assign({}, this.defaultParams), params);
489
752
  }
753
+ refreshControls() {
754
+ this.inputs = this.$el.querySelectorAll(this.config.inputSelector);
755
+ }
490
756
  findSubmitElements() {
491
757
  return Array.from(this.$el.querySelectorAll('input[type="submit"], input[type="image"], button[type="submit"], button:not([type])'));
492
758
  }
759
+ bindSubmitHandlers() {
760
+ if (!this._onSubmitClickHandler) {
761
+ this._onSubmitClickHandler = (e) => __awaiter(this, void 0, void 0, function* () {
762
+ yield this.handleSubmitEvent(e);
763
+ });
764
+ }
765
+ this.$submits.forEach($submit => {
766
+ $submit.removeEventListener('click', this._onSubmitClickHandler);
767
+ });
768
+ this.$submits = this.findSubmitElements();
769
+ this.$submits.forEach($submit => {
770
+ $submit.addEventListener('click', this._onSubmitClickHandler);
771
+ });
772
+ }
493
773
  initialization() {
494
- this.inputs = this.$el.querySelectorAll(this.config.inputSelector);
774
+ this.refreshControls();
495
775
  this._onSubmitHandler = (e) => __awaiter(this, void 0, void 0, function* () {
496
- var _a, _b, _c, _d;
776
+ yield this.handleSubmitEvent(e);
777
+ });
778
+ this.$el.addEventListener('submit', this._onSubmitHandler);
779
+ }
780
+ handleSubmitEvent(e) {
781
+ return __awaiter(this, void 0, void 0, function* () {
782
+ var _a, _b, _c, _d, _e, _f;
497
783
  e.preventDefault();
498
784
  (_b = (_a = this.config).onBeforeValidate) === null || _b === void 0 ? void 0 : _b.call(_a, this);
499
785
  const isValid = yield this.validate();
500
786
  (_d = (_c = this.config).onAfterValidate) === null || _d === void 0 ? void 0 : _d.call(_c, isValid, this);
501
- if (isValid)
502
- this.submit();
787
+ if (!isValid) {
788
+ (_f = (_e = this.config).onValidationError) === null || _f === void 0 ? void 0 : _f.call(_e, this.getErrors(), this);
789
+ return {
790
+ success: false,
791
+ error: this.getErrors(),
792
+ };
793
+ }
794
+ return this.submit();
503
795
  });
504
- this.$el.addEventListener('submit', this._onSubmitHandler);
796
+ }
797
+ normalizeTriggers(value, fallback) {
798
+ const triggers = value == null ? fallback : Array.isArray(value) ? value : [value];
799
+ return Array.from(new Set(triggers));
800
+ }
801
+ clearFieldValidationEvents() {
802
+ this.fieldEventHandlers.forEach(({ $input, event, handler }) => {
803
+ $input.removeEventListener(event, handler);
804
+ });
805
+ this.fieldEventHandlers = [];
806
+ }
807
+ bindFieldValidationEvents() {
808
+ this.clearFieldValidationEvents();
809
+ const validateOn = this.normalizeTriggers(this.config.validateOn, ['submit']).filter((event) => event !== 'submit');
810
+ const revalidateOn = this.normalizeTriggers(this.config.revalidateOn, ['input', 'change']).filter((event) => event !== 'submit');
811
+ const events = Array.from(new Set([...validateOn, ...revalidateOn]));
812
+ if (events.length === 0)
813
+ return;
814
+ const fields = this.getAllFields();
815
+ fields.forEach($input => {
816
+ events.forEach(event => {
817
+ const handler = () => {
818
+ const shouldValidate = validateOn.includes(event) || this.hasFieldError($input);
819
+ if (shouldValidate)
820
+ this.scheduleFieldValidation($input);
821
+ };
822
+ $input.addEventListener(event, handler);
823
+ this.fieldEventHandlers.push({ $input, event, handler });
824
+ });
825
+ });
826
+ }
827
+ scheduleFieldValidation($input) {
828
+ const currentTimer = this.liveValidationTimers.get($input);
829
+ if (currentTimer)
830
+ clearTimeout(currentTimer);
831
+ const delay = Math.max(0, Number(this.config.validationDebounce) || 0);
832
+ const run = () => {
833
+ void this.validateField($input);
834
+ };
835
+ if (delay === 0) {
836
+ run();
837
+ return;
838
+ }
839
+ const timer = setTimeout(run, delay);
840
+ this.liveValidationTimers.set($input, timer);
841
+ }
842
+ setupMutationObserver() {
843
+ var _a;
844
+ (_a = this.mutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
845
+ this.mutationObserver = undefined;
846
+ if (!this.config.observeMutations || typeof MutationObserver === 'undefined')
847
+ return;
848
+ this.mutationObserver = new MutationObserver(() => {
849
+ this.refreshControls();
850
+ this.bindSubmitHandlers();
851
+ this.bindFieldValidationEvents();
852
+ });
853
+ this.mutationObserver.observe(this.$el, {
854
+ childList: true,
855
+ subtree: true,
856
+ });
857
+ }
858
+ getErrorContainer($input) {
859
+ const attrName = this.config.errorContainerAttribute || 'data-error-container';
860
+ const selector = $input.getAttribute(attrName);
861
+ if (!selector)
862
+ return null;
863
+ try {
864
+ return document.querySelector(selector);
865
+ }
866
+ catch (_a) {
867
+ console.warn(`[FormFather] Invalid error container selector "${selector}"`);
868
+ return null;
869
+ }
505
870
  }
506
871
  /**
507
872
  * Показывает ошибку для поля ввода.
@@ -510,6 +875,15 @@ class Form {
510
875
  * @param {String} text - Текст ошибки.
511
876
  */
512
877
  showError($input, text) {
878
+ $input.setAttribute('aria-invalid', 'true');
879
+ const $errorContainer = this.getErrorContainer($input);
880
+ if ($errorContainer) {
881
+ $errorContainer.textContent = text;
882
+ if (!$errorContainer.hasAttribute('role'))
883
+ $errorContainer.setAttribute('role', 'alert');
884
+ $errorContainer.removeAttribute('hidden');
885
+ return;
886
+ }
513
887
  const $inputWrapper = closest($input, this.config.inputWrapperSelector);
514
888
  if (!$inputWrapper) {
515
889
  console.warn(`Не найдена обёртка для поля с именем: ${$input.name || $input.type}`);
@@ -520,7 +894,6 @@ class Form {
520
894
  customShowError.call($inputWrapper, text);
521
895
  return;
522
896
  }
523
- $input.setAttribute('aria-invalid', 'true');
524
897
  $inputWrapper.classList.add('input__wrapper--error');
525
898
  let $error = $inputWrapper.querySelector('[data-form-father-error]');
526
899
  if (!$error) {
@@ -586,7 +959,12 @@ class Form {
586
959
  hideError($input) {
587
960
  var _a;
588
961
  const $inputWrapper = closest($input, this.config.inputWrapperSelector) || $input.parentElement;
962
+ const $errorContainer = this.getErrorContainer($input);
589
963
  $input.removeAttribute('aria-invalid');
964
+ if ($errorContainer) {
965
+ $errorContainer.textContent = '';
966
+ $errorContainer.setAttribute('hidden', '');
967
+ }
590
968
  (_a = $inputWrapper === null || $inputWrapper === void 0 ? void 0 : $inputWrapper.querySelector('[data-form-father-error]')) === null || _a === void 0 ? void 0 : _a.remove();
591
969
  $inputWrapper === null || $inputWrapper === void 0 ? void 0 : $inputWrapper.classList.remove('input__wrapper--error');
592
970
  if ($inputWrapper && $inputWrapper.classList.contains('input__wrapper')) {
@@ -600,7 +978,7 @@ class Form {
600
978
  */
601
979
  scrollToFirstErroredInput(inputsList) {
602
980
  if (inputsList.length > 0) {
603
- const allInputInFormList = Array.from(this.$el.querySelectorAll('input'));
981
+ const allInputInFormList = Array.from(this.$el.querySelectorAll('input, textarea, select'));
604
982
  let $firstInputOnForm = null;
605
983
  let minIndex = Infinity;
606
984
  inputsList.forEach($input => {
@@ -625,125 +1003,333 @@ class Form {
625
1003
  }
626
1004
  }
627
1005
  }
1006
+ focusFirstErroredInput(inputsList) {
1007
+ var _a;
1008
+ if (inputsList.length === 0)
1009
+ return;
1010
+ const allInputInFormList = Array.from(this.$el.querySelectorAll('input, textarea, select'));
1011
+ const $firstInputOnForm = inputsList
1012
+ .slice()
1013
+ .sort((a, b) => allInputInFormList.indexOf(a) - allInputInFormList.indexOf(b))[0];
1014
+ (_a = $firstInputOnForm === null || $firstInputOnForm === void 0 ? void 0 : $firstInputOnForm.focus) === null || _a === void 0 ? void 0 : _a.call($firstInputOnForm, { preventScroll: true });
1015
+ }
1016
+ getValidationSchema() {
1017
+ return Object.assign(Object.assign({}, (Form.defaultValidationSchema || {})), (this.config.validationSchema || {}));
1018
+ }
1019
+ getAllFields($block = this.$el) {
1020
+ return Array.from($block.querySelectorAll('input, textarea, select'));
1021
+ }
1022
+ getFieldKey($input) {
1023
+ return $input.name || $input.id || $input.getAttribute('data-field') || $input.type || 'field';
1024
+ }
1025
+ hasFieldError($input) {
1026
+ const field = this.getFieldKey($input);
1027
+ return $input.getAttribute('aria-invalid') === 'true' || this.errors.some(error => error.field === field);
1028
+ }
1029
+ clearFieldErrorRecord($input) {
1030
+ const field = this.getFieldKey($input);
1031
+ this.errors = this.errors.filter(error => error.field !== field);
1032
+ }
1033
+ setErrorRecord(field, rule, message, source) {
1034
+ this.errors.push({
1035
+ field,
1036
+ rule,
1037
+ message,
1038
+ source,
1039
+ });
1040
+ }
1041
+ getValidationStateAttribute() {
1042
+ return this.config.validationStateAttribute || 'data-form-father-state';
1043
+ }
1044
+ setFieldValidationState($input, state) {
1045
+ const attr = this.getValidationStateAttribute();
1046
+ if (state) {
1047
+ $input.setAttribute(attr, state);
1048
+ }
1049
+ else {
1050
+ $input.removeAttribute(attr);
1051
+ }
1052
+ }
1053
+ startFieldValidation($input) {
1054
+ const token = ++this.fieldValidationTokenCounter;
1055
+ this.fieldValidationTokens.set($input, token);
1056
+ $input.setAttribute('aria-busy', 'true');
1057
+ this.setFieldValidationState($input, 'validating');
1058
+ return token;
1059
+ }
1060
+ isLatestFieldValidation($input, token) {
1061
+ return token == null || this.fieldValidationTokens.get($input) === token;
1062
+ }
1063
+ finishFieldValidation($input, token, isValid) {
1064
+ if (!this.isLatestFieldValidation($input, token))
1065
+ return;
1066
+ $input.removeAttribute('aria-busy');
1067
+ this.setFieldValidationState($input, isValid ? 'valid' : 'invalid');
1068
+ }
1069
+ clearFieldValidationState($input) {
1070
+ this.fieldValidationTokens.delete($input);
1071
+ $input.removeAttribute('aria-busy');
1072
+ this.setFieldValidationState($input, null);
1073
+ }
1074
+ setValidationError($input, rule, message, source) {
1075
+ this.clearFieldErrorRecord($input);
1076
+ this.setErrorRecord(this.getFieldKey($input), rule, message, source);
1077
+ }
1078
+ resolveField(field) {
1079
+ if (typeof field !== 'string')
1080
+ return field;
1081
+ return this.findFieldByName(field);
1082
+ }
1083
+ parseRuleString(ruleString) {
1084
+ const [rule, ...params] = ruleString.split(':').map(s => s.trim());
1085
+ return params.length > 0 ? { rule, params: params.length === 1 ? params[0] : params } : rule;
1086
+ }
1087
+ parseValidationAttribute(value) {
1088
+ return (value !== null && value !== void 0 ? value : '')
1089
+ .split(/[,\s|]+/)
1090
+ .map(s => s.trim())
1091
+ .filter(Boolean)
1092
+ .map(ruleString => this.parseRuleString(ruleString));
1093
+ }
1094
+ getRuleName(rule) {
1095
+ return typeof rule === 'string' ? rule : rule.rule;
1096
+ }
1097
+ getRuleIdentity(rule) {
1098
+ return JSON.stringify(rule);
1099
+ }
1100
+ collectRules($input, fromSchema = [], schemaKeys = new Set()) {
1101
+ const list = [];
1102
+ if ($input.hasAttribute('required'))
1103
+ list.push('required');
1104
+ list.push(...fromSchema);
1105
+ const dataValidateRules = this.parseValidationAttribute($input.getAttribute('data-validate')).filter(rule => {
1106
+ const ruleName = this.getRuleName(rule);
1107
+ return getValidator(ruleName) || !schemaKeys.has(ruleName);
1108
+ });
1109
+ list.push(...dataValidateRules);
1110
+ list.push(...this.parseValidationAttribute($input.getAttribute('data-custom-validate')));
1111
+ const filtered = [];
1112
+ const seen = new Set();
1113
+ for (const rule of list) {
1114
+ const ruleName = this.getRuleName(rule);
1115
+ if (!getValidator(ruleName)) {
1116
+ console.warn(`[FormFather] Unknown validation rule "${ruleName}"`);
1117
+ continue;
1118
+ }
1119
+ const identity = this.getRuleIdentity(rule);
1120
+ if (seen.has(identity))
1121
+ continue;
1122
+ seen.add(identity);
1123
+ filtered.push(rule);
1124
+ }
1125
+ return filtered;
1126
+ }
1127
+ getRuleMessage($input, rule, messages, defaultMessage, overrideMessage) {
1128
+ const attrMessage = $input.getAttribute(`data-error-${rule}`);
1129
+ return overrideMessage || attrMessage || messages[rule] || defaultMessage;
1130
+ }
1131
+ fieldMatchesSchemaDefinition($input, key, def) {
1132
+ if (def.selector) {
1133
+ try {
1134
+ return $input.matches(def.selector);
1135
+ }
1136
+ catch (_a) {
1137
+ console.warn(`[FormFather] Invalid validation selector "${def.selector}"`);
1138
+ return false;
1139
+ }
1140
+ }
1141
+ return this.parseValidationAttribute($input.getAttribute('data-validate')).some(rule => this.getRuleName(rule) === key);
1142
+ }
1143
+ getFieldValidationConfig($input, schema = this.getValidationSchema()) {
1144
+ const rules = [];
1145
+ const messages = {};
1146
+ for (const [key, def] of Object.entries(schema)) {
1147
+ if (!this.fieldMatchesSchemaDefinition($input, key, def))
1148
+ continue;
1149
+ rules.push(...def.rules);
1150
+ Object.assign(messages, def.messages || {});
1151
+ }
1152
+ return {
1153
+ rules: this.collectRules($input, rules, new Set(Object.keys(schema))),
1154
+ messages,
1155
+ };
1156
+ }
1157
+ getValidationFields($block, schema) {
1158
+ const fields = new Set();
1159
+ const addFields = (nodeList) => {
1160
+ Array.from(nodeList).forEach($input => {
1161
+ if ($input instanceof HTMLInputElement ||
1162
+ $input instanceof HTMLTextAreaElement ||
1163
+ $input instanceof HTMLSelectElement) {
1164
+ fields.add($input);
1165
+ }
1166
+ });
1167
+ };
1168
+ for (const [key, def] of Object.entries(schema)) {
1169
+ if (def.selector) {
1170
+ try {
1171
+ addFields($block.querySelectorAll(def.selector));
1172
+ }
1173
+ catch (_a) {
1174
+ console.warn(`[FormFather] Invalid validation selector "${def.selector}"`);
1175
+ }
1176
+ continue;
1177
+ }
1178
+ this.getAllFields($block)
1179
+ .filter($input => this.parseValidationAttribute($input.getAttribute('data-validate')).some(rule => this.getRuleName(rule) === key))
1180
+ .forEach($input => fields.add($input));
1181
+ }
1182
+ addFields($block.querySelectorAll('[required], [data-validate], [data-custom-validate]'));
1183
+ return Array.from(fields);
1184
+ }
1185
+ validateInput($input_1, rules_1) {
1186
+ return __awaiter(this, arguments, void 0, function* ($input, rules, messages = {}, $block = this.$el, validationToken) {
1187
+ if ($input.hasAttribute('data-no-validate'))
1188
+ return true;
1189
+ const isCheckable = $input instanceof HTMLInputElement && ($input.type === 'checkbox' || $input.type === 'radio');
1190
+ const empty = isCheckable ? !$input.checked : $input.value.trim().length === 0;
1191
+ const hasRequiredRule = rules.some(rule => this.getRuleName(rule) === 'required');
1192
+ if (empty && !hasRequiredRule) {
1193
+ if (!this.isLatestFieldValidation($input, validationToken))
1194
+ return !this.hasFieldError($input);
1195
+ this.clearFieldErrorRecord($input);
1196
+ this.hideError($input);
1197
+ return true;
1198
+ }
1199
+ for (const validationRule of rules) {
1200
+ const { rule, params } = typeof validationRule === 'string'
1201
+ ? { rule: validationRule, params: undefined }
1202
+ : { rule: validationRule.rule, params: validationRule.params };
1203
+ const validator = getValidator(rule);
1204
+ if (!validator)
1205
+ continue;
1206
+ const raw = yield validator.fn($input.value, $input, $block, params);
1207
+ const isObjectResult = typeof raw === 'object' && raw !== null;
1208
+ const passed = isObjectResult ? raw.valid === true : !!raw;
1209
+ if (!this.isLatestFieldValidation($input, validationToken))
1210
+ return !this.hasFieldError($input);
1211
+ if (isObjectResult && raw.effect) {
1212
+ yield raw.effect({
1213
+ value: $input.value,
1214
+ $input,
1215
+ $form: $block,
1216
+ params,
1217
+ });
1218
+ }
1219
+ if (!this.isLatestFieldValidation($input, validationToken))
1220
+ return !this.hasFieldError($input);
1221
+ if (!passed) {
1222
+ const result = raw;
1223
+ const message = this.getRuleMessage($input, rule, messages, validator.defaultMessage, isObjectResult && typeof result.message === 'string' ? result.message : undefined);
1224
+ this.setValidationError($input, rule, message, 'client');
1225
+ this.showError($input, message);
1226
+ return false;
1227
+ }
1228
+ if (isObjectResult && raw.stopOthers) {
1229
+ return true;
1230
+ }
1231
+ }
1232
+ if (!this.isLatestFieldValidation($input, validationToken))
1233
+ return !this.hasFieldError($input);
1234
+ this.clearFieldErrorRecord($input);
1235
+ this.hideError($input);
1236
+ return true;
1237
+ });
1238
+ }
1239
+ runFieldValidation($input_1, rules_1) {
1240
+ return __awaiter(this, arguments, void 0, function* ($input, rules, messages = {}, $block = this.$el) {
1241
+ const validationToken = this.startFieldValidation($input);
1242
+ try {
1243
+ const isValid = yield this.validateInput($input, rules, messages, $block, validationToken);
1244
+ this.finishFieldValidation($input, validationToken, isValid);
1245
+ return isValid;
1246
+ }
1247
+ catch (error) {
1248
+ if (this.isLatestFieldValidation($input, validationToken)) {
1249
+ this.clearFieldValidationState($input);
1250
+ }
1251
+ throw error;
1252
+ }
1253
+ });
1254
+ }
1255
+ getRadioGroup($input, $block) {
1256
+ if (!($input instanceof HTMLInputElement) || $input.type !== 'radio' || !$input.name)
1257
+ return [];
1258
+ return Array.from($block.querySelectorAll('input[type="radio"]')).filter($radio => $radio.name === $input.name);
1259
+ }
1260
+ /** Показывает ошибку конкретного поля по имени или DOM-элементу. */
1261
+ showFieldError(field, message, source = 'manual', rule = source) {
1262
+ const $field = this.resolveField(field);
1263
+ if (!$field) {
1264
+ console.warn(`[FormFather] Field "${String(field)}" not found`);
1265
+ return false;
1266
+ }
1267
+ this.setValidationError($field, rule, message, source);
1268
+ this.showError($field, message);
1269
+ $field.removeAttribute('aria-busy');
1270
+ this.setFieldValidationState($field, 'invalid');
1271
+ return true;
1272
+ }
1273
+ /** Проверяет одно поле по имени или DOM-элементу. */
1274
+ validateField(field) {
1275
+ return __awaiter(this, void 0, void 0, function* () {
1276
+ const $field = this.resolveField(field);
1277
+ if (!$field) {
1278
+ console.warn(`[FormFather] Field "${String(field)}" not found`);
1279
+ return false;
1280
+ }
1281
+ const { rules, messages } = this.getFieldValidationConfig($field);
1282
+ return this.runFieldValidation($field, rules, messages);
1283
+ });
1284
+ }
628
1285
  /**
629
1286
  * Проверяет все поля ввода.
630
1287
  *
631
- * Порядок для КАЖДОГО `<input>`:
1288
+ * Порядок для каждого поля:
632
1289
  *
633
1290
  * 1. `required` — если атрибут `required`
634
1291
  * 2. правила из `validationSchema`
635
- * 3. правила из `data-custom-validate`
1292
+ * 3. правила из `data-validate`
1293
+ * 4. правила из `data-custom-validate`
636
1294
  *
637
1295
  * Для одного поля показывается только первая ошибка. Неизвестные правила фиксируются `console.warn` и исключаются ДО
638
- * валидации, поэтому валидационный цикл больше не проверяет их наличие.
1296
+ * валидации.
639
1297
  */
640
1298
  validate() {
641
1299
  return __awaiter(this, arguments, void 0, function* ($block = this.$el) {
642
- /* ---------- финальная схема (defaults + config) ---------- */
643
- const schema = Object.assign(Object.assign({}, (Form.defaultValidationSchema || {})), (this.config.validationSchema || {}));
644
- const warnUnknown = (r) => console.warn(`[FormFather] Unknown validation rule "${r}"`);
1300
+ const schema = this.getValidationSchema();
645
1301
  const erroredInputs = [];
646
1302
  const processed = new WeakSet();
647
1303
  let ok = true;
648
- /* ---------- helpers ---------- */
649
- /** Собирает и тут же фильтрует unknown-rules */
650
- const collectRules = ($input, fromSchema = []) => {
651
- var _a;
652
- const list = [];
653
- if ($input.hasAttribute('required'))
654
- list.push('required'); // 1
655
- list.push(...fromSchema); // 2
656
- const custom = ((_a = $input.getAttribute('data-custom-validate')) !== null && _a !== void 0 ? _a : '')
657
- .split(',')
658
- .map(s => s.trim())
659
- .filter(Boolean);
660
- list.push(...custom.map(ruleString => {
661
- const [rule, ...params] = ruleString.split(':').map(s => s.trim());
662
- return params.length > 0 ? { rule, params: params.length === 1 ? params[0] : params } : rule;
663
- })); // 3
664
- /* —–– предварительная фильтрация unknown ––– */
665
- const filtered = [];
666
- for (const r of list) {
667
- const ruleName = typeof r === 'string' ? r : r.rule;
668
- if (getValidator(ruleName))
669
- filtered.push(r);
670
- else
671
- warnUnknown(ruleName);
1304
+ this.errors = [];
1305
+ for (const $input of this.getValidationFields($block, schema)) {
1306
+ if (processed.has($input))
1307
+ continue;
1308
+ const radioGroup = this.getRadioGroup($input, $block);
1309
+ if (radioGroup.length > 0) {
1310
+ radioGroup.forEach($radio => processed.add($radio));
672
1311
  }
673
- return filtered;
674
- };
675
- const validateInput = ($input_1, rules_1, ...args_1) => __awaiter(this, [$input_1, rules_1, ...args_1], void 0, function* ($input, rules, msgs = {}) {
676
- if ($input.hasAttribute('data-no-validate'))
677
- return;
678
- const empty = $input.type === 'checkbox' || $input.type === 'radio' ? !$input.checked : $input.value.trim().length === 0;
679
- const hasRequiredRule = rules.some(r => (typeof r === 'string' ? r === 'required' : r.rule === 'required'));
680
- if (empty && !hasRequiredRule) {
681
- this.hideError($input);
682
- return;
683
- }
684
- for (const r of rules) {
685
- const { rule, params } = typeof r === 'string' ? { rule: r, params: undefined } : r;
686
- const vd = getValidator(rule); // гарантированно существует
687
- const raw = yield vd.fn($input.value, $input, $block, params);
688
- const isObj = typeof raw === 'object' && raw !== null;
689
- const passed = isObj ? raw.valid === true : !!raw;
690
- // сайд-эффект (если есть)
691
- if (isObj && raw.effect) {
692
- yield raw.effect({
693
- value: $input.value,
694
- $input,
695
- $form: $block,
696
- params,
697
- });
698
- }
699
- if (!passed) {
700
- ok = false;
701
- const r = raw;
702
- const msg = (isObj && typeof r.message === 'string' && r.message) || msgs[rule] || vd.defaultMessage;
703
- this.showError($input, msg);
704
- erroredInputs.push($input);
705
- return; // первую ошибку отобразили
706
- }
707
- // хотим прервать цепочку правил, даже если успех
708
- if (isObj && raw.stopOthers) {
709
- return;
710
- }
711
- }
712
- this.hideError($input);
713
- });
714
- const getRadioGroup = ($input) => {
715
- if ($input.type !== 'radio' || !$input.name)
716
- return [];
717
- return Array.from($block.querySelectorAll('input[type="radio"]')).filter($radio => $radio.name === $input.name);
718
- };
719
- /* ---------- 1. inputs из schema (config + defaults) ---------- */
720
- for (const [key, def] of Object.entries(schema)) {
721
- const { selector, rules = [], messages = {} } = def;
722
- const nodeList = selector
723
- ? $block.querySelectorAll(selector) // явный CSS-селектор
724
- : $block.querySelectorAll(`[data-validate="${key}"]`); // fallback по name
725
- for (const $input of Array.from(nodeList)) {
726
- if (processed.has($input))
727
- continue;
728
- const radioGroup = getRadioGroup($input);
729
- if (radioGroup.length > 0) {
730
- radioGroup.forEach($radio => processed.add($radio));
731
- yield validateInput($input, collectRules($input, rules), messages);
732
- continue;
733
- }
1312
+ else {
734
1313
  processed.add($input);
735
- yield validateInput($input, collectRules($input, rules), messages);
1314
+ }
1315
+ const { rules, messages } = this.getFieldValidationConfig($input, schema);
1316
+ const fieldValid = yield this.runFieldValidation($input, rules, messages, $block);
1317
+ if (!fieldValid) {
1318
+ ok = false;
1319
+ erroredInputs.push($input);
736
1320
  }
737
1321
  }
738
- /* ---------- 2. inputs только с data-custom ---------- */
739
- const rest = Array.from($block.querySelectorAll('[data-custom-validate]')).filter($i => !processed.has($i));
740
- for (const $input of rest) {
741
- processed.add($input);
742
- yield validateInput($input, collectRules($input, []));
1322
+ const formValidation = yield this.validateFormValidators();
1323
+ if (!formValidation.ok) {
1324
+ ok = false;
1325
+ erroredInputs.push(...formValidation.erroredInputs);
743
1326
  }
744
- /* ---------- scroll ---------- */
1327
+ const visibleErroredInputs = erroredInputs.filter(item => !item.hasAttribute('data-no-error-scroll'));
745
1328
  if (!ok && this.config.scrollToFirstErroredInput) {
746
- this.scrollToFirstErroredInput(erroredInputs.filter(item => !item.hasAttribute('data-no-error-scroll')));
1329
+ this.scrollToFirstErroredInput(visibleErroredInputs);
1330
+ }
1331
+ if (!ok && this.config.focusFirstErroredInput) {
1332
+ this.focusFirstErroredInput(visibleErroredInputs);
747
1333
  }
748
1334
  return ok;
749
1335
  });
@@ -949,6 +1535,7 @@ class Form {
949
1535
  }
950
1536
  /** Полностью снимает слушатели и чистит артефакты формы. */
951
1537
  destroy() {
1538
+ var _a;
952
1539
  // 1) Снять submit-слушатель
953
1540
  if (this._onSubmitHandler) {
954
1541
  this.$el.removeEventListener('submit', this._onSubmitHandler);
@@ -961,6 +1548,9 @@ class Form {
961
1548
  });
962
1549
  this._onSubmitClickHandler = undefined;
963
1550
  }
1551
+ this.clearFieldValidationEvents();
1552
+ (_a = this.mutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
1553
+ this.mutationObserver = undefined;
964
1554
  // 2) Удалить формовый error-блок под инпутами (если есть)
965
1555
  const formErrorWrapper = this.$el.querySelector('.error-block-under-input__wrapper');
966
1556
  if (formErrorWrapper) {
@@ -976,13 +1566,18 @@ class Form {
976
1566
  });
977
1567
  // 4) Сбросить ошибки у инпутов (если разметка позволяет)
978
1568
  const inputs = this.$el.querySelectorAll('input, textarea, select');
979
- inputs.forEach($i => this.hideError($i));
1569
+ inputs.forEach($i => {
1570
+ this.hideError($i);
1571
+ this.clearFieldValidationState($i);
1572
+ });
980
1573
  // 5) Убрать возможный флаг скролла
981
1574
  document.documentElement.removeAttribute('data-now-scrolling');
982
1575
  // 6) Очистить ссылки на элементы, чтобы помочь GC
983
1576
  this.inputs = null;
984
1577
  this.$submits = [];
1578
+ this.errors = [];
985
1579
  this.waitResponse = false;
1580
+ FORM_INSTANCES.delete(this.$el);
986
1581
  }
987
1582
  /**
988
1583
  * Проверяет входные данные на их корректность.
@@ -1099,7 +1694,10 @@ class Form {
1099
1694
  const $field = this.findFieldByName(error.name);
1100
1695
  if ($field) {
1101
1696
  fieldsList.push($field);
1697
+ this.setValidationError($field, 'server', error['error-msg'], 'server');
1102
1698
  this.showError($field, error['error-msg']);
1699
+ $field.removeAttribute('aria-busy');
1700
+ this.setFieldValidationState($field, 'invalid');
1103
1701
  }
1104
1702
  else {
1105
1703
  console.warn(`Не найдено поле с именем: ${error.name}, для вывода ошибки: ${error['error-msg']}`);
@@ -1107,6 +1705,8 @@ class Form {
1107
1705
  });
1108
1706
  if (this.config.scrollToFirstErroredInput === true)
1109
1707
  this.scrollToFirstErroredInput(fieldsList);
1708
+ if (this.config.focusFirstErroredInput === true)
1709
+ this.focusFirstErroredInput(fieldsList);
1110
1710
  }
1111
1711
  }
1112
1712
  sendData() {
@@ -1167,12 +1767,8 @@ class Form {
1167
1767
  }
1168
1768
  submit() {
1169
1769
  return __awaiter(this, void 0, void 0, function* () {
1170
- var _a, _b, _c, _d, _e, _f, _g, _h;
1770
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
1171
1771
  if (!this.waitResponse) {
1172
- if (this.config.showLoaderButton === true)
1173
- this.showLoader();
1174
- (_b = (_a = this.config).onSubmit) === null || _b === void 0 ? void 0 : _b.call(_a, this);
1175
- this.waitResponse = true;
1176
1772
  let loadingFinished = false;
1177
1773
  const finishLoading = () => {
1178
1774
  if (loadingFinished)
@@ -1183,27 +1779,44 @@ class Form {
1183
1779
  loadingFinished = true;
1184
1780
  };
1185
1781
  try {
1782
+ const beforeSubmitResult = yield ((_b = (_a = this.config).onBeforeSubmit) === null || _b === void 0 ? void 0 : _b.call(_a, this));
1783
+ if (beforeSubmitResult === false) {
1784
+ return {
1785
+ success: false,
1786
+ };
1787
+ }
1788
+ if (this.config.showLoaderButton === true)
1789
+ this.showLoader();
1790
+ (_d = (_c = this.config).onSubmit) === null || _d === void 0 ? void 0 : _d.call(_c, this);
1791
+ this.waitResponse = true;
1186
1792
  const response = yield this.sendData();
1187
1793
  const responseBody = yield this.parseResponseBody(response);
1188
1794
  finishLoading();
1189
- (_d = (_c = this.config).onResponse) === null || _d === void 0 ? void 0 : _d.call(_c, responseBody, this);
1795
+ (_f = (_e = this.config).onResponse) === null || _f === void 0 ? void 0 : _f.call(_e, responseBody, this);
1190
1796
  if (response.ok && responseBody.success === true) {
1191
- (_f = (_e = this.config).onResponseSuccess) === null || _f === void 0 ? void 0 : _f.call(_e, responseBody, this);
1797
+ (_h = (_g = this.config).onResponseSuccess) === null || _h === void 0 ? void 0 : _h.call(_g, responseBody, this);
1192
1798
  }
1193
1799
  else {
1194
- (_h = (_g = this.config).onResponseUnsuccess) === null || _h === void 0 ? void 0 : _h.call(_g, responseBody, this);
1800
+ (_k = (_j = this.config).onResponseUnsuccess) === null || _k === void 0 ? void 0 : _k.call(_j, responseBody, this);
1195
1801
  this.handleUnsuccessfulResponse(responseBody);
1196
1802
  }
1197
1803
  parseCommonResponseProperties(responseBody);
1804
+ return {
1805
+ success: response.ok && responseBody.success === true,
1806
+ response,
1807
+ responseBody,
1808
+ };
1198
1809
  }
1199
1810
  catch (error) {
1200
1811
  finishLoading();
1812
+ (_m = (_l = this.config).onSubmitError) === null || _m === void 0 ? void 0 : _m.call(_l, error, this);
1201
1813
  throw error;
1202
1814
  }
1203
1815
  }
1816
+ return undefined;
1204
1817
  });
1205
1818
  }
1206
1819
  }
1207
1820
 
1208
- export { blockScrollBody, closest, Form as default, getAllValidators, getValidator, isEmailValid, isPhoneValid, isUrlValid, parseCommonResponseProperties, registerValidator, serializeFormToJSON, serializeToFormData, unblockScrollBody };
1821
+ export { FORM_ERROR_FIELD, blockScrollBody, closest, createFieldValidator, createSchemaValidator, Form as default, getAllValidators, getValidator, isEmailValid, isPhoneValid, isUrlValid, parseCommonResponseProperties, registerSchemaValidator, registerValidator, serializeFormToJSON, serializeToFormData, unblockScrollBody };
1209
1822
  //# sourceMappingURL=index.js.map