@hubsync/esign-web-sdk 6.9.12 → 6.9.14

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.
@@ -457,11 +457,9 @@ export class VerdocsSign {
457
457
  }
458
458
  focusNextFieldAfter(field) {
459
459
  setTimeout(() => {
460
- const fields = this.getSortedFillableFields();
461
- const currentIndex = fields.findIndex(f => f.name === field.name);
462
- if (currentIndex >= 0 && currentIndex < fields.length - 1) {
463
- this.focusFieldElement(fields[currentIndex + 1]);
464
- }
460
+ const next = this.getNextUnfilledField(field);
461
+ if (next)
462
+ this.focusFieldElement(next);
465
463
  }, 100);
466
464
  }
467
465
  focusFieldElement(field) {
@@ -476,11 +474,6 @@ export class VerdocsSign {
476
474
  handleStartSigning() {
477
475
  this.markEnvelopeStarted();
478
476
  this.signingProgressMode = 'signing';
479
- const fields = this.getSortedFillableFields();
480
- const startField = fields.find(field => !this.isFieldActuallyFilled(field)) || fields[0];
481
- if (startField) {
482
- this.focusFieldElement(startField);
483
- }
484
477
  this.adoptingSignature = true;
485
478
  }
486
479
  async handleFieldChange(field, e) {
@@ -513,6 +506,7 @@ export class VerdocsSign {
513
506
  }
514
507
  if (e.detail === null) {
515
508
  console.log('[SIGN] Clearing initial');
509
+ this.initialId = null;
516
510
  const updateResult = await updateEnvelopeField(this.endpoint, this.envelopeId, this.roleId, field.name, null, false);
517
511
  return this.updateRecipientFieldValue(field.name, updateResult);
518
512
  }
@@ -558,6 +552,7 @@ export class VerdocsSign {
558
552
  }
559
553
  if (e.detail === null) {
560
554
  console.log('[SIGN] Clearing signature');
555
+ this.signatureId = null;
561
556
  const updateResult = await updateEnvelopeField(this.endpoint, this.envelopeId, this.roleId, field.name, null, false);
562
557
  return this.updateRecipientFieldValue(field.name, updateResult);
563
558
  }
@@ -623,6 +618,20 @@ export class VerdocsSign {
623
618
  async handleNext() {
624
619
  var _a;
625
620
  if (this.nextSubmits) {
621
+ // Re-verify required fields synchronously before submitting. nextSubmits can be stale if
622
+ // a text field was cleared but not yet blurred before the button was clicked — the focusout
623
+ // save is async, so the local value hasn't updated by the time onClick fires.
624
+ const allFields = this.getSortedFillableFields();
625
+ const requiredIncomplete = allFields.some(f => f.required && !this.isFieldActuallyFilled(f));
626
+ if (requiredIncomplete) {
627
+ this.nextSubmits = false;
628
+ this.nextButtonLabel = 'Next';
629
+ const currentField = allFields.find(f => f.name === this.focusedField);
630
+ const next = this.getNextUnfilledField(currentField);
631
+ if (next)
632
+ this.focusFieldElement(next);
633
+ return;
634
+ }
626
635
  try {
627
636
  // Patches the date picker to be forcibly removed if still showing during submission
628
637
  (_a = document.getElementById('air-datepicker-global-container')) === null || _a === void 0 ? void 0 : _a.remove();
@@ -641,12 +650,10 @@ export class VerdocsSign {
641
650
  }
642
651
  return;
643
652
  }
644
- // Find all unfilled fields and move to the next one in sequence
645
- const allUnfilled = this.getSortedFillableFields().filter(f => !this.isFieldActuallyFilled(f));
646
- const nextUnfilled = this.getNextFieldFromList(allUnfilled);
647
- if (nextUnfilled) {
648
- this.focusFieldElement(nextUnfilled);
649
- }
653
+ const currentField = this.getSortedFillableFields().find(f => f.name === this.focusedField);
654
+ const next = this.getNextUnfilledField(currentField);
655
+ if (next)
656
+ this.focusFieldElement(next);
650
657
  }
651
658
  handleNextOptional() {
652
659
  const unfilledOptional = this.getSortedFillableFields().filter(f => !f.required && !this.isFieldActuallyFilled(f));
@@ -670,8 +677,10 @@ export class VerdocsSign {
670
677
  }
671
678
  // See if everything that "needs to be" filled in is, and all "fillable fields" are valid
672
679
  checkRecipientFields() {
673
- const invalidFields = this.getRecipientFields().filter(field => !isFieldValid(field, this.getRecipientFields()));
674
- if (invalidFields.length < 1) {
680
+ const allFields = this.getSortedFillableFields();
681
+ const requiredIncomplete = allFields.some(f => f.required && !this.isFieldActuallyFilled(f));
682
+ const invalidFilled = allFields.some(f => this.isFieldActuallyFilled(f) && !isFieldValid(f, this.getRecipientFields()));
683
+ if (!requiredIncomplete && !invalidFilled) {
675
684
  this.nextButtonLabel = 'Finish';
676
685
  if (!this.nextSubmits) {
677
686
  this.nextSubmits = true;
@@ -715,20 +724,15 @@ export class VerdocsSign {
715
724
  (field.type !== 'radio' || field.value === 'true') &&
716
725
  (field.type !== 'checkbox' || field.value === 'true'));
717
726
  }
718
- getNextRequiredField() {
719
- // Find and focus the next incomplete field (that is fillable)
720
- const emptyFields = this.getSortedFillableFields().filter(field => field.required && !this.isFieldActuallyFilled(field));
721
- sortFields(emptyFields);
722
- if (emptyFields.length === 0) {
723
- const allUnfilled = this.getSortedFillableFields().filter(field => !this.isFieldActuallyFilled(field));
724
- sortFields(allUnfilled);
725
- if (allUnfilled.length > 0) {
726
- // If we are here, there are no required fields left, but there are optional ones.
727
- return this.getNextFieldFromList(allUnfilled);
728
- }
729
- return null;
730
- }
731
- return this.getNextFieldFromList(emptyFields);
727
+ getNextUnfilledField(after) {
728
+ const allFields = this.getSortedFillableFields();
729
+ const startIndex = after ? allFields.findIndex(f => f.name === after.name) + 1 : 0;
730
+ // Look forward from current position first
731
+ const nextAfter = allFields.slice(startIndex).find(f => !this.isFieldActuallyFilled(f));
732
+ if (nextAfter)
733
+ return nextAfter;
734
+ // Wrap to beginning if nothing unfilled ahead
735
+ return allFields.slice(0, startIndex).find(f => !this.isFieldActuallyFilled(f)) || null;
732
736
  }
733
737
  getNextFieldFromList(fields) {
734
738
  const focusedIndex = fields.findIndex(field => field.name === this.focusedField);
@@ -761,14 +765,16 @@ export class VerdocsSign {
761
765
  // Remove existing flags
762
766
  const existingFlags = controlsDiv.querySelectorAll('.verdocs-flag-instance');
763
767
  existingFlags.forEach(el => el.remove());
764
- let nextField = this.getNextRequiredField();
765
768
  const focusedFieldObj = this.getRecipientFields().find(f => f.name === this.focusedField);
766
- // If the currently focused field is unfilled, we should point the flag to IT, not the next one.
767
- // getNextRequiredField() is designed for the "Next" button (skipping current), but the visual flag
768
- // should guide the user to the current task if it's incomplete.
769
+ // If the currently focused field is unfilled, point the flag to it.
770
+ // Otherwise find the next unfilled field forward from the current position.
771
+ let nextField;
769
772
  if (focusedFieldObj && !this.isFieldActuallyFilled(focusedFieldObj)) {
770
773
  nextField = focusedFieldObj;
771
774
  }
775
+ else {
776
+ nextField = this.getNextUnfilledField(focusedFieldObj);
777
+ }
772
778
  if (nextField && nextField.page === pageInfo.pageNumber && nextField.document_id === pageInfo.documentId) {
773
779
  const variant = 'fill';
774
780
  let label = 'FILL';
@@ -1105,7 +1111,7 @@ export class VerdocsSign {
1105
1111
  return (h("span", { class: "remaining-count" }, remaining, " required field", remaining === 1 ? '' : 's', " left"));
1106
1112
  }
1107
1113
  return (h("span", { class: "remaining-count" }, h("span", { class: "check-icon", innerHTML: CheckCircleIcon }), "All required fields complete", optionalLeft > 0 && h("span", { class: "separator" }, "|"), optionalLeft > 0 && (h("span", { class: "review-optional", onClick: () => this.handleNextOptional() }, "Review ", optionalLeft, " optional"))));
1108
- })(), !this.finishLater && (h("verdocs-button", { size: "xsmall", label: this.nextButtonLabel === 'Next' ? 'Next Required' : this.nextButtonLabel, disabled: !this.agreed || this.submitting, onClick: () => this.handleNext() })), h("div", { class: { 'icon-button': true, 'minus': true, 'disabled': this.zoomLevel === 'normal' }, innerHTML: ToolbarMinusIcon, onClick: () => this.handleZoomOut() }), h("div", { class: { 'icon-button': true, 'plus': true, 'disabled': this.zoomLevel === 'zoom2' }, innerHTML: ToolbarPlusIcon, onClick: () => this.handleZoomIn() }), h("verdocs-dropdown", { options: !this.isDone && !this.finishLater ? inProgressMenuOptions : doneMenuOptions, onOptionSelected: e => this.handleOptionSelected(e) })))), this.toolbarStyle === 'controls' && (h("div", { class: "controls-toolbar" }, h("div", { class: "left-controls" }, h("div", { class: "title" }, this.envelope.name)), h("div", { class: "center-controls", style: { display: 'none' } }, h("span", { class: "label" }, "Page"), h("div", { class: "select-wrapper" }, h("verdocs-select-input", { options: pageOptions, value: this.pageNumber.toString(), onInput: e => this.handlePageSelect(e) })), h("span", { class: "count" }, "of ", totalPages)), h("div", { class: "right-controls" }, h("verdocs-button", { class: "mobile-next-button", label: this.nextButtonLabel, size: "xsmall", disabled: !this.agreed || this.submitting, onClick: () => this.handleNext() }), h("div", { class: { 'icon-button': true, 'minus': true, 'disabled': this.zoomLevel === 'normal' }, innerHTML: ToolbarMinusIcon, onClick: () => this.handleZoomOut() }), h("div", { class: { 'icon-button': true, 'plus': true, 'disabled': this.zoomLevel === 'zoom2' }, innerHTML: ToolbarPlusIcon, onClick: () => this.handleZoomIn() }), h("div", { class: "icon-button download", innerHTML: ToolbarDownloadIcon, onClick: () => this.handleOptionSelected({ detail: { id: 'download' } }) }), h("div", { class: "icon-button print", innerHTML: ToolbarPrintIcon, onClick: () => this.handleOptionSelected({ detail: { id: 'print' } }) })))), h("verdocs-signing-progress", { mode: this.signingProgressMode, focusedField: this.focusedField, fields: this.getSortedFillableFields(), recipientFields: this.getRecipientFields(), onStarted: () => {
1114
+ })(), !this.finishLater && (h("verdocs-button", { size: "xsmall", label: this.nextButtonLabel, disabled: !this.agreed || this.submitting, onClick: () => this.handleNext() })), h("div", { class: { 'icon-button': true, 'minus': true, 'disabled': this.zoomLevel === 'normal' }, innerHTML: ToolbarMinusIcon, onClick: () => this.handleZoomOut() }), h("div", { class: { 'icon-button': true, 'plus': true, 'disabled': this.zoomLevel === 'zoom2' }, innerHTML: ToolbarPlusIcon, onClick: () => this.handleZoomIn() }), h("verdocs-dropdown", { options: !this.isDone && !this.finishLater ? inProgressMenuOptions : doneMenuOptions, onOptionSelected: e => this.handleOptionSelected(e) })))), this.toolbarStyle === 'controls' && (h("div", { class: "controls-toolbar" }, h("div", { class: "left-controls" }, h("div", { class: "title" }, this.envelope.name)), h("div", { class: "center-controls", style: { display: 'none' } }, h("span", { class: "label" }, "Page"), h("div", { class: "select-wrapper" }, h("verdocs-select-input", { options: pageOptions, value: this.pageNumber.toString(), onInput: e => this.handlePageSelect(e) })), h("span", { class: "count" }, "of ", totalPages)), h("div", { class: "right-controls" }, h("verdocs-button", { class: "mobile-next-button", label: this.nextButtonLabel, size: "xsmall", disabled: !this.agreed || this.submitting, onClick: () => this.handleNext() }), h("div", { class: { 'icon-button': true, 'minus': true, 'disabled': this.zoomLevel === 'normal' }, innerHTML: ToolbarMinusIcon, onClick: () => this.handleZoomOut() }), h("div", { class: { 'icon-button': true, 'plus': true, 'disabled': this.zoomLevel === 'zoom2' }, innerHTML: ToolbarPlusIcon, onClick: () => this.handleZoomIn() }), h("div", { class: "icon-button download", innerHTML: ToolbarDownloadIcon, onClick: () => this.handleOptionSelected({ detail: { id: 'download' } }) }), h("div", { class: "icon-button print", innerHTML: ToolbarPrintIcon, onClick: () => this.handleOptionSelected({ detail: { id: 'print' } }) })))), h("verdocs-signing-progress", { mode: this.signingProgressMode, focusedField: this.focusedField, fields: this.getSortedFillableFields(), recipientFields: this.getRecipientFields(), onStarted: () => {
1109
1115
  this.handleStartSigning();
1110
1116
  }, onNext: () => this.handleNext(), onPrevious: () => this.handlePrev(), onExit: () => this.handleNext() }), h("div", { class: `document signed-document-container zoom-${this.zoomLevel}` }, (this.envelope.documents || []).map(envelopeDocument => {
1111
1117
  const pageNumbers = integerSequence(1, envelopeDocument.pages);
@@ -1146,7 +1152,10 @@ export class VerdocsSign {
1146
1152
  this.showSpinner = false;
1147
1153
  this.adoptingSignature = false;
1148
1154
  this.markEnvelopeStarted();
1149
- // Apply the new signature/initials to the field that triggered the dialog.
1155
+ // Apply the new signature/initials to the field that triggered the dialog (e.g. user
1156
+ // clicked an existing sig/initial field). Track which field was applied so we can
1157
+ // advance forward from it rather than jumping back to the start of the document.
1158
+ let appliedToField = null;
1150
1159
  if (this.focusedField) {
1151
1160
  const fieldObj = this.getRecipientFields().find(f => f.name === this.focusedField);
1152
1161
  if (fieldObj) {
@@ -1154,10 +1163,16 @@ export class VerdocsSign {
1154
1163
  if (id) {
1155
1164
  const updateResult = await updateEnvelopeField(this.endpoint, this.envelopeId, this.roleId, fieldObj.name, id, false);
1156
1165
  this.updateRecipientFieldValue(fieldObj.name, updateResult);
1166
+ appliedToField = fieldObj;
1157
1167
  }
1158
1168
  }
1159
1169
  this.focusedField = '';
1160
1170
  }
1171
+ // Navigate to the next unfilled field: forward from where we applied (or from the
1172
+ // beginning when adopting at session start with no pre-focused field).
1173
+ const nextField = this.getNextUnfilledField(appliedToField !== null && appliedToField !== void 0 ? appliedToField : undefined);
1174
+ if (nextField)
1175
+ this.focusFieldElement(nextField);
1161
1176
  // Update any existing field elements in the DOM with the new IDs. This prevents them from
1162
1177
  // needing to show the adoption dialog again if they are clicked.
1163
1178
  const sigFields = this.el.querySelectorAll('verdocs-field-signature');