@unvired/turboforms-embed-sdk 1.0.21 → 1.0.22

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.
@@ -513,12 +513,16 @@ body {
513
513
  box-sizing: border-box;
514
514
  background-color: #f5f5f5;
515
515
  font-family: Arial, sans-serif;
516
+ overflow: hidden; /* Prevent window from scrolling */
516
517
  }
517
518
 
518
519
  .formio-wrapper {
519
520
  border: 1px solid #ccc;
520
521
  background-color: #fff;
521
522
  border-radius: 8px;
523
+ overflow-y: auto; /* Enable internal scrolling */
524
+ -webkit-overflow-scrolling: touch;
525
+ height: 100%; /* Fill space */
522
526
  }
523
527
 
524
528
  #progressContainer {
@@ -1084,15 +1088,34 @@ body {
1084
1088
  }
1085
1089
 
1086
1090
  /* Add buffer at bottom to allow last components to scroll above the keyboard */
1087
- #form-container {
1088
- padding-bottom: 500px !important;
1091
+ #formio-wrapper {
1092
+ padding-bottom: 800px !important;
1089
1093
  }
1090
1094
 
1091
- /* Ensure DataGrid rows allow dropdowns to overflow now that we disabled fixed positioning on iOS */
1092
- .formio-component-datagrid .ui.table tr,
1093
- .formio-component-datagrid .ui.table td {
1095
+ /* Logically add space below the select when opened to allow scrolling the last row higher */
1096
+ .choices.is-open {
1097
+ margin-bottom: 800px !important;
1094
1098
  overflow: visible !important;
1095
1099
  }
1100
+
1101
+ /* Prevent dropdown from being layered behind table headers or other rows */
1102
+ .choices__list--dropdown {
1103
+ z-index: 10000 !important;
1104
+ overflow-y: auto !important;
1105
+ -webkit-overflow-scrolling: touch;
1106
+ }
1107
+
1108
+ /* Ensure the internal list doesn't have its own scrollbar/max-height that clips items */
1109
+ .choices__list--dropdown .choices__list {
1110
+ max-height: none !important;
1111
+ overflow: visible !important;
1112
+ padding-bottom: 20px !important;
1113
+ }
1114
+
1115
+ /* Add extra space for the last item to ensure it's not clipped on iOS */
1116
+ .choices__list--dropdown .choices__item:last-child {
1117
+ padding-bottom: 20px !important;
1118
+ }
1096
1119
  }</style>
1097
1120
  <style>.r6o-drawing{cursor:none}.r6o-relations-layer.readonly .handle rect{pointer-events:none}.r6o-relations-layer{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.r6o-relations-layer circle{stroke:#515151;stroke-width:.4;fill:#3f3f3f}.r6o-relations-layer path{stroke:#595959;stroke-linecap:round;stroke-linejoin:round;fill:transparent}.r6o-relations-layer path.connection{stroke-width:1.6;stroke-dasharray:2,3}.r6o-relations-layer path.r6o-arrow{stroke-width:1.8;fill:#7f7f7f}.r6o-relations-layer .handle rect{stroke-width:1;stroke:#595959;fill:#fff;pointer-events:auto;cursor:pointer}.r6o-relations-layer .handle text{font-size:10px}.r6o-relations-layer .hover{stroke:rgba(63,63,63,.9);stroke-width:1.4;fill:transparent}
1098
1121
  .r6o-btn{background-color:#4483c4;border:1px solid #4483c4;box-sizing:border-box;color:#fff;cursor:pointer;display:inline-block;font-size:14px;margin:0;outline:none;text-decoration:none;white-space:nowrap;padding:6px 18px;min-width:70px;vertical-align:middle;-webkit-border-radius:2px;-khtml-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.r6o-btn *{vertical-align:middle;cursor:pointer}.r6o-btn .r6o-icon{margin-right:4px}.r6o-btn:disabled{border-color:#a3c2e2 !important;background-color:#a3c2e2 !important}.r6o-btn:hover{background-color:#4f92d7;border-color:#4f92d7}.r6o-btn.outline{border:1px solid #4483c4;color:#4483c4;background-color:transparent;text-shadow:none}.r6o-autocomplete{display:inline;position:relative}.r6o-autocomplete div[role=combobox]{display:inline}.r6o-autocomplete input{outline:none;border:none;width:80px;height:100%;line-height:14px;white-space:pre;box-sizing:border-box;background-color:transparent;font-size:14px;color:#3f3f3f}.r6o-autocomplete ul{position:absolute;margin:0;padding:0;list-style-type:none;background-color:#fff;border-radius:3px;border:1px solid #d6d7d9;box-sizing:border-box;box-shadow:0 0 20px rgba(0,0,0,.25)}.r6o-autocomplete ul:empty{display:none}.r6o-autocomplete li{box-sizing:border-box;padding:2px 12px;width:100%;cursor:pointer}.r6o-editable-text{max-height:120px;overflow:auto;outline:none;min-height:2em;font-size:14px;font-family:"Lato",sans-serif}.r6o-editable-text:empty:not(:focus):before{content:attr(data-placeholder);color:#c2c2c2}.r6o-widget.comment{font-size:14px;min-height:3em;background-color:#fff;position:relative}.r6o-widget.comment .r6o-editable-text,.r6o-widget.comment .r6o-readonly-comment{padding:10px;width:100%;box-sizing:border-box;outline:none;border:none;background-color:transparent;resize:none}.r6o-widget.comment .r6o-readonly-comment{white-space:pre}.r6o-widget.comment .r6o-editable-text::-webkit-input-placeholder{color:#c2c2c2}.r6o-widget.comment .r6o-editable-text::-moz-placeholder{color:#c2c2c2}.r6o-widget.comment .r6o-editable-text:-moz-placeholder{color:#c2c2c2}.r6o-widget.comment .r6o-editable-text:-ms-input-placeholder{color:#c2c2c2}.r6o-widget.comment .r6o-lastmodified{border:1px solid #e5e5e5;display:inline-block;border-radius:2px;margin:0 10px 8px 10px;padding:4px 5px;line-height:100%;font-size:12px}.r6o-widget.comment .r6o-lastmodified .r6o-lastmodified-at{color:#757575;padding-left:3px}.r6o-widget.comment .r6o-arrow-down{position:absolute;height:20px;width:20px;top:9px;right:9px;line-height:22px;background-color:#fff;text-align:center;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;cursor:pointer;-webkit-border-radius:1px;-khtml-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.r6o-widget.comment .r6o-arrow-down.r6o-menu-open{border-color:#4483c4}.r6o-widget.comment .r6o-comment-dropdown-menu{position:absolute;top:32px;right:8px;background-color:#fff;border:1px solid #e5e5e5;list-style-type:none;margin:0;padding:5px 0;z-index:9999;-webkit-box-shadow:0 2px 4px rgba(0,0,0,.2);-moz-box-shadow:0 2px 4px rgba(0,0,0,.2);box-shadow:0 2px 4px rgba(0,0,0,.2)}.r6o-widget.comment .r6o-comment-dropdown-menu li{padding:0 15px;cursor:pointer}.r6o-widget.comment .r6o-comment-dropdown-menu li:hover{background-color:#ecf0f1}.r6o-widget.comment .r6o-purposedropdown{position:relative;z-index:2}.r6o-widget.comment.editable{background-color:#ecf0f1}.r6o-widget.r6o-tag:empty{display:none}@media all and (-ms-high-contrast: none),(-ms-high-contrast: active){.r6o-widget.tag .r6o-taglist li{height:27px}.r6o-widget.tag .r6o-taglist li .r6o-delete-wrapper .r6o-delete{position:relative;top:-4px}}.r6o-widget.r6o-tag{background-color:#ecf0f1;border-bottom:1px solid #e5e5e5;padding:1px 3px;display:flex}.r6o-widget.r6o-tag ul{margin:0;padding:0;list-style-type:none;z-index:1}.r6o-widget.r6o-tag ul.r6o-taglist{flex:0;white-space:nowrap}.r6o-widget.r6o-tag ul.r6o-taglist li{margin:0;display:inline-block;margin:1px 1px 1px 0;padding:0;vertical-align:middle;overflow:hidden;font-size:12px;background-color:#fff;border:1px solid #d6d7d9;cursor:pointer;position:relative;line-height:180%;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-border-radius:2px;-khtml-border-radius:2px;-moz-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 4px rgba(0,0,0,.1);-moz-box-shadow:0 0 4px rgba(0,0,0,.1);box-shadow:0 0 4px rgba(0,0,0,.1)}.r6o-widget.r6o-tag ul.r6o-taglist li .r6o-label{padding:2px 8px;display:inline-block}.r6o-widget.r6o-tag ul.r6o-taglist li .r6o-delete-wrapper{display:inline-block;padding:2px 0;color:#fff;width:0;height:100%;background-color:#4483c4;-webkit-border-top-right-radius:2px;-webkit-border-bottom-right-radius:2px;-khtml-border-radius-topright:2px;-khtml-border-radius-bottomright:2px;-moz-border-radius-topright:2px;-moz-border-radius-bottomright:2px;border-top-right-radius:2px;border-bottom-right-radius:2px}.r6o-widget.r6o-tag ul.r6o-taglist li .r6o-delete-wrapper .r6o-delete{padding:2px 6px}.r6o-widget.r6o-tag ul.r6o-taglist li .r6o-delete-wrapper svg{vertical-align:text-top}.r6o-widget.r6o-tag ul.r6o-taglist li .r6o-delete-enter-active{width:24px;transition:width 200ms}.r6o-widget.r6o-tag ul.r6o-taglist li .r6o-delete-enter-done{width:24px}.r6o-widget.r6o-tag ul.r6o-taglist li .r6o-delete-exit{width:24px}.r6o-widget.r6o-tag ul.r6o-taglist li .r6o-delete-exit-active{width:0;transition:width 200ms}.r6o-widget.r6o-tag .r6o-autocomplete{flex:1;position:relative}.r6o-widget.r6o-tag .r6o-autocomplete li{font-size:14px}.r6o-widget.r6o-tag input{width:100%;padding:0 3px;min-width:80px;outline:none;border:none;line-height:170%;background-color:transparent;color:#3f3f3f}.r6o-widget.r6o-tag input::-webkit-input-placeholder{color:#c2c2c2}.r6o-widget.r6o-tag input::-moz-placeholder{color:#c2c2c2}.r6o-widget.r6o-tag input:-moz-placeholder{color:#c2c2c2}.r6o-widget.r6o-tag input:-ms-input-placeholder{color:#c2c2c2}.r6o-editor{position:absolute;z-index:99999;width:400px;color:#3f3f3f;opacity:0;font-family:"Lato",sans-serif;font-size:17px;line-height:27px;-webkit-transition:opacity .2s ease-in;-moz-transition:opacity .2s ease-in;transition:opacity .2s ease-in}.r6o-editor .r6o-arrow{position:absolute;overflow:hidden;top:-12px;left:12px;width:28px;height:12px;display:none}.r6o-editor .r6o-arrow:after{content:"";position:absolute;top:5px;left:5px;width:18px;height:18px;background-color:#fff;-webkit-backface-visibility:hidden;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.r6o-editor .r6o-editor-inner{background-color:#fff;-webkit-border-radius:2px;-khtml-border-radius:2px;-moz-border-radius:2px;border-radius:2px;-webkit-box-shadow:2px 2px 42px rgba(0,0,0,.4);-moz-box-shadow:2px 2px 42px rgba(0,0,0,.4);box-shadow:2px 2px 42px rgba(0,0,0,.4)}.r6o-editor .r6o-editor-inner .r6o-widget:first-child{-webkit-border-top-left-radius:2px;-webkit-border-top-right-radius:2px;-khtml-border-radius-topleft:2px;-khtml-border-radius-topright:2px;-moz-border-radius-topleft:2px;-moz-border-radius-topright:2px;border-top-left-radius:2px;border-top-right-radius:2px}.r6o-editor .r6o-editor-inner .r6o-widget{border-bottom:1px solid #e5e5e5}.r6o-editor .r6o-footer{position:relative;text-align:right;padding:8px 0}.r6o-editor .r6o-footer .r6o-btn{margin-right:8px}.r6o-editor .r6o-footer .r6o-btn.delete-annotation{position:absolute;top:7px;left:7px;background-color:transparent;border:none;color:#4483c4;width:32px;height:32px;min-width:0;border-radius:100%;padding:0;display:flex;justify-content:center;align-items:center;-webkit-transition:all .1s ease-in;-moz-transition:all .1s ease-in;-o-transition:all .1s ease-in;transition:all .1s ease-in}.r6o-editor .r6o-footer .r6o-btn.delete-annotation:hover{color:#fff;background-color:#ef352c}@media(max-width: 640px){.r6o-editor{width:260px}}.r6o-editor.r6o-arrow-top .r6o-arrow{display:block}.r6o-editor.r6o-arrow-right{margin-left:8px}.r6o-editor.r6o-arrow-right .r6o-arrow{left:auto;right:12px}.r6o-editor.r6o-arrow-bottom .r6o-arrow{display:block;top:auto;bottom:-12px}.r6o-editor.r6o-arrow-bottom .r6o-arrow::after{top:-11px;box-shadow:none}.r6o-editor.pushed .r6o-arrow,.r6o-editor.dragged .r6o-arrow{display:none}.r6o-editor .r6o-draggable{cursor:move}.r6o-purposedropdown{width:150px;display:inline-block}.r6o-noselect{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.r6o-editor{margin-left:-12px}.r6o-annotation{background-color:rgba(255,165,0,.2);border-bottom:2px solid orange;cursor:pointer}.r6o-selection{background-color:rgba(207,207,255,.63);cursor:pointer}.r6o-hide-selection::selection,.r6o-hide-selection ::selection{background:transparent}.r6o-hide-selection::-moz-selection .r6o-hide-selection ::-moz-selection{background:transparent}.r6o-relation-editor{position:absolute;font-family:"Lato",sans-serif;font-size:17px;line-height:27px;-webkit-box-shadow:0 1px 14px rgba(0,0,0,.4);-moz-box-shadow:0 1px 14px rgba(0,0,0,.4);box-shadow:0 1px 14px rgba(0,0,0,.4);-webkit-border-radius:3px;-khtml-border-radius:3px;-moz-border-radius:3px;border-radius:3px;transform:translate(-50%, -50%);background-color:#fff}.r6o-relation-editor svg{vertical-align:middle;shape-rendering:geometricPrecision}.r6o-relation-editor *{box-sizing:border-box}.r6o-relation-editor .input-wrapper{height:34px;padding:0 6px;margin-right:68px;font-size:14px;background-color:#ecf0f1;cursor:text;-webkit-border-top-left-radius:3px;-webkit-border-bottom-left-radius:3px;-khtml-border-radius-topleft:3px;-khtml-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px;-moz-border-radius-bottomleft:3px;border-top-left-radius:3px;border-bottom-left-radius:3px}.r6o-relation-editor .input-wrapper .r6o-autocomplete ul{position:relative;left:-6px}.r6o-relation-editor .buttons{position:absolute;display:inline-flex;top:0;right:0}.r6o-relation-editor .buttons span{height:34px;display:inline-block;width:34px;text-align:center;font-size:14px;cursor:pointer;padding:1px 0}.r6o-relation-editor .buttons .delete{background-color:#fff;color:#9ca4b1;border-left:1px solid #e5e5e5}.r6o-relation-editor .buttons .delete:hover{background-color:#f6f6f6}.r6o-relation-editor .buttons .ok{background-color:#4483c4;color:#fff;-webkit-border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;-khtml-border-radius-topright:3px;-khtml-border-radius-bottomright:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.r6o-relation-editor .buttons .ok:hover{background-color:#4f92d7}
@@ -40578,12 +40601,16 @@ body {
40578
40601
  box-sizing: border-box;
40579
40602
  background-color: #f5f5f5;
40580
40603
  font-family: Arial, sans-serif;
40604
+ overflow: hidden; /* Prevent window from scrolling */
40581
40605
  }
40582
40606
 
40583
40607
  .formio-wrapper {
40584
40608
  border: 1px solid #ccc;
40585
40609
  background-color: #fff;
40586
40610
  border-radius: 8px;
40611
+ overflow-y: auto; /* Enable internal scrolling */
40612
+ -webkit-overflow-scrolling: touch;
40613
+ height: 100%; /* Fill space */
40587
40614
  }
40588
40615
 
40589
40616
  #progressContainer {
@@ -41149,15 +41176,34 @@ body {
41149
41176
  }
41150
41177
 
41151
41178
  /* Add buffer at bottom to allow last components to scroll above the keyboard */
41152
- #form-container {
41153
- padding-bottom: 500px !important;
41179
+ #formio-wrapper {
41180
+ padding-bottom: 800px !important;
41154
41181
  }
41155
41182
 
41156
- /* Ensure DataGrid rows allow dropdowns to overflow now that we disabled fixed positioning on iOS */
41157
- .formio-component-datagrid .ui.table tr,
41158
- .formio-component-datagrid .ui.table td {
41183
+ /* Logically add space below the select when opened to allow scrolling the last row higher */
41184
+ .choices.is-open {
41185
+ margin-bottom: 800px !important;
41159
41186
  overflow: visible !important;
41160
41187
  }
41188
+
41189
+ /* Prevent dropdown from being layered behind table headers or other rows */
41190
+ .choices__list--dropdown {
41191
+ z-index: 10000 !important;
41192
+ overflow-y: auto !important;
41193
+ -webkit-overflow-scrolling: touch;
41194
+ }
41195
+
41196
+ /* Ensure the internal list doesn't have its own scrollbar/max-height that clips items */
41197
+ .choices__list--dropdown .choices__list {
41198
+ max-height: none !important;
41199
+ overflow: visible !important;
41200
+ padding-bottom: 20px !important;
41201
+ }
41202
+
41203
+ /* Add extra space for the last item to ensure it's not clipped on iOS */
41204
+ .choices__list--dropdown .choices__item:last-child {
41205
+ padding-bottom: 20px !important;
41206
+ }
41161
41207
  }</style>
41162
41208
 
41163
41209
  <script>
@@ -47063,16 +47109,9 @@ var Choices = /** @class */function () {
47063
47109
  requestAnimationFrame(function () {
47064
47110
  _this.dropdown.show();
47065
47111
  _this.containerOuter.open(_this.dropdown.distanceFromTopWindow);
47066
- if (!preventInputFocus && _this._canSearch) {
47067
- // On iOS, delay focus slightly to ensure the keyboard has time to reset/close from previous field
47068
- if (window.platform && window.platform.iosPlatform) {
47069
- setTimeout(() => {
47070
- _this.input.focus({ preventScroll: true });
47071
- }, 150);
47072
- } else {
47112
+ if (!preventInputFocus && _this._canSearch) {
47073
47113
  _this.input.focus({ preventScroll: true });
47074
47114
  }
47075
- }
47076
47115
  _this.passedElement.triggerEvent(constants_1.EVENTS.showDropdown, {});
47077
47116
  });
47078
47117
  return this;
@@ -54697,12 +54736,16 @@ class SmartSelectComponent extends SmartSelectField {
54697
54736
  if (this.component.multiple) {
54698
54737
  this.focusableElement = this.choices.input.element;
54699
54738
  } else {
54700
- this.focusableElement = this.choices.containerInner.element;
54739
+ // On iOS, focus the search input even for single select to ensure keyboard opens and centering logic works
54740
+ if (choicesOptions.searchEnabled && window.platform && window.platform.iosPlatform) {
54741
+ this.focusableElement = this.choices.input.element;
54742
+ } else {
54743
+ this.focusableElement = this.choices.containerInner.element;
54744
+ }
54701
54745
  this.choices.containerOuter.element.setAttribute('tabIndex', '-1');
54702
54746
  if (choicesOptions.searchEnabled) {
54703
54747
  // Proactively position on mousedown so it's ready before focus happens
54704
54748
  this.addEventListener(this.choices.containerOuter.element, 'mousedown', () => this.positionDropdown());
54705
- this.addEventListener(this.choices.containerOuter.element, 'focus', () => this.focusableElement.focus({ preventScroll: true }));
54706
54749
  }
54707
54750
  }
54708
54751
  this.addFocusBlurEvents(this.focusableElement);
@@ -54719,6 +54762,12 @@ class SmartSelectComponent extends SmartSelectField {
54719
54762
  }
54720
54763
  }
54721
54764
  if (window && this.choices && this.shouldPositionDropdown) {
54765
+ const wrapper = document.getElementById('formio-wrapper');
54766
+ if (wrapper) {
54767
+ this.addEventListener(wrapper, 'scroll', () => {
54768
+ this.positionDropdown(true);
54769
+ }, false, true);
54770
+ }
54722
54771
  this.addEventListener(window.document, 'scroll', () => {
54723
54772
  this.positionDropdown(true);
54724
54773
  }, false, true);
@@ -54877,11 +54926,8 @@ class SmartSelectComponent extends SmartSelectField {
54877
54926
 
54878
54927
  // Ensure select is visible above keyboard on iOS
54879
54928
  if (window.platform && window.platform.iosPlatform) {
54880
- setTimeout(() => {
54881
- if (this.focusableElement) {
54882
- this.focusableElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
54883
- }
54884
- }, 300);
54929
+ // We removed native scrollIntoView as it interferes with manual scroll control in focus.js
54930
+ // focus.js handles the centering now.
54885
54931
  }
54886
54932
 
54887
54933
  // Restore and lock scroll on iPad to prevent "flicker" shifts during keyboard toggles
@@ -54999,14 +55045,29 @@ class SmartSelectComponent extends SmartSelectField {
54999
55045
  const containerPosition = container.getBoundingClientRect();
55000
55046
  const isFlipped = container.classList.contains('is-flipped');
55001
55047
 
55002
- _.assign(dropdown.style, {
55003
- top: `${isFlipped ? containerPosition.top - dropdown.offsetHeight : containerPosition.top + containerPosition.height}px`,
55048
+ const vpH = window.visualViewport ? window.visualViewport.height : window.innerHeight;
55049
+ const top = isFlipped ? containerPosition.top - dropdown.offsetHeight : containerPosition.top + containerPosition.height;
55050
+
55051
+ const style = {
55004
55052
  left: `${containerPosition.left}px`,
55005
55053
  width: `${containerPosition.width}px`,
55006
55054
  position: 'fixed',
55007
55055
  bottom: 'unset',
55008
55056
  right: 'unset',
55009
- });
55057
+ top: `${top}px`,
55058
+ };
55059
+
55060
+ // On iOS, we limit the max-height of the fixed dropdown to stay within the viewport.
55061
+ // We also use a scroll listener on the wrapper (added in attach()) to ensure this fixed
55062
+ // element re-positions during scrolling, making it behave like an absolute element.
55063
+ if (window.platform && window.platform.iosPlatform) {
55064
+ // Limit max-height to available space but ensure at least 300px for a good scroll area.
55065
+ // We subtract 30px to leave a small gap at the bottom for better visibility.
55066
+ const availableHeight = isFlipped ? top - 20 : vpH - top - 30;
55067
+ style.maxHeight = `${Math.max(availableHeight, 300)}px`;
55068
+ }
55069
+
55070
+ _.assign(dropdown.style, style);
55010
55071
  }
55011
55072
 
55012
55073
  hasDataGridAncestor(comp) {
@@ -60626,7 +60687,7 @@ window.CommentOnBack = CommentOnBack;
60626
60687
 
60627
60688
 
60628
60689
  <div id="sticky-footer">
60629
- <div class="build-version">SDK v1.0.21</div>
60690
+ <div class="build-version">SDK v1.0.22</div>
60630
60691
  <div class="relative-position">
60631
60692
  <button id="unvired-more-btn" class="ui button primary dataGrid-addRow" onclick="toggleTooltip()">
60632
60693
  <i class="icon options"></i>
@@ -1489,12 +1489,16 @@ body {
1489
1489
  box-sizing: border-box;
1490
1490
  background-color: #f5f5f5;
1491
1491
  font-family: Arial, sans-serif;
1492
+ overflow: hidden; /* Prevent window from scrolling */
1492
1493
  }
1493
1494
 
1494
1495
  .formio-wrapper {
1495
1496
  border: 1px solid #ccc;
1496
1497
  background-color: #fff;
1497
1498
  border-radius: 8px;
1499
+ overflow-y: auto; /* Enable internal scrolling */
1500
+ -webkit-overflow-scrolling: touch;
1501
+ height: 100%; /* Fill space */
1498
1502
  }
1499
1503
 
1500
1504
  #progressContainer {
@@ -2060,15 +2064,34 @@ body {
2060
2064
  }
2061
2065
 
2062
2066
  /* Add buffer at bottom to allow last components to scroll above the keyboard */
2063
- #form-container {
2064
- padding-bottom: 500px !important;
2067
+ #formio-wrapper {
2068
+ padding-bottom: 800px !important;
2065
2069
  }
2066
2070
 
2067
- /* Ensure DataGrid rows allow dropdowns to overflow now that we disabled fixed positioning on iOS */
2068
- .formio-component-datagrid .ui.table tr,
2069
- .formio-component-datagrid .ui.table td {
2071
+ /* Logically add space below the select when opened to allow scrolling the last row higher */
2072
+ .choices.is-open {
2073
+ margin-bottom: 800px !important;
2070
2074
  overflow: visible !important;
2071
2075
  }
2076
+
2077
+ /* Prevent dropdown from being layered behind table headers or other rows */
2078
+ .choices__list--dropdown {
2079
+ z-index: 10000 !important;
2080
+ overflow-y: auto !important;
2081
+ -webkit-overflow-scrolling: touch;
2082
+ }
2083
+
2084
+ /* Ensure the internal list doesn't have its own scrollbar/max-height that clips items */
2085
+ .choices__list--dropdown .choices__list {
2086
+ max-height: none !important;
2087
+ overflow: visible !important;
2088
+ padding-bottom: 20px !important;
2089
+ }
2090
+
2091
+ /* Add extra space for the last item to ensure it's not clipped on iOS */
2092
+ .choices__list--dropdown .choices__item:last-child {
2093
+ padding-bottom: 20px !important;
2094
+ }
2072
2095
  }
2073
2096
 
2074
2097
  /* === STYLE_SEPARATOR === */
@@ -27899,7 +27922,7 @@ select.ui.dropdown {
27899
27922
  <div id="formio-cmt" style="margin-bottom: 20px;"></div>
27900
27923
  </div>
27901
27924
  <div id="sticky-footer">
27902
- <div class="build-version">SDK v1.0.21</div>
27925
+ <div class="build-version">SDK v1.0.22</div>
27903
27926
  <button class="ui button primary dataGrid-addRow" id="saveBtn" disabled="true" onclick="FormOnSave()">
27904
27927
  <i class="icon save large"></i>Save
27905
27928
  </button>
@@ -54315,16 +54338,9 @@ var Choices = /** @class */function () {
54315
54338
  requestAnimationFrame(function () {
54316
54339
  _this.dropdown.show();
54317
54340
  _this.containerOuter.open(_this.dropdown.distanceFromTopWindow);
54318
- if (!preventInputFocus && _this._canSearch) {
54319
- // On iOS, delay focus slightly to ensure the keyboard has time to reset/close from previous field
54320
- if (window.platform && window.platform.iosPlatform) {
54321
- setTimeout(() => {
54322
- _this.input.focus({ preventScroll: true });
54323
- }, 150);
54324
- } else {
54341
+ if (!preventInputFocus && _this._canSearch) {
54325
54342
  _this.input.focus({ preventScroll: true });
54326
54343
  }
54327
- }
54328
54344
  _this.passedElement.triggerEvent(constants_1.EVENTS.showDropdown, {});
54329
54345
  });
54330
54346
  return this;
@@ -61950,12 +61966,16 @@ class SmartSelectComponent extends SmartSelectField {
61950
61966
  if (this.component.multiple) {
61951
61967
  this.focusableElement = this.choices.input.element;
61952
61968
  } else {
61953
- this.focusableElement = this.choices.containerInner.element;
61969
+ // On iOS, focus the search input even for single select to ensure keyboard opens and centering logic works
61970
+ if (choicesOptions.searchEnabled && window.platform && window.platform.iosPlatform) {
61971
+ this.focusableElement = this.choices.input.element;
61972
+ } else {
61973
+ this.focusableElement = this.choices.containerInner.element;
61974
+ }
61954
61975
  this.choices.containerOuter.element.setAttribute('tabIndex', '-1');
61955
61976
  if (choicesOptions.searchEnabled) {
61956
61977
  // Proactively position on mousedown so it's ready before focus happens
61957
61978
  this.addEventListener(this.choices.containerOuter.element, 'mousedown', () => this.positionDropdown());
61958
- this.addEventListener(this.choices.containerOuter.element, 'focus', () => this.focusableElement.focus({ preventScroll: true }));
61959
61979
  }
61960
61980
  }
61961
61981
  this.addFocusBlurEvents(this.focusableElement);
@@ -61972,6 +61992,12 @@ class SmartSelectComponent extends SmartSelectField {
61972
61992
  }
61973
61993
  }
61974
61994
  if (window && this.choices && this.shouldPositionDropdown) {
61995
+ const wrapper = document.getElementById('formio-wrapper');
61996
+ if (wrapper) {
61997
+ this.addEventListener(wrapper, 'scroll', () => {
61998
+ this.positionDropdown(true);
61999
+ }, false, true);
62000
+ }
61975
62001
  this.addEventListener(window.document, 'scroll', () => {
61976
62002
  this.positionDropdown(true);
61977
62003
  }, false, true);
@@ -62130,11 +62156,8 @@ class SmartSelectComponent extends SmartSelectField {
62130
62156
 
62131
62157
  // Ensure select is visible above keyboard on iOS
62132
62158
  if (window.platform && window.platform.iosPlatform) {
62133
- setTimeout(() => {
62134
- if (this.focusableElement) {
62135
- this.focusableElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
62136
- }
62137
- }, 300);
62159
+ // We removed native scrollIntoView as it interferes with manual scroll control in focus.js
62160
+ // focus.js handles the centering now.
62138
62161
  }
62139
62162
 
62140
62163
  // Restore and lock scroll on iPad to prevent "flicker" shifts during keyboard toggles
@@ -62252,14 +62275,29 @@ class SmartSelectComponent extends SmartSelectField {
62252
62275
  const containerPosition = container.getBoundingClientRect();
62253
62276
  const isFlipped = container.classList.contains('is-flipped');
62254
62277
 
62255
- _.assign(dropdown.style, {
62256
- top: \`\${isFlipped ? containerPosition.top - dropdown.offsetHeight : containerPosition.top + containerPosition.height}px\`,
62278
+ const vpH = window.visualViewport ? window.visualViewport.height : window.innerHeight;
62279
+ const top = isFlipped ? containerPosition.top - dropdown.offsetHeight : containerPosition.top + containerPosition.height;
62280
+
62281
+ const style = {
62257
62282
  left: \`\${containerPosition.left}px\`,
62258
62283
  width: \`\${containerPosition.width}px\`,
62259
62284
  position: 'fixed',
62260
62285
  bottom: 'unset',
62261
62286
  right: 'unset',
62262
- });
62287
+ top: \`\${top}px\`,
62288
+ };
62289
+
62290
+ // On iOS, we limit the max-height of the fixed dropdown to stay within the viewport.
62291
+ // We also use a scroll listener on the wrapper (added in attach()) to ensure this fixed
62292
+ // element re-positions during scrolling, making it behave like an absolute element.
62293
+ if (window.platform && window.platform.iosPlatform) {
62294
+ // Limit max-height to available space but ensure at least 300px for a good scroll area.
62295
+ // We subtract 30px to leave a small gap at the bottom for better visibility.
62296
+ const availableHeight = isFlipped ? top - 20 : vpH - top - 30;
62297
+ style.maxHeight = \`\${Math.max(availableHeight, 300)}px\`;
62298
+ }
62299
+
62300
+ _.assign(dropdown.style, style);
62263
62301
  }
62264
62302
 
62265
62303
  hasDataGridAncestor(comp) {
@@ -66348,6 +66386,161 @@ window.showDynamicModal = showDynamicModal;
66348
66386
 
66349
66387
  // === SCRIPT_SEPARATOR ===
66350
66388
 
66389
+ (function(){
66390
+ let isKeyboardOpen = false;
66391
+ let activeElement = null;
66392
+ const INITIAL_WINDOW_HEIGHT = window.innerHeight;
66393
+
66394
+ console.log("[Focus] Script initialized. Initial Height:", INITIAL_WINDOW_HEIGHT);
66395
+
66396
+ // SOFT LOCK
66397
+ window.addEventListener('scroll', () => {
66398
+ if (isKeyboardOpen && Math.abs(window.scrollY) > 20) {
66399
+ window.scrollTo(0, 0);
66400
+ }
66401
+ }, { passive: true });
66402
+
66403
+ function updateFooterVisibility(keyboardOpen) {
66404
+ // Only hide the footer on iOS to make room for the software keyboard.
66405
+ // On Windows/Desktop, we should keep the footer visible at all times.
66406
+ const isIOS = (window.platform && window.platform.iosPlatform);
66407
+ if (!isIOS) return;
66408
+
66409
+ const footer = document.getElementById('sticky-footer');
66410
+ if (!footer) return;
66411
+
66412
+ if (keyboardOpen) {
66413
+ footer.style.display = 'none';
66414
+ } else {
66415
+ footer.style.display = 'flex';
66416
+ }
66417
+ }
66418
+
66419
+ // Track focused input
66420
+ document.addEventListener('focusin', (e) => {
66421
+ const el = e.target;
66422
+ if (!el || !['INPUT', 'TEXTAREA', 'SELECT'].includes(el.tagName)) return;
66423
+
66424
+ console.log("[Focus] \u{1F3AF} focusin on:", el.name || el.id || el.tagName);
66425
+ updateFooterVisibility(true);
66426
+
66427
+ activeElement = el;
66428
+
66429
+ // Special handling for Choices.js: if an input inside choices is focused,
66430
+ // we want to ensure the whole container is considered the active element for centering
66431
+ if (el.classList.contains('choices__input')) {
66432
+ const container = el.closest('.choices');
66433
+ if (container) {
66434
+ activeElement = container;
66435
+ console.log("[Focus] \u{1F4E6} Choices.js detected, centering container.");
66436
+ }
66437
+ }
66438
+
66439
+ if (isKeyboardOpen) {
66440
+ console.log("[Focus] \u26A1 Already open, centering now.");
66441
+ centerActiveElement();
66442
+ }
66443
+ });
66444
+
66445
+ // Restore footer when focus is lost and keyboard is not open
66446
+ document.addEventListener('focusout', (e) => {
66447
+ setTimeout(() => {
66448
+ const currentlyFocused = document.activeElement;
66449
+ const isInputFocused = currentlyFocused && ['INPUT', 'TEXTAREA', 'SELECT'].includes(currentlyFocused.tagName);
66450
+
66451
+ if (!isInputFocused && !isKeyboardOpen) {
66452
+ console.log("[Focus] \u{1F6AA} focusout and keyboard closed, restoring footer.");
66453
+ updateFooterVisibility(false);
66454
+ }
66455
+ }, 200);
66456
+ });
66457
+
66458
+ // Detect keyboard open/close
66459
+ if (window.visualViewport) {
66460
+ window.visualViewport.addEventListener('resize', () => {
66461
+ const isIOS = (window.platform && window.platform.iosPlatform);
66462
+ if (!isIOS) return;
66463
+
66464
+ const viewport = window.visualViewport;
66465
+ const vpH = viewport.height;
66466
+ const isNowOpen = vpH < INITIAL_WINDOW_HEIGHT - 100;
66467
+
66468
+ if (isNowOpen) {
66469
+ const wasAlreadyOpen = isKeyboardOpen;
66470
+ isKeyboardOpen = true;
66471
+ updateFooterVisibility(true);
66472
+
66473
+ if (activeElement && !wasAlreadyOpen) {
66474
+ console.log("[Focus] \u{1F3A2} Keyboard sliding. Following...");
66475
+ startFollowingElement();
66476
+ }
66477
+ } else {
66478
+ isKeyboardOpen = false;
66479
+ updateFooterVisibility(false);
66480
+ window.scrollTo(0, 0);
66481
+ }
66482
+ });
66483
+ }
66484
+
66485
+ // Manually follow the element for a short duration to ensure it stays centered during animation
66486
+ function startFollowingElement() {
66487
+ let start = Date.now();
66488
+ const duration = 400; // Follow for 400ms (typical iOS keyboard duration)
66489
+
66490
+ function step() {
66491
+ centerActiveElement();
66492
+ if (Date.now() - start < duration) {
66493
+ requestAnimationFrame(step);
66494
+ }
66495
+ }
66496
+ requestAnimationFrame(step);
66497
+ }
66498
+
66499
+ function centerActiveElement() {
66500
+ if (!activeElement) return;
66501
+ const wrapper = document.getElementById('formio-wrapper');
66502
+ if (!wrapper) return;
66503
+
66504
+ const vpH = window.visualViewport ? window.visualViewport.height : window.innerHeight;
66505
+
66506
+ // Calculate absolute offset of element relative to wrapper
66507
+ let offsetTop = 0;
66508
+ let curr = activeElement;
66509
+ while (curr && curr !== wrapper && curr !== null) {
66510
+ offsetTop += curr.offsetTop;
66511
+ curr = curr.offsetParent;
66512
+ }
66513
+
66514
+ // For Choices.js, if the dropdown is open, we might want to adjust centering
66515
+ // to ensure the dropdown is visible. However, centering the container is usually best.
66516
+ const elementHeight = activeElement.offsetHeight;
66517
+
66518
+ // targetScroll = (distance from top) - (half of available space) + (half of element height)
66519
+ const targetScroll = offsetTop - (vpH / 2) + (elementHeight / 2);
66520
+
66521
+ // Apply instantly but accurately
66522
+ if (Math.abs(wrapper.scrollTop - targetScroll) > 5) {
66523
+ wrapper.scrollTop = targetScroll;
66524
+ }
66525
+ }
66526
+
66527
+ // Listen for Choices.js events to re-center when dropdown opens/closes
66528
+ document.addEventListener('showDropdown', () => {
66529
+ if (isKeyboardOpen) {
66530
+ console.log("[Focus] \u{1F53D} showDropdown detected, re-centering.");
66531
+ setTimeout(centerActiveElement, 100);
66532
+ }
66533
+ }, true);
66534
+
66535
+ document.addEventListener('hideDropdown', () => {
66536
+ console.log("[Focus] \u{1F53C} hideDropdown detected, resetting view.");
66537
+ // When dropdown hides, we might want to center the closed select again
66538
+ setTimeout(centerActiveElement, 100);
66539
+ }, true);
66540
+ })();
66541
+
66542
+ // === SCRIPT_SEPARATOR ===
66543
+
66351
66544
  (function(){
66352
66545
  var formObj = null;
66353
66546
  var initialFormData = null;
@@ -66689,6 +66882,7 @@ async function loadRNform(
66689
66882
  // Determine form configuration based on mode
66690
66883
  let formConfig = {
66691
66884
  noAlerts: true,
66885
+ scroll: false, // DISABLE Formio internal scrolling
66692
66886
  language: window.FORMIO_LANGUAGE || "en",
66693
66887
  i18n: window.FORMIO_I18N,
66694
66888
  };
@@ -68106,7 +68300,7 @@ window.deleteAppDocument = async function(id) {
68106
68300
  window.getAllDocuments = getAllDocuments;
68107
68301
  window.hasDocuments = hasDocuments;
68108
68302
  function getBuildVersion() {
68109
- return "1.0.21";
68303
+ return "1.0.22";
68110
68304
  }
68111
68305
  export {
68112
68306
  getBuildVersion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unvired/turboforms-embed-sdk",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "Reusable vanilla JS form library that works with React, Angular, Ionic, etc.",
5
5
  "main": "dist/unvired-forms-sdk.js",
6
6
  "types": "dist/index.d.ts",