diff --git a/package.json b/package.json
index 369c297e..1b49f406 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,8 @@
"localized",
"material",
"bootstrap",
- "i18n"
+ "i18n",
+ "modal"
],
"files": [
"lang",
diff --git a/src/index.js b/src/index.js
index 07988e0f..a1ed5ef8 100644
--- a/src/index.js
+++ b/src/index.js
@@ -239,12 +239,15 @@ class PhoneInput extends React.Component {
if(this.props.onMount){
this.props.onMount(this.state.formattedNumber.replace(/[^0-9]+/g,''), this.getCountryData(), this.state.formattedNumber)
}
+ window.addEventListener('resize', this.handleWindowResize);
}
componentWillUnmount() {
if (document.removeEventListener && this.props.enableClickOutside) {
document.removeEventListener('mousedown', this.handleClickOutside);
}
+ window.removeEventListener('resize', this.handleWindowResize);
+ this.handleOverflowDisposal();
}
componentDidUpdate(prevProps, prevState, snapshot) {
@@ -489,12 +492,102 @@ class PhoneInput extends React.Component {
}
}
+ handleWindowResize = debounce(() => {
+ if (this.state.showDropdown) {
+ const newModalStyle = this.calculateModalPosition();
+ this.setState({ modalStyle: newModalStyle });
+ }
+ }, 100);
+
+ calculateModalPosition = () => {
+ if (!this.rootRef) return {};
+
+ const root = this.rootRef;
+ const { x, y, height } = root.getBoundingClientRect();
+ const { innerHeight, innerWidth } = window;
+
+ let dropdownHeight = 200;
+ let mTop = 0;
+ let dropdownWidth = 300;
+
+ if (this.dropdownRef) {
+ const dropdownRect = this.dropdownRef.getBoundingClientRect();
+ const { marginTop } = window.getComputedStyle(this.dropdownRef);
+ dropdownHeight = dropdownRect.height;
+ dropdownWidth = dropdownRect.width;
+ mTop = parseInt(marginTop)
+ }
+
+ const spaceBelow = innerHeight - (y + height);
+ const spaceAbove = y;
+
+ let modalTop = y + height;
+ let modalLeft = x + 1;
+
+ if (spaceBelow < dropdownHeight + dropdownHeight * 0.1 && spaceAbove >= dropdownHeight + dropdownHeight * 0.1) {
+ modalTop = y - dropdownHeight - mTop * 2;
+ }
+
+ if (modalLeft + dropdownWidth > innerWidth) {
+ modalLeft = innerWidth - dropdownWidth - 10;
+ }
+ if (modalLeft < 0) {
+ modalLeft = 10;
+ }
+
+ return {
+ position: 'absolute',
+ left: modalLeft,
+ top: modalTop
+ };
+ }
+
+ /**
+ * ### Create Modal
+ *
+ * this function will render list insite fixed modal
+ *
+ * this way it will avoid various issues
+ * that occure on overflows, dialogs and many more
+ */
+ handleCreateModal() {
+ const root = this.rootRef;
+ if(!root) return;
+
+ document.documentElement.style.overflow = 'hidden';
+
+ const initialModalStyle = this.calculateModalPosition();
+ const modalStyle = this.state.modalStyle.position ? this.state.modalStyle : initialModalStyle;
+
+ requestAnimationFrame(() => {
+ if (this.dropdownRef) {
+ const refinedStyle = this.calculateModalPosition();
+ this.setState({ modalStyle: refinedStyle });
+ }
+ });
+
+ return (
+
+ {this.getCountryDropdownList()}
+
+ );
+ }
+ /**
+ * ### Dispose Overflow
+ *
+ * this function will dispose overflow hidden on HTML
+ */
+ handleOverflowDisposal() {
+ document.documentElement.removeAttribute('style');
+ this.setState({ modalStyle: {} });
+ }
handleFlagDropdownClick = (e) => {
e.preventDefault();
if (!this.state.showDropdown && this.props.disabled) return;
const { preferredCountries, onlyCountries, selectedCountry } = this.state
+
const allCountries = this.concatPreferredCountries(preferredCountries, onlyCountries);
const highlightCountryIndex = allCountries.findIndex(o =>
@@ -503,6 +596,7 @@ class PhoneInput extends React.Component {
this.setState({
showDropdown: !this.state.showDropdown,
highlightCountryIndex,
+ modalStyle: this.state.showDropdown ? {} : this.calculateModalPosition()
}, () => {
if (this.state.showDropdown) {
this.scrollTo(this.getElement(this.state.highlightCountryIndex));
@@ -628,6 +722,7 @@ class PhoneInput extends React.Component {
formattedNumber,
searchValue: ''
}, () => {
+ this.handleOverflowDisposal();
this.cursorToEnd();
if (this.props.onChange) this.props.onChange(formattedNumber.replace(/[^0-9]+/g,''), this.getCountryData(), e, formattedNumber);
});
@@ -728,9 +823,10 @@ class PhoneInput extends React.Component {
break;
case keys.ESC:
case keys.TAB:
- this.setState({
- showDropdown: false
- }, this.cursorToEnd);
+ this.setState({ showDropdown: false }, () => {
+ this.handleOverflowDisposal();
+ this.cursorToEnd();
+ });
break;
default:
if ((e.which >= keys.A && e.which <= keys.Z) || e.which === keys.SPACE) {
@@ -751,7 +847,11 @@ class PhoneInput extends React.Component {
handleClickOutside = (e) => {
if (this.dropdownRef && !this.dropdownContainerRef.contains(e.target)) {
- this.state.showDropdown && this.setState({ showDropdown: false });
+ if (this.state.showDropdown) {
+ this.setState({ showDropdown: false }, () => {
+ this.handleOverflowDisposal();
+ });
+ }
}
}
@@ -953,6 +1053,7 @@ class PhoneInput extends React.Component {
return (
this.rootRef = el}
className={`${containerClasses} ${this.props.className}`}
style={this.props.style || this.props.containerStyle}
onKeyDown={this.handleKeydown}>
@@ -999,13 +1100,18 @@ class PhoneInput extends React.Component {
role='button'
aria-haspopup="listbox"
aria-expanded={showDropdown ? true : undefined}
+ aria-controls='modal-root'
>
}
- {showDropdown && this.getCountryDropdownList()}
+ {showDropdown && (
+ this.setState({ showDropdown: false, modalStyle: {} }, () => this.handleOverflowDisposal())}>
+ {this.handleCreateModal()}
+
+ )}
);
diff --git a/src/style/bootstrap.less b/src/style/bootstrap.less
index 01317a38..cfe14013 100644
--- a/src/style/bootstrap.less
+++ b/src/style/bootstrap.less
@@ -183,4 +183,9 @@
padding: 0 5px;
white-space: nowrap;
}
+ .modal-root {
+ position: fixed;
+ z-index: 99999;
+ inset: 0px;
+ }
}
diff --git a/src/style/high-res.less b/src/style/high-res.less
index 21d2b2b6..08fdb0b3 100644
--- a/src/style/high-res.less
+++ b/src/style/high-res.less
@@ -168,4 +168,9 @@
padding: 0 2px;
white-space: nowrap;
}
+ .modal-root {
+ position: fixed;
+ z-index: 99999;
+ inset: 0px;
+ }
}
diff --git a/src/style/material.less b/src/style/material.less
index 913d9941..730061ea 100644
--- a/src/style/material.less
+++ b/src/style/material.less
@@ -186,4 +186,9 @@
font-size: 13px;
white-space: nowrap;
}
+ .modal-root {
+ position: fixed;
+ z-index: 99999;
+ inset: 0px;
+ }
}
diff --git a/src/style/plain.less b/src/style/plain.less
index 707efabe..98acb1c8 100644
--- a/src/style/plain.less
+++ b/src/style/plain.less
@@ -169,4 +169,9 @@
padding: 0 2px;
white-space: nowrap;
}
+ .modal-root {
+ position: fixed;
+ z-index: 99999;
+ inset: 0px;
+ }
}
diff --git a/src/style/semantic-ui.less b/src/style/semantic-ui.less
index 70d77352..9afee1ff 100644
--- a/src/style/semantic-ui.less
+++ b/src/style/semantic-ui.less
@@ -190,4 +190,9 @@
padding: 0 2px;
white-space: nowrap;
}
+ .modal-root {
+ position: fixed;
+ z-index: 99999;
+ inset: 0px;
+ }
}
diff --git a/src/style/style.less b/src/style/style.less
index 298a1046..5f96b89c 100644
--- a/src/style/style.less
+++ b/src/style/style.less
@@ -176,4 +176,9 @@
padding: 0 2px;
white-space: nowrap;
}
+ .modal-root {
+ position: fixed;
+ z-index: 99999;
+ inset: 0px;
+ }
}