@@ -8,25 +8,31 @@ type Control = {
88
99// The lifecycle of Popup.js is open -> _opened -> close -> _closed, we're interested in the first (open) and last (_closed)
1010type OpenUI5Popup = {
11- prototype : {
12- open : ( ...args : any [ ] ) => void ,
13- _closed : ( ...args : any [ ] ) => void ,
14- getOpenState : ( ) => "CLOSED" | "CLOSING" | "OPEN" | "OPENING" ,
15- getContent : ( ) => Control | HTMLElement | null , // this is the OpenUI5 Element/Control instance that opens the Popup (usually sap.m.Popover/sap.m.Dialog)
16- onFocusEvent : ( ...args : any [ ] ) => void ,
17- }
11+ open : ( ...args : any [ ] ) => void ,
12+ _closed : ( ...args : any [ ] ) => void ,
13+ getOpenState : ( ) => "CLOSED" | "CLOSING" | "OPEN" | "OPENING" ,
14+ getContent : ( ) => Control | HTMLElement | null , // this is the OpenUI5 Element/Control instance that opens the Popup (usually sap.m.Popover/sap.m.Dialog)
15+ onFocusEvent : ( ...args : any [ ] ) => void ,
16+ getModal : ( ) => boolean
1817} ;
1918
20- type OpenUI5PopupBasedControl = {
19+ type OpenUI5PopupClass = {
20+ prototype : OpenUI5Popup
21+ } ;
22+
23+ type OpenUI5DialogClass = {
2124 prototype : {
2225 onsapescape : ( ...args : any [ ] ) => void ,
2326 oPopup : OpenUI5Popup ,
2427 }
2528} ;
2629
2730type PopupInfo = {
28- type : "OpenUI5" | " WebComponent";
31+ type : "WebComponent" ;
2932 instance : object ;
33+ } | {
34+ type : "OpenUI5" ;
35+ instance : OpenUI5Popup ;
3036} ;
3137
3238// contains all OpenUI5 and Web Component popups that are currently opened
@@ -38,12 +44,20 @@ const addOpenedPopup = (popupInfo: PopupInfo) => {
3844
3945const removeOpenedPopup = ( popup : object ) => {
4046 const index = AllOpenedPopupsRegistry . openedRegistry . findIndex ( el => el . instance === popup ) ;
47+
48+ if ( index === AllOpenedPopupsRegistry . openedRegistry . length - 1 ) {
49+ fixTopmostOpenUI5Popup ( ) ;
50+ }
51+
4152 if ( index > - 1 ) {
4253 AllOpenedPopupsRegistry . openedRegistry . splice ( index , 1 ) ;
4354 }
4455} ;
4556
4657const getTopmostPopup = ( ) => {
58+ if ( AllOpenedPopupsRegistry . openedRegistry . length === 0 ) {
59+ return null ;
60+ }
4761 return AllOpenedPopupsRegistry . openedRegistry [ AllOpenedPopupsRegistry . openedRegistry . length - 1 ] . instance ;
4862} ;
4963
@@ -68,16 +82,83 @@ const hasWebComponentPopupAbove = (popup: object) => {
6882 return false ;
6983} ;
7084
71- const openNativePopover = ( domRef : HTMLElement ) => {
85+ const getPopupContentElement = ( popup : OpenUI5Popup ) : HTMLElement | null => {
86+ const content = popup . getContent ( ) ;
87+ return content instanceof HTMLElement ? content : content ?. getDomRef ( ) || null ;
88+ } ;
89+
90+ const openNativePopoverForOpenUI5 = ( popup : OpenUI5Popup ) => {
91+ const openingInitiated = [ "OPENING" , "OPEN" ] . includes ( popup . getOpenState ( ) ) ;
92+ if ( ! openingInitiated || ! isNativePopoverOpen ( ) ) {
93+ return ;
94+ }
95+
96+ const domRef = getPopupContentElement ( popup ) ;
97+
98+ if ( ! domRef ) {
99+ return ;
100+ }
101+
102+ const openUI5BlockLayer = document . getElementById ( "sap-ui-blocklayer-popup" ) ;
103+
104+ if ( popup . getModal ( ) && openUI5BlockLayer ) {
105+ openUI5BlockLayer . setAttribute ( "popover" , "manual" ) ;
106+ openUI5BlockLayer . hidePopover ( ) ;
107+ openUI5BlockLayer . showPopover ( ) ;
108+ }
109+
72110 domRef . setAttribute ( "popover" , "manual" ) ;
73111 domRef . showPopover ( ) ;
74112} ;
75113
76- const closeNativePopover = ( domRef : HTMLElement ) => {
114+ const closeNativePopoverForOpenUI5 = ( popup : OpenUI5Popup ) => {
115+ const domRef = getPopupContentElement ( popup ) ;
116+
117+ if ( ! domRef ) {
118+ return ;
119+ }
120+
77121 if ( domRef . hasAttribute ( "popover" ) ) {
78122 domRef . hidePopover ( ) ;
79123 domRef . removeAttribute ( "popover" ) ;
80124 }
125+
126+ if ( getTopmostPopup ( ) !== popup ) {
127+ return ;
128+ }
129+
130+ // The OpenUI5 block layer is only one for all modal OpenUI5 popups,
131+ // and it is displayed above all opened pupups - OpenUI5 and Web Components,
132+ // as a result, we need to hide this block layer.
133+ // If the underlying popup is a Web Component - it is displayed like a native popover, and we don't need to do anything
134+ // If the underlying popup is an OpenUI5 popup, it will be fixed in fixTopmostOpenUI5Popup method.
135+ if ( popup . getModal ( ) ) {
136+ const openUI5BlockLayer = document . getElementById ( "sap-ui-blocklayer-popup" ) ;
137+ if ( openUI5BlockLayer && openUI5BlockLayer . hasAttribute ( "popover" ) ) {
138+ openUI5BlockLayer . hidePopover ( ) ;
139+ }
140+ }
141+ } ;
142+
143+ const fixTopmostOpenUI5Popup = ( ) => {
144+ if ( ! isNativePopoverOpen ( ) ) {
145+ return ;
146+ }
147+
148+ const prevPopup = AllOpenedPopupsRegistry . openedRegistry [ AllOpenedPopupsRegistry . openedRegistry . length - 2 ] ;
149+ if ( ! prevPopup
150+ || prevPopup . type !== "OpenUI5"
151+ || ! prevPopup . instance . getModal ( ) ) {
152+ return ;
153+ }
154+
155+ const content = getPopupContentElement ( prevPopup . instance ) ;
156+ const openUI5BlockLayer = document . getElementById ( "sap-ui-blocklayer-popup" ) ;
157+
158+ content ?. hidePopover ( ) ;
159+ openUI5BlockLayer ?. showPopover ( ) ;
160+
161+ content ?. showPopover ( ) ;
81162} ;
82163
83164const isNativePopoverOpen = ( root : Document | ShadowRoot = document ) : boolean => {
@@ -91,9 +172,9 @@ const isNativePopoverOpen = (root: Document | ShadowRoot = document): boolean =>
91172 } ) ;
92173} ;
93174
94- const patchPopupBasedControl = ( PopupBasedControl : OpenUI5PopupBasedControl ) => {
95- const origOnsapescape = PopupBasedControl . prototype . onsapescape ;
96- PopupBasedControl . prototype . onsapescape = function onsapescape ( ...args : any [ ] ) {
175+ const patchDialog = ( Dialog : OpenUI5DialogClass ) => {
176+ const origOnsapescape = Dialog . prototype . onsapescape ;
177+ Dialog . prototype . onsapescape = function onsapescape ( ...args : any [ ] ) {
97178 if ( hasWebComponentPopupAbove ( this . oPopup ) ) {
98179 return ;
99180 }
@@ -102,21 +183,11 @@ const patchPopupBasedControl = (PopupBasedControl: OpenUI5PopupBasedControl) =>
102183 } ;
103184} ;
104185
105- const patchOpen = ( Popup : OpenUI5Popup ) => {
186+ const patchOpen = ( Popup : OpenUI5PopupClass ) => {
106187 const origOpen = Popup . prototype . open ;
107188 Popup . prototype . open = function open ( ...args : any [ ] ) {
108189 origOpen . apply ( this , args ) ; // call open first to initiate opening
109- const topLayerAlreadyInUse = isNativePopoverOpen ( ) ;
110- const openingInitiated = [ "OPENING" , "OPEN" ] . includes ( this . getOpenState ( ) ) ;
111- if ( openingInitiated && topLayerAlreadyInUse ) {
112- const element = this . getContent ( ) ;
113- if ( element ) {
114- const domRef = element instanceof HTMLElement ? element : element ?. getDomRef ( ) ;
115- if ( domRef ) {
116- openNativePopover ( domRef ) ;
117- }
118- }
119- }
190+ openNativePopoverForOpenUI5 ( this ) ;
120191
121192 addOpenedPopup ( {
122193 type : "OpenUI5" ,
@@ -125,21 +196,16 @@ const patchOpen = (Popup: OpenUI5Popup) => {
125196 } ;
126197} ;
127198
128- const patchClosed = ( Popup : OpenUI5Popup ) => {
199+ const patchClosed = ( Popup : OpenUI5PopupClass ) => {
129200 const _origClosed = Popup . prototype . _closed ;
130201 Popup . prototype . _closed = function _closed ( ...args : any [ ] ) {
131- const element = this . getContent ( ) ;
132- const domRef = element instanceof HTMLElement ? element : element ?. getDomRef ( ) ;
202+ closeNativePopoverForOpenUI5 ( this ) ;
133203 _origClosed . apply ( this , args ) ; // only then call _close
134- if ( domRef ) {
135- closeNativePopover ( domRef ) ; // unset the popover attribute and close the native popover, but only if still in DOM
136- }
137-
138204 removeOpenedPopup ( this ) ;
139205 } ;
140206} ;
141207
142- const patchFocusEvent = ( Popup : OpenUI5Popup ) => {
208+ const patchFocusEvent = ( Popup : OpenUI5PopupClass ) => {
143209 const origFocusEvent = Popup . prototype . onFocusEvent ;
144210 Popup . prototype . onFocusEvent = function onFocusEvent ( ...args : any [ ] ) {
145211 if ( ! hasWebComponentPopupAbove ( this ) ) {
@@ -154,13 +220,13 @@ const createGlobalStyles = () => {
154220 document . adoptedStyleSheets = [ ...document . adoptedStyleSheets , stylesheet ] ;
155221} ;
156222
157- const patchPopup = ( Popup : OpenUI5Popup , Dialog : OpenUI5PopupBasedControl ) => {
223+ const patchPopup = ( Popup : OpenUI5PopupClass , Dialog : OpenUI5DialogClass ) => {
158224 insertOpenUI5PopupStyles ( ) ;
159225 patchOpen ( Popup ) ; // Popup.prototype.open
160226 patchClosed ( Popup ) ; // Popup.prototype._closed
161227 createGlobalStyles ( ) ; // Ensures correct popover positioning by OpenUI5 (otherwise 0,0 is the center of the screen)
162228 patchFocusEvent ( Popup ) ; // Popup.prototype.onFocusEvent
163- patchPopupBasedControl ( Dialog ) ; // Dialog.prototype.onsapescape
229+ patchDialog ( Dialog ) ; // Dialog.prototype.onsapescape
164230} ;
165231
166232export {
@@ -170,4 +236,4 @@ export {
170236 getTopmostPopup ,
171237} ;
172238
173- export type { OpenUI5Popup , OpenUI5PopupBasedControl , PopupInfo } ;
239+ export type { OpenUI5PopupClass , OpenUI5DialogClass , PopupInfo } ;
0 commit comments