@unvired/turboforms-embed-sdk 1.0.19 → 1.0.21

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.
@@ -1077,6 +1077,22 @@ body {
1077
1077
  margin-right: auto;
1078
1078
 
1079
1079
  }
1080
+
1081
+ /* Prevent iOS zoom/jump on Select search input by ensuring font-size is 16px */
1082
+ .ui.form .choices[data-type*='select-one'] input.choices__input {
1083
+ font-size: 16px !important;
1084
+ }
1085
+
1086
+ /* Add buffer at bottom to allow last components to scroll above the keyboard */
1087
+ #form-container {
1088
+ padding-bottom: 500px !important;
1089
+ }
1090
+
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 {
1094
+ overflow: visible !important;
1095
+ }
1080
1096
  }</style>
1081
1097
  <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}
1082
1098
  .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}
@@ -41126,6 +41142,22 @@ body {
41126
41142
  margin-right: auto;
41127
41143
 
41128
41144
  }
41145
+
41146
+ /* Prevent iOS zoom/jump on Select search input by ensuring font-size is 16px */
41147
+ .ui.form .choices[data-type*='select-one'] input.choices__input {
41148
+ font-size: 16px !important;
41149
+ }
41150
+
41151
+ /* Add buffer at bottom to allow last components to scroll above the keyboard */
41152
+ #form-container {
41153
+ padding-bottom: 500px !important;
41154
+ }
41155
+
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 {
41159
+ overflow: visible !important;
41160
+ }
41129
41161
  }</style>
41130
41162
 
41131
41163
  <script>
@@ -47032,7 +47064,14 @@ var Choices = /** @class */function () {
47032
47064
  _this.dropdown.show();
47033
47065
  _this.containerOuter.open(_this.dropdown.distanceFromTopWindow);
47034
47066
  if (!preventInputFocus && _this._canSearch) {
47035
- _this.input.focus();
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 {
47073
+ _this.input.focus({ preventScroll: true });
47074
+ }
47036
47075
  }
47037
47076
  _this.passedElement.triggerEvent(constants_1.EVENTS.showDropdown, {});
47038
47077
  });
@@ -47515,7 +47554,7 @@ var Choices = /** @class */function () {
47515
47554
  });
47516
47555
  // Focus input as without focus, a user cannot do anything with a
47517
47556
  // highlighted item
47518
- this.input.focus();
47557
+ this.input.focus({ preventScroll: true });
47519
47558
  };
47520
47559
  Choices.prototype._handleChoiceAction = function (activeItems, element) {
47521
47560
  if (!activeItems || !element) {
@@ -47944,7 +47983,7 @@ var Choices = /** @class */function () {
47944
47983
  var containerWasExactTarget = target === this.containerOuter.element || target === this.containerInner.element;
47945
47984
  if (containerWasExactTarget) {
47946
47985
  if (this._isTextElement) {
47947
- this.input.focus();
47986
+ this.input.focus({ preventScroll: true });
47948
47987
  } else if (this._isSelectMultipleElement) {
47949
47988
  this.showDropdown();
47950
47989
  }
@@ -48004,11 +48043,12 @@ var Choices = /** @class */function () {
48004
48043
  if (!this.dropdown.isActive && !this.containerOuter.isDisabled) {
48005
48044
  if (this._isTextElement) {
48006
48045
  if (document.activeElement !== this.input.element) {
48007
- this.input.focus();
48046
+ this.input.focus({ preventScroll: true });
48008
48047
  }
48009
48048
  } else {
48010
48049
  this.showDropdown();
48011
- this.containerOuter.focus();
48050
+ // Always focus container to trigger keyboard close if switching fields
48051
+ this.containerOuter.focus({ preventScroll: true });
48012
48052
  }
48013
48053
  } else if (this._isSelectOneElement && target !== this.input.element && !this.dropdown.element.contains(target)) {
48014
48054
  this.hideDropdown();
@@ -48087,7 +48127,7 @@ var Choices = /** @class */function () {
48087
48127
  // closes the dropdown. To stop this, we refocus our input
48088
48128
  // if we know we are on IE *and* are scrolling.
48089
48129
  this._isScrollingOnIe = false;
48090
- this.input.element.focus();
48130
+ this.input.element.focus({ preventScroll: true });
48091
48131
  }
48092
48132
  };
48093
48133
  Choices.prototype._onFormReset = function () {
@@ -54570,7 +54610,7 @@ class SmartSelectComponent extends SmartSelectField {
54570
54610
  noChoicesText: this.t('No choices to choose from'),
54571
54611
  searchPlaceholderValue: this.t('Type to search'),
54572
54612
  shouldSort: false,
54573
- position: (this.component.dropdown || 'auto'),
54613
+ position: this.component.dropdown ? this.component.dropdown : (window.platform && window.platform.iosPlatform ? "bottom" : "auto"),
54574
54614
  searchEnabled: useSearch,
54575
54615
  searchChoices: !this.component.searchField,
54576
54616
  searchFields: _.get(this, 'component.searchFields', ['label']),
@@ -54660,7 +54700,9 @@ class SmartSelectComponent extends SmartSelectField {
54660
54700
  this.focusableElement = this.choices.containerInner.element;
54661
54701
  this.choices.containerOuter.element.setAttribute('tabIndex', '-1');
54662
54702
  if (choicesOptions.searchEnabled) {
54663
- this.addEventListener(this.choices.containerOuter.element, 'focus', () => this.focusableElement.focus());
54703
+ // Proactively position on mousedown so it's ready before focus happens
54704
+ this.addEventListener(this.choices.containerOuter.element, 'mousedown', () => this.positionDropdown());
54705
+ this.addEventListener(this.choices.containerOuter.element, 'focus', () => this.focusableElement.focus({ preventScroll: true }));
54664
54706
  }
54665
54707
  }
54666
54708
  this.addFocusBlurEvents(this.focusableElement);
@@ -54787,26 +54829,100 @@ class SmartSelectComponent extends SmartSelectField {
54787
54829
  dataObj.skip = this.defaultDownloadedResources.length + this.component.limit;
54788
54830
  this.setItems(this.defaultDownloadedResources);
54789
54831
  }
54832
+ });
54833
+
54834
+ this.addEventListener(input, 'hideDropdown', () => {
54835
+ const container = document.getElementById('form-container');
54836
+ const currentScroll = container ? container.scrollTop : 0;
54790
54837
 
54791
54838
  if (this.choices && this.choices.input && this.choices.input.element) {
54792
54839
  this.choices.input.element.value = '';
54793
54840
  }
54794
- this.updateItems(null, true);
54841
+ // Only update items if it's really necessary (e.g. clearing masterdata search)
54842
+ // Redrawing on hide often causes scroll jumps on iOS
54843
+ if (this.component.dataSrc === 'masterdata') {
54844
+ this.updateItems(null, true);
54845
+ }
54846
+
54847
+ // Restore scroll after hide/blur jump
54848
+ if (container && currentScroll > 0) {
54849
+ // Restore if jumping to top or significant shift, but ignore if we recently clicked another select
54850
+ requestAnimationFrame(() => {
54851
+ if (container.scrollTop === 0 && !window._unv_selecting) {
54852
+ container.scrollTop = currentScroll;
54853
+ }
54854
+ });
54855
+ }
54795
54856
  });
54796
54857
  }
54797
54858
 
54859
+ this.addEventListener(input, 'mousedown', () => {
54860
+ window._unv_selecting = true;
54861
+ // Force full keyboard cycle on iOS to prevent layout "ghosting" from previous field
54862
+ if (window.platform && window.platform.iosPlatform && document.activeElement && (document.activeElement.tagName === 'INPUT' || document.activeElement.classList.contains('choices__input'))) {
54863
+ document.activeElement.blur();
54864
+ }
54865
+ setTimeout(() => { window._unv_selecting = false; }, 500);
54866
+ this.positionDropdown();
54867
+ });
54868
+
54798
54869
  this.addEventListener(input, 'showDropdown', () => {
54799
- this.update();
54870
+ const container = document.getElementById('form-container');
54871
+ const currentScroll = container ? container.scrollTop : 0;
54872
+
54873
+ if (!this.activated) {
54874
+ this.update();
54875
+ }
54800
54876
  this.positionDropdown();
54877
+
54878
+ // Ensure select is visible above keyboard on iOS
54879
+ if (window.platform && window.platform.iosPlatform) {
54880
+ setTimeout(() => {
54881
+ if (this.focusableElement) {
54882
+ this.focusableElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
54883
+ }
54884
+ }, 300);
54885
+ }
54886
+
54887
+ // Restore and lock scroll on iPad to prevent "flicker" shifts during keyboard toggles
54888
+ if (container && currentScroll > 0 && window.platform && window.platform.iosPlatform) {
54889
+ // Immediate restoration
54890
+ container.scrollTop = currentScroll;
54891
+
54892
+ // Follow-up restoration for async layout shifts
54893
+ requestAnimationFrame(() => {
54894
+ container.scrollTop = currentScroll;
54895
+ });
54896
+
54897
+ // Periodical restoration during the typical keyboard transition time (300ms)
54898
+ let count = 0;
54899
+ const interval = setInterval(() => {
54900
+ if (container.scrollTop !== currentScroll) {
54901
+ container.scrollTop = currentScroll;
54902
+ }
54903
+ if (++count > 10) clearInterval(interval);
54904
+ }, 30);
54905
+ } else if (container && currentScroll > 0) {
54906
+ // Standard restoration for other platforms
54907
+ requestAnimationFrame(() => {
54908
+ if (container.scrollTop === 0) {
54909
+ container.scrollTop = currentScroll;
54910
+ }
54911
+ });
54912
+ }
54913
+
54801
54914
  if ((this.component.dataSrc === 'masterdata') && this.component.masterdata) {
54802
54915
  this.component.dropdownOpened = true;
54803
- if (this.defaultDownloadedResources && ((this.defaultDownloadedResources.length == 1) || ((this.defaultDownloadedResources.length < this.component.limit) && this.component.multiple))) {
54804
- this.defaultDownloadedResources = [];
54805
- this.setMasterdata();
54806
- } else if (this.defaultDownloadedResources && this.defaultDownloadedResources.length > 0) {
54807
- this.setItems(this.defaultDownloadedResources);
54808
- } else {
54809
- this.setMasterdata();
54916
+ // Avoid redundant setItems if we already have the data
54917
+ if (!this.defaultDownloadedResources || this.defaultDownloadedResources.length === 0) {
54918
+ if (this.defaultDownloadedResources && ((this.defaultDownloadedResources.length == 1) || ((this.defaultDownloadedResources.length < this.component.limit) && this.component.multiple))) {
54919
+ this.defaultDownloadedResources = [];
54920
+ this.setMasterdata();
54921
+ } else if (this.defaultDownloadedResources && this.defaultDownloadedResources.length > 0) {
54922
+ this.setItems(this.defaultDownloadedResources);
54923
+ } else {
54924
+ this.setMasterdata();
54925
+ }
54810
54926
  }
54811
54927
  }
54812
54928
  });
@@ -54858,6 +54974,12 @@ class SmartSelectComponent extends SmartSelectField {
54858
54974
  this.disabled = this.shouldDisabled;
54859
54975
  this.updateItems();
54860
54976
  this.triggerUpdate();
54977
+
54978
+ // If not lazy loading, mark as activated to avoid redraw on first open
54979
+ if (!this.component.lazyLoad) {
54980
+ this.activated = true;
54981
+ }
54982
+
54861
54983
  return superAttach;
54862
54984
  }
54863
54985
  setDropdownPosition() {
@@ -56779,7 +56901,12 @@ class ColumnsComponent extends ColumnsNestedComponent {
56779
56901
  const redraw = this.justify();
56780
56902
 
56781
56903
  if (redraw) {
56782
- this.redraw();
56904
+ // Avoid redrawing columns if an input is focused to prevent scroll/focus loss
56905
+ const activeEl = document.activeElement;
56906
+ const isFocusable = activeEl && (activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA' || activeEl.classList.contains('choices__input'));
56907
+ if (!isFocusable) {
56908
+ this.redraw();
56909
+ }
56783
56910
  }
56784
56911
  }
56785
56912
 
@@ -60499,7 +60626,7 @@ window.CommentOnBack = CommentOnBack;
60499
60626
 
60500
60627
 
60501
60628
  <div id="sticky-footer">
60502
- <div class="build-version">v1.0.19</div>
60629
+ <div class="build-version">SDK v1.0.21</div>
60503
60630
  <div class="relative-position">
60504
60631
  <button id="unvired-more-btn" class="ui button primary dataGrid-addRow" onclick="toggleTooltip()">
60505
60632
  <i class="icon options"></i>
@@ -2053,6 +2053,22 @@ body {
2053
2053
  margin-right: auto;
2054
2054
 
2055
2055
  }
2056
+
2057
+ /* Prevent iOS zoom/jump on Select search input by ensuring font-size is 16px */
2058
+ .ui.form .choices[data-type*='select-one'] input.choices__input {
2059
+ font-size: 16px !important;
2060
+ }
2061
+
2062
+ /* Add buffer at bottom to allow last components to scroll above the keyboard */
2063
+ #form-container {
2064
+ padding-bottom: 500px !important;
2065
+ }
2066
+
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 {
2070
+ overflow: visible !important;
2071
+ }
2056
2072
  }
2057
2073
 
2058
2074
  /* === STYLE_SEPARATOR === */
@@ -27769,7 +27785,7 @@ select.ui.dropdown {
27769
27785
  }
27770
27786
  .build-version {
27771
27787
  margin-right: auto;
27772
- font-size: 11px;
27788
+ font-size: 10px;
27773
27789
  color: #888;
27774
27790
  padding-left: 10px;
27775
27791
  font-family: inherit;
@@ -27883,7 +27899,7 @@ select.ui.dropdown {
27883
27899
  <div id="formio-cmt" style="margin-bottom: 20px;"></div>
27884
27900
  </div>
27885
27901
  <div id="sticky-footer">
27886
- <div class="build-version">v1.0.19</div>
27902
+ <div class="build-version">SDK v1.0.21</div>
27887
27903
  <button class="ui button primary dataGrid-addRow" id="saveBtn" disabled="true" onclick="FormOnSave()">
27888
27904
  <i class="icon save large"></i>Save
27889
27905
  </button>
@@ -54300,7 +54316,14 @@ var Choices = /** @class */function () {
54300
54316
  _this.dropdown.show();
54301
54317
  _this.containerOuter.open(_this.dropdown.distanceFromTopWindow);
54302
54318
  if (!preventInputFocus && _this._canSearch) {
54303
- _this.input.focus();
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 {
54325
+ _this.input.focus({ preventScroll: true });
54326
+ }
54304
54327
  }
54305
54328
  _this.passedElement.triggerEvent(constants_1.EVENTS.showDropdown, {});
54306
54329
  });
@@ -54783,7 +54806,7 @@ var Choices = /** @class */function () {
54783
54806
  });
54784
54807
  // Focus input as without focus, a user cannot do anything with a
54785
54808
  // highlighted item
54786
- this.input.focus();
54809
+ this.input.focus({ preventScroll: true });
54787
54810
  };
54788
54811
  Choices.prototype._handleChoiceAction = function (activeItems, element) {
54789
54812
  if (!activeItems || !element) {
@@ -55212,7 +55235,7 @@ var Choices = /** @class */function () {
55212
55235
  var containerWasExactTarget = target === this.containerOuter.element || target === this.containerInner.element;
55213
55236
  if (containerWasExactTarget) {
55214
55237
  if (this._isTextElement) {
55215
- this.input.focus();
55238
+ this.input.focus({ preventScroll: true });
55216
55239
  } else if (this._isSelectMultipleElement) {
55217
55240
  this.showDropdown();
55218
55241
  }
@@ -55272,11 +55295,12 @@ var Choices = /** @class */function () {
55272
55295
  if (!this.dropdown.isActive && !this.containerOuter.isDisabled) {
55273
55296
  if (this._isTextElement) {
55274
55297
  if (document.activeElement !== this.input.element) {
55275
- this.input.focus();
55298
+ this.input.focus({ preventScroll: true });
55276
55299
  }
55277
55300
  } else {
55278
55301
  this.showDropdown();
55279
- this.containerOuter.focus();
55302
+ // Always focus container to trigger keyboard close if switching fields
55303
+ this.containerOuter.focus({ preventScroll: true });
55280
55304
  }
55281
55305
  } else if (this._isSelectOneElement && target !== this.input.element && !this.dropdown.element.contains(target)) {
55282
55306
  this.hideDropdown();
@@ -55355,7 +55379,7 @@ var Choices = /** @class */function () {
55355
55379
  // closes the dropdown. To stop this, we refocus our input
55356
55380
  // if we know we are on IE *and* are scrolling.
55357
55381
  this._isScrollingOnIe = false;
55358
- this.input.element.focus();
55382
+ this.input.element.focus({ preventScroll: true });
55359
55383
  }
55360
55384
  };
55361
55385
  Choices.prototype._onFormReset = function () {
@@ -61839,7 +61863,7 @@ class SmartSelectComponent extends SmartSelectField {
61839
61863
  noChoicesText: this.t('No choices to choose from'),
61840
61864
  searchPlaceholderValue: this.t('Type to search'),
61841
61865
  shouldSort: false,
61842
- position: (this.component.dropdown || 'auto'),
61866
+ position: this.component.dropdown ? this.component.dropdown : (window.platform && window.platform.iosPlatform ? "bottom" : "auto"),
61843
61867
  searchEnabled: useSearch,
61844
61868
  searchChoices: !this.component.searchField,
61845
61869
  searchFields: _.get(this, 'component.searchFields', ['label']),
@@ -61929,7 +61953,9 @@ class SmartSelectComponent extends SmartSelectField {
61929
61953
  this.focusableElement = this.choices.containerInner.element;
61930
61954
  this.choices.containerOuter.element.setAttribute('tabIndex', '-1');
61931
61955
  if (choicesOptions.searchEnabled) {
61932
- this.addEventListener(this.choices.containerOuter.element, 'focus', () => this.focusableElement.focus());
61956
+ // Proactively position on mousedown so it's ready before focus happens
61957
+ this.addEventListener(this.choices.containerOuter.element, 'mousedown', () => this.positionDropdown());
61958
+ this.addEventListener(this.choices.containerOuter.element, 'focus', () => this.focusableElement.focus({ preventScroll: true }));
61933
61959
  }
61934
61960
  }
61935
61961
  this.addFocusBlurEvents(this.focusableElement);
@@ -62056,26 +62082,100 @@ class SmartSelectComponent extends SmartSelectField {
62056
62082
  dataObj.skip = this.defaultDownloadedResources.length + this.component.limit;
62057
62083
  this.setItems(this.defaultDownloadedResources);
62058
62084
  }
62085
+ });
62086
+
62087
+ this.addEventListener(input, 'hideDropdown', () => {
62088
+ const container = document.getElementById('form-container');
62089
+ const currentScroll = container ? container.scrollTop : 0;
62059
62090
 
62060
62091
  if (this.choices && this.choices.input && this.choices.input.element) {
62061
62092
  this.choices.input.element.value = '';
62062
62093
  }
62063
- this.updateItems(null, true);
62094
+ // Only update items if it's really necessary (e.g. clearing masterdata search)
62095
+ // Redrawing on hide often causes scroll jumps on iOS
62096
+ if (this.component.dataSrc === 'masterdata') {
62097
+ this.updateItems(null, true);
62098
+ }
62099
+
62100
+ // Restore scroll after hide/blur jump
62101
+ if (container && currentScroll > 0) {
62102
+ // Restore if jumping to top or significant shift, but ignore if we recently clicked another select
62103
+ requestAnimationFrame(() => {
62104
+ if (container.scrollTop === 0 && !window._unv_selecting) {
62105
+ container.scrollTop = currentScroll;
62106
+ }
62107
+ });
62108
+ }
62064
62109
  });
62065
62110
  }
62066
62111
 
62112
+ this.addEventListener(input, 'mousedown', () => {
62113
+ window._unv_selecting = true;
62114
+ // Force full keyboard cycle on iOS to prevent layout "ghosting" from previous field
62115
+ if (window.platform && window.platform.iosPlatform && document.activeElement && (document.activeElement.tagName === 'INPUT' || document.activeElement.classList.contains('choices__input'))) {
62116
+ document.activeElement.blur();
62117
+ }
62118
+ setTimeout(() => { window._unv_selecting = false; }, 500);
62119
+ this.positionDropdown();
62120
+ });
62121
+
62067
62122
  this.addEventListener(input, 'showDropdown', () => {
62068
- this.update();
62123
+ const container = document.getElementById('form-container');
62124
+ const currentScroll = container ? container.scrollTop : 0;
62125
+
62126
+ if (!this.activated) {
62127
+ this.update();
62128
+ }
62069
62129
  this.positionDropdown();
62130
+
62131
+ // Ensure select is visible above keyboard on iOS
62132
+ if (window.platform && window.platform.iosPlatform) {
62133
+ setTimeout(() => {
62134
+ if (this.focusableElement) {
62135
+ this.focusableElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
62136
+ }
62137
+ }, 300);
62138
+ }
62139
+
62140
+ // Restore and lock scroll on iPad to prevent "flicker" shifts during keyboard toggles
62141
+ if (container && currentScroll > 0 && window.platform && window.platform.iosPlatform) {
62142
+ // Immediate restoration
62143
+ container.scrollTop = currentScroll;
62144
+
62145
+ // Follow-up restoration for async layout shifts
62146
+ requestAnimationFrame(() => {
62147
+ container.scrollTop = currentScroll;
62148
+ });
62149
+
62150
+ // Periodical restoration during the typical keyboard transition time (300ms)
62151
+ let count = 0;
62152
+ const interval = setInterval(() => {
62153
+ if (container.scrollTop !== currentScroll) {
62154
+ container.scrollTop = currentScroll;
62155
+ }
62156
+ if (++count > 10) clearInterval(interval);
62157
+ }, 30);
62158
+ } else if (container && currentScroll > 0) {
62159
+ // Standard restoration for other platforms
62160
+ requestAnimationFrame(() => {
62161
+ if (container.scrollTop === 0) {
62162
+ container.scrollTop = currentScroll;
62163
+ }
62164
+ });
62165
+ }
62166
+
62070
62167
  if ((this.component.dataSrc === 'masterdata') && this.component.masterdata) {
62071
62168
  this.component.dropdownOpened = true;
62072
- if (this.defaultDownloadedResources && ((this.defaultDownloadedResources.length == 1) || ((this.defaultDownloadedResources.length < this.component.limit) && this.component.multiple))) {
62073
- this.defaultDownloadedResources = [];
62074
- this.setMasterdata();
62075
- } else if (this.defaultDownloadedResources && this.defaultDownloadedResources.length > 0) {
62076
- this.setItems(this.defaultDownloadedResources);
62077
- } else {
62078
- this.setMasterdata();
62169
+ // Avoid redundant setItems if we already have the data
62170
+ if (!this.defaultDownloadedResources || this.defaultDownloadedResources.length === 0) {
62171
+ if (this.defaultDownloadedResources && ((this.defaultDownloadedResources.length == 1) || ((this.defaultDownloadedResources.length < this.component.limit) && this.component.multiple))) {
62172
+ this.defaultDownloadedResources = [];
62173
+ this.setMasterdata();
62174
+ } else if (this.defaultDownloadedResources && this.defaultDownloadedResources.length > 0) {
62175
+ this.setItems(this.defaultDownloadedResources);
62176
+ } else {
62177
+ this.setMasterdata();
62178
+ }
62079
62179
  }
62080
62180
  }
62081
62181
  });
@@ -62127,6 +62227,12 @@ class SmartSelectComponent extends SmartSelectField {
62127
62227
  this.disabled = this.shouldDisabled;
62128
62228
  this.updateItems();
62129
62229
  this.triggerUpdate();
62230
+
62231
+ // If not lazy loading, mark as activated to avoid redraw on first open
62232
+ if (!this.component.lazyLoad) {
62233
+ this.activated = true;
62234
+ }
62235
+
62130
62236
  return superAttach;
62131
62237
  }
62132
62238
  setDropdownPosition() {
@@ -64053,7 +64159,12 @@ class ColumnsComponent extends ColumnsNestedComponent {
64053
64159
  const redraw = this.justify();
64054
64160
 
64055
64161
  if (redraw) {
64056
- this.redraw();
64162
+ // Avoid redrawing columns if an input is focused to prevent scroll/focus loss
64163
+ const activeEl = document.activeElement;
64164
+ const isFocusable = activeEl && (activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA' || activeEl.classList.contains('choices__input'));
64165
+ if (!isFocusable) {
64166
+ this.redraw();
64167
+ }
64057
64168
  }
64058
64169
  }
64059
64170
 
@@ -66733,71 +66844,141 @@ async function loadRNform(
66733
66844
  setupOnChange(privateExternal, permission);
66734
66845
 
66735
66846
 
66736
- formObj.formReady.then(() => {
66737
- if (formPreviousData) {
66738
- formObj.submission = {
66739
- data:
66740
- typeof formPreviousData === "string"
66741
- ? JSON.parse(formPreviousData)
66742
- : formPreviousData,
66743
- };
66744
- }
66847
+ formObj.formReady
66848
+ .then(() => {
66849
+ console.log("[FormIO] \u{1F680} formReady.then() triggered");
66745
66850
 
66746
- // Calculate initial progress immediately
66747
- console.log("[FormIO] \u{1F4CA} Form ready \u2014 calculating initial progress...");
66748
- executeProgressCalculation(privateExternal, permission);
66851
+ if (formPreviousData) {
66852
+ console.log("[FormIO] \u{1F4E6} Previous form data found:", formPreviousData);
66749
66853
 
66750
- // Hide FormIO submit button component and wizard buttons in readOnly mode
66751
- if (mode == "readOnly") {
66752
- const submitComponents = document.querySelectorAll(
66753
- ".formio-component-submit"
66754
- );
66755
- submitComponents.forEach((component) => {
66756
- component.style.display = "none";
66757
- });
66854
+ formObj.submission = {
66855
+ data:
66856
+ typeof formPreviousData === "string"
66857
+ ? JSON.parse(formPreviousData)
66858
+ : formPreviousData,
66859
+ };
66758
66860
 
66759
- // Hide wizard navigation buttons
66760
- const wizardNextBtns = document.querySelectorAll(
66761
- ".btn-wizard-nav-next"
66762
- );
66763
- wizardNextBtns.forEach((btn) => {
66764
- btn.style.display = "none";
66861
+ console.log("[FormIO] \u2705 Previous submission data applied");
66862
+ } else {
66863
+ console.log("[FormIO] \u2139\uFE0F No previous form data found");
66864
+ }
66865
+
66866
+ // Calculate initial progress immediately
66867
+ console.log("[FormIO] \u{1F4CA} Calculating initial progress...");
66868
+ executeProgressCalculation(privateExternal, permission);
66869
+ console.log("[FormIO] \u2705 Progress calculation completed");
66870
+
66871
+ // Hide FormIO submit button component and wizard buttons in readOnly mode
66872
+ if (mode == "readOnly") {
66873
+ console.log("[FormIO] \u{1F512} ReadOnly mode detected");
66874
+
66875
+ const submitComponents = document.querySelectorAll(
66876
+ ".formio-component-submit"
66877
+ );
66878
+ console.log(
66879
+ \`[FormIO] Found \${submitComponents.length} submit components\`
66880
+ );
66881
+
66882
+ submitComponents.forEach((component, index) => {
66883
+ component.style.display = "none";
66884
+ console.log(\`[FormIO] Hidden submit component \${index + 1}\`);
66885
+ });
66886
+
66887
+ const wizardNextBtns = document.querySelectorAll(
66888
+ ".btn-wizard-nav-next"
66889
+ );
66890
+ console.log(
66891
+ \`[FormIO] Found \${wizardNextBtns.length} wizard next buttons\`
66892
+ );
66893
+
66894
+ wizardNextBtns.forEach((btn, index) => {
66895
+ btn.style.display = "none";
66896
+ console.log(\`[FormIO] Hidden wizard next button \${index + 1}\`);
66897
+ });
66898
+
66899
+ const wizardSubmitBtns = document.querySelectorAll(
66900
+ ".btn-wizard-nav-submit"
66901
+ );
66902
+ console.log(
66903
+ \`[FormIO] Found \${wizardSubmitBtns.length} wizard submit buttons\`
66904
+ );
66905
+
66906
+ wizardSubmitBtns.forEach((btn, index) => {
66907
+ btn.style.display = "none";
66908
+ console.log(\`[FormIO] Hidden wizard submit button \${index + 1}\`);
66909
+ });
66910
+
66911
+ const wizardPrevBtns = document.querySelectorAll(
66912
+ ".btn-wizard-nav-previous"
66913
+ );
66914
+ console.log(
66915
+ \`[FormIO] Found \${wizardPrevBtns.length} wizard previous buttons\`
66916
+ );
66917
+
66918
+ wizardPrevBtns.forEach((btn, index) => {
66919
+ btn.style.display = "none";
66920
+ console.log(\`[FormIO] Hidden wizard previous button \${index + 1}\`);
66921
+ });
66922
+ } else {
66923
+ console.log("[FormIO] \u{1F513} Editable mode detected");
66924
+ }
66925
+
66926
+ console.log("[FormIO] \u{1F4E1} Sending FORM_RENDER callback...");
66927
+ sendEventCallback({
66928
+ type: FORM_EVENTS.FORM_RENDER,
66929
+ message: "Success: Form rendered successfully.",
66930
+ data: {
66931
+ submissionData: formPreviousData,
66932
+ formTemplate: template,
66933
+ },
66765
66934
  });
66935
+ console.log("[FormIO] \u2705 FORM_RENDER callback sent");
66936
+
66937
+ // Hide SDK loader
66938
+ const sdkLoader = document.getElementById("sdk-form-loader");
66766
66939
 
66767
- const wizardSubmitBtns = document.querySelectorAll(
66768
- ".btn-wizard-nav-submit"
66940
+ if (sdkLoader) {
66941
+ console.log("[FormIO] \u23F3 Loader found, hiding...");
66942
+ sdkLoader.classList.add("hidden");
66943
+ console.log("[FormIO] \u2705 Loader hidden");
66944
+ } else {
66945
+ console.log("[FormIO] \u26A0\uFE0F Loader element not found");
66946
+ }
66947
+
66948
+ // Dispatch custom event
66949
+ console.log("[FormIO] \u{1F4E2} Dispatching FormRenderingComplete event...");
66950
+
66951
+ document.dispatchEvent(
66952
+ new CustomEvent("FormRenderingComplete", {
66953
+ detail: {
66954
+ timestamp: new Date().toISOString(),
66955
+ status: "success",
66956
+ },
66957
+ })
66769
66958
  );
66770
- wizardSubmitBtns.forEach((btn) => {
66771
- btn.style.display = "none";
66772
- });
66773
66959
 
66774
- const wizardPrevBtns = document.querySelectorAll(
66775
- ".btn-wizard-nav-previous"
66960
+ console.log(
66961
+ "[FormIO] \u2705 FormRenderingComplete event dispatched successfully"
66776
66962
  );
66777
- wizardPrevBtns.forEach((btn) => {
66778
- btn.style.display = "none";
66779
- });
66780
- }
66781
66963
 
66782
- sendEventCallback({
66783
- type: FORM_EVENTS.FORM_RENDER,
66784
- message: "Success: Form rendered successfully.",
66785
- data: { submissionData: formPreviousData, formTemplate: template },
66964
+ // File input
66965
+ const fileInput = document.querySelector('input[type="file"]');
66966
+
66967
+ if (fileInput) {
66968
+ fileInput.setAttribute("accept", "*/*");
66969
+ console.log("[FormIO] \u{1F4CE} File input found, accept=*/* applied");
66970
+ } else {
66971
+ console.log("[FormIO] \u2139\uFE0F No file input found");
66972
+ }
66973
+
66974
+ console.log("[FormIO] \u{1F389} Full formReady process completed");
66975
+ })
66976
+ .catch((error) => {
66977
+ console.error("[FormIO] \u274C formReady.then() failed:", error);
66786
66978
  });
66787
- console.log("[FormIO] \u2705 Form rendered successfully \u2014 SDK handover complete");
66788
66979
 
66789
- // Hide SDK loader when form is ready
66790
- const sdkLoader = document.getElementById('sdk-form-loader');
66791
- if (sdkLoader) {
66792
- sdkLoader.classList.add('hidden');
66793
- }
66794
66980
 
66795
- // mark rendered; LessRenderingComplete will also set readiness when styling finishes
66796
66981
 
66797
- document.dispatchEvent(new CustomEvent("FormRenderingComplete"));
66798
- const fileInput = document.querySelector('input[type="file"]');
66799
- if (fileInput) fileInput.setAttribute("accept", "*/*");
66800
- });
66801
66982
  } catch (error) {
66802
66983
  console.error("\u274C Error creating form (after retries):", error);
66803
66984
 
@@ -67925,7 +68106,7 @@ window.deleteAppDocument = async function(id) {
67925
68106
  window.getAllDocuments = getAllDocuments;
67926
68107
  window.hasDocuments = hasDocuments;
67927
68108
  function getBuildVersion() {
67928
- return "1.0.19";
68109
+ return "1.0.21";
67929
68110
  }
67930
68111
  export {
67931
68112
  getBuildVersion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unvired/turboforms-embed-sdk",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
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",