From d20d707e8004205abfec061593a43cc25a2ac482 Mon Sep 17 00:00:00 2001 From: David Gogniashvili Date: Thu, 12 Jun 2025 13:50:59 +0400 Subject: [PATCH 1/4] added keyword --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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", From 9de60766e3b5245b8d3f5681e3530379d8f15f34 Mon Sep 17 00:00:00 2001 From: David Gogniashvili Date: Thu, 12 Jun 2025 15:59:56 +0400 Subject: [PATCH 2/4] added modal class to styles --- src/style/bootstrap.less | 5 +++++ src/style/high-res.less | 5 +++++ src/style/material.less | 5 +++++ src/style/plain.less | 5 +++++ src/style/semantic-ui.less | 5 +++++ src/style/style.less | 5 +++++ 6 files changed, 30 insertions(+) diff --git a/src/style/bootstrap.less b/src/style/bootstrap.less index 01317a38..d3362c91 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: 1300; + inset: 0px; + } } diff --git a/src/style/high-res.less b/src/style/high-res.less index 21d2b2b6..1e97f4ef 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: 1300; + inset: 0px; + } } diff --git a/src/style/material.less b/src/style/material.less index 913d9941..3991d178 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: 1300; + inset: 0px; + } } diff --git a/src/style/plain.less b/src/style/plain.less index 707efabe..5575f494 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: 1300; + inset: 0px; + } } diff --git a/src/style/semantic-ui.less b/src/style/semantic-ui.less index 70d77352..101938ae 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: 1300; + inset: 0px; + } } diff --git a/src/style/style.less b/src/style/style.less index 298a1046..1f60f083 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: 1300; + inset: 0px; + } } From 672b3788f63c8a60350b9c7126ad79a73007608d Mon Sep 17 00:00:00 2001 From: David Gogniashvili Date: Thu, 12 Jun 2025 16:10:22 +0400 Subject: [PATCH 3/4] zIndex update --- src/style/bootstrap.less | 2 +- src/style/high-res.less | 2 +- src/style/material.less | 2 +- src/style/plain.less | 2 +- src/style/semantic-ui.less | 2 +- src/style/style.less | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/style/bootstrap.less b/src/style/bootstrap.less index d3362c91..cfe14013 100644 --- a/src/style/bootstrap.less +++ b/src/style/bootstrap.less @@ -185,7 +185,7 @@ } .modal-root { position: fixed; - z-index: 1300; + z-index: 99999; inset: 0px; } } diff --git a/src/style/high-res.less b/src/style/high-res.less index 1e97f4ef..08fdb0b3 100644 --- a/src/style/high-res.less +++ b/src/style/high-res.less @@ -170,7 +170,7 @@ } .modal-root { position: fixed; - z-index: 1300; + z-index: 99999; inset: 0px; } } diff --git a/src/style/material.less b/src/style/material.less index 3991d178..730061ea 100644 --- a/src/style/material.less +++ b/src/style/material.less @@ -188,7 +188,7 @@ } .modal-root { position: fixed; - z-index: 1300; + z-index: 99999; inset: 0px; } } diff --git a/src/style/plain.less b/src/style/plain.less index 5575f494..98acb1c8 100644 --- a/src/style/plain.less +++ b/src/style/plain.less @@ -171,7 +171,7 @@ } .modal-root { position: fixed; - z-index: 1300; + z-index: 99999; inset: 0px; } } diff --git a/src/style/semantic-ui.less b/src/style/semantic-ui.less index 101938ae..9afee1ff 100644 --- a/src/style/semantic-ui.less +++ b/src/style/semantic-ui.less @@ -192,7 +192,7 @@ } .modal-root { position: fixed; - z-index: 1300; + z-index: 99999; inset: 0px; } } diff --git a/src/style/style.less b/src/style/style.less index 1f60f083..5f96b89c 100644 --- a/src/style/style.less +++ b/src/style/style.less @@ -178,7 +178,7 @@ } .modal-root { position: fixed; - z-index: 1300; + z-index: 99999; inset: 0px; } } From a535b0df912edbd481e2f4fe42871c6c2034774e Mon Sep 17 00:00:00 2001 From: David Gogniashvili Date: Thu, 12 Jun 2025 18:58:38 +0400 Subject: [PATCH 4/4] added modal behabiour --- src/index.js | 116 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 5 deletions(-) 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' >
{!disableDropdown &&
}
} - {showDropdown && this.getCountryDropdownList()} + {showDropdown && ( + + )} );