-
Notifications
You must be signed in to change notification settings - Fork 279
fix(ui5-popover): render block layers in the correct order #12659
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
kskondov
wants to merge
20
commits into
main
Choose a base branch
from
ui5-popover-block-layers
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 9 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
e76d000
fix(ui5-popover): render block layers in the correct order
kskondov 9f880ec
fix(ui5-popover): render block layers in the correct order
kskondov 2f34f5c
Merge remote-tracking branch 'origin/main' into ui5-popover-block-layers
TeodorTaushanov 598adbb
chore: handle opening and closing
TeodorTaushanov f7fd078
fix(ui5-popover): render block layers in the correct order
kskondov 9b7f595
Merge remote-tracking branch 'origin/main' into ui5-popover-block-layers
TeodorTaushanov 63fe5d3
chore: improve TypeScript structure
TeodorTaushanov b214070
chore: improve TypeScript structure, fix some issues
TeodorTaushanov da23cdf
Merge remote-tracking branch 'origin/main' into ui5-popover-block-layers
TeodorTaushanov 4a25afd
Merge remote-tracking branch 'origin/main' into ui5-popover-block-layers
TeodorTaushanov b4ea772
Merge remote-tracking branch 'origin/main' into ui5-popover-block-layers
TeodorTaushanov d2d83e6
chore: improve code
TeodorTaushanov 2a94f75
Merge remote-tracking branch 'origin/main' into ui5-popover-block-layers
TeodorTaushanov 06049d7
Merge remote-tracking branch 'origin/main' into ui5-popover-block-layers
TeodorTaushanov dc01f29
chore: address code comments
TeodorTaushanov c0a5936
Merge remote-tracking branch 'origin/main' into ui5-popover-block-layers
TeodorTaushanov 75cc691
chore: address code comments
TeodorTaushanov b6ae187
Merge remote-tracking branch 'origin/main' into ui5-popover-block-layers
TeodorTaushanov 1204bbe
chore: fix tests
TeodorTaushanov ff2da1c
Merge remote-tracking branch 'origin/main' into ui5-popover-block-layers
TeodorTaushanov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,4 +2,9 @@ | |
| border: none; | ||
| overflow: visible; | ||
| margin: 0; | ||
| } | ||
|
|
||
| .sapUiBLy[popover] { | ||
| width: 100%; | ||
| height: 100%; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,27 +6,33 @@ type Control = { | |
| getDomRef: () => HTMLElement | null, | ||
| } | ||
|
|
||
| // The lifecycle of Popup.js is open -> _opened -> close -> _closed, we're interested in the first (open) and last (_closed) | ||
| type OpenUI5Popup = { | ||
| prototype: { | ||
| open: (...args: any[]) => void, | ||
| _closed: (...args: any[]) => void, | ||
| getOpenState: () => "CLOSED" | "CLOSING" | "OPEN" | "OPENING", | ||
| getContent: () => Control | HTMLElement | null, // this is the OpenUI5 Element/Control instance that opens the Popup (usually sap.m.Popover/sap.m.Dialog) | ||
| onFocusEvent: (...args: any[]) => void, | ||
| } | ||
| open: (...args: any[]) => void, | ||
| _closed: (...args: any[]) => void, | ||
| getOpenState: () => "CLOSED" | "CLOSING" | "OPEN" | "OPENING", | ||
| getContent: () => Control | HTMLElement | null, // this is the OpenUI5 Element/Control instance that opens the Popup (usually sap.m.Popover/sap.m.Dialog) | ||
| onFocusEvent: (...args: any[]) => void, | ||
| getModal: () => boolean | ||
| }; | ||
|
|
||
| type OpenUI5PopupBasedControl = { | ||
| // The lifecycle of Popup.js is open -> _opened -> close -> _closed, we're interested in the first (open) and last (_closed) | ||
TeodorTaushanov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| type OpenUI5PopupClass = { | ||
| prototype: OpenUI5Popup | ||
| }; | ||
|
|
||
| type OpenUI5DialogClass = { | ||
| prototype: { | ||
| onsapescape: (...args: any[]) => void, | ||
| oPopup: OpenUI5Popup, | ||
| } | ||
| }; | ||
|
|
||
| type PopupInfo = { | ||
| type: "OpenUI5" | "WebComponent"; | ||
| type: "WebComponent"; | ||
| instance: object; | ||
| } | { | ||
| type: "OpenUI5"; | ||
| instance: OpenUI5Popup; | ||
| }; | ||
|
|
||
| // contains all OpenUI5 and Web Component popups that are currently opened | ||
|
|
@@ -38,6 +44,11 @@ const addOpenedPopup = (popupInfo: PopupInfo) => { | |
|
|
||
| const removeOpenedPopup = (popup: object) => { | ||
| const index = AllOpenedPopupsRegistry.openedRegistry.findIndex(el => el.instance === popup); | ||
|
|
||
| if (index === AllOpenedPopupsRegistry.openedRegistry.length - 1) { | ||
| fixOpenUI5PopupBelow(); | ||
| } | ||
|
|
||
| if (index > -1) { | ||
| AllOpenedPopupsRegistry.openedRegistry.splice(index, 1); | ||
| } | ||
|
|
@@ -68,16 +79,56 @@ const hasWebComponentPopupAbove = (popup: object) => { | |
| return false; | ||
| }; | ||
|
|
||
| const openNativePopover = (domRef: HTMLElement) => { | ||
| const enableNativePopoverForOpenUI5 = (domRef: HTMLElement, popup: OpenUI5Popup) => { | ||
TeodorTaushanov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const openUI5BlockLayer = document.getElementById("sap-ui-blocklayer-popup"); | ||
|
|
||
| if (popup.getModal() && openUI5BlockLayer) { | ||
| openUI5BlockLayer.setAttribute("popover", "manual"); | ||
| openUI5BlockLayer.hidePopover(); | ||
| openUI5BlockLayer.showPopover(); | ||
| } | ||
|
|
||
| domRef.setAttribute("popover", "manual"); | ||
| domRef.showPopover(); | ||
| }; | ||
|
|
||
| const closeNativePopover = (domRef: HTMLElement) => { | ||
| const disableNativePopoverForOpenUI5 = (domRef: HTMLElement) => { | ||
| if (domRef.hasAttribute("popover")) { | ||
| domRef.hidePopover(); | ||
| domRef.removeAttribute("popover"); | ||
| } | ||
|
|
||
| const lastPopup = AllOpenedPopupsRegistry.openedRegistry[AllOpenedPopupsRegistry.openedRegistry.length - 1]; | ||
TeodorTaushanov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (lastPopup.type === "OpenUI5" && lastPopup.instance.getModal()) { | ||
| const openUI5BlockLayer = document.getElementById("sap-ui-blocklayer-popup"); | ||
| if (openUI5BlockLayer && openUI5BlockLayer.hasAttribute("popover")) { | ||
| openUI5BlockLayer.hidePopover(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| const fixOpenUI5PopupBelow = () => { | ||
| if (!isNativePopoverOpen()) { | ||
| return; | ||
| } | ||
|
|
||
| const prevPopup = AllOpenedPopupsRegistry.openedRegistry[AllOpenedPopupsRegistry.openedRegistry.length - 2]; | ||
| if (!prevPopup || prevPopup.type !== "OpenUI5" || !prevPopup.instance.getModal()) { | ||
TeodorTaushanov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return; | ||
| } | ||
|
|
||
| const prevPopupContent = prevPopup.instance.getContent()!; | ||
| const content = prevPopupContent instanceof HTMLElement ? prevPopupContent : prevPopupContent?.getDomRef(); | ||
TeodorTaushanov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| const openUI5BlockLayer = document.getElementById("sap-ui-blocklayer-popup"); | ||
|
|
||
| content?.hidePopover(); | ||
|
|
||
| if (prevPopup.instance.getModal()) { | ||
TeodorTaushanov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| openUI5BlockLayer?.showPopover(); | ||
| } | ||
|
|
||
| content?.showPopover(); | ||
| }; | ||
|
|
||
| const isNativePopoverOpen = (root: Document | ShadowRoot = document): boolean => { | ||
|
|
@@ -91,9 +142,9 @@ const isNativePopoverOpen = (root: Document | ShadowRoot = document): boolean => | |
| }); | ||
| }; | ||
|
|
||
| const patchPopupBasedControl = (PopupBasedControl: OpenUI5PopupBasedControl) => { | ||
| const origOnsapescape = PopupBasedControl.prototype.onsapescape; | ||
| PopupBasedControl.prototype.onsapescape = function onsapescape(...args: any[]) { | ||
| const patchDialog = (Dialog: OpenUI5DialogClass) => { | ||
| const origOnsapescape = Dialog.prototype.onsapescape; | ||
| Dialog.prototype.onsapescape = function onsapescape(...args: any[]) { | ||
| if (hasWebComponentPopupAbove(this.oPopup)) { | ||
| return; | ||
| } | ||
|
|
@@ -102,18 +153,17 @@ const patchPopupBasedControl = (PopupBasedControl: OpenUI5PopupBasedControl) => | |
| }; | ||
| }; | ||
|
|
||
| const patchOpen = (Popup: OpenUI5Popup) => { | ||
| const patchOpen = (Popup: OpenUI5PopupClass) => { | ||
| const origOpen = Popup.prototype.open; | ||
| Popup.prototype.open = function open(...args: any[]) { | ||
| origOpen.apply(this, args); // call open first to initiate opening | ||
| const topLayerAlreadyInUse = isNativePopoverOpen(); | ||
| const openingInitiated = ["OPENING", "OPEN"].includes(this.getOpenState()); | ||
| if (openingInitiated && topLayerAlreadyInUse) { | ||
| if (openingInitiated && isNativePopoverOpen()) { | ||
| const element = this.getContent(); | ||
| if (element) { | ||
| const domRef = element instanceof HTMLElement ? element : element?.getDomRef(); | ||
| if (domRef) { | ||
| openNativePopover(domRef); | ||
| enableNativePopoverForOpenUI5(domRef, this); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -125,21 +175,21 @@ const patchOpen = (Popup: OpenUI5Popup) => { | |
| }; | ||
| }; | ||
|
|
||
| const patchClosed = (Popup: OpenUI5Popup) => { | ||
| const patchClosed = (Popup: OpenUI5PopupClass) => { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if dialogs are destroyed instead of closed, the block layer remains with "popover=manual" |
||
| const _origClosed = Popup.prototype._closed; | ||
| Popup.prototype._closed = function _closed(...args: any[]) { | ||
| const element = this.getContent(); | ||
| const domRef = element instanceof HTMLElement ? element : element?.getDomRef(); | ||
| _origClosed.apply(this, args); // only then call _close | ||
| if (domRef) { | ||
| closeNativePopover(domRef); // unset the popover attribute and close the native popover, but only if still in DOM | ||
| disableNativePopoverForOpenUI5(domRef); // unset the popover attribute and close the native popover, but only if still in DOM | ||
TeodorTaushanov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| removeOpenedPopup(this); | ||
| }; | ||
| }; | ||
|
|
||
| const patchFocusEvent = (Popup: OpenUI5Popup) => { | ||
| const patchFocusEvent = (Popup: OpenUI5PopupClass) => { | ||
| const origFocusEvent = Popup.prototype.onFocusEvent; | ||
| Popup.prototype.onFocusEvent = function onFocusEvent(...args: any[]) { | ||
| if (!hasWebComponentPopupAbove(this)) { | ||
|
|
@@ -154,14 +204,13 @@ const createGlobalStyles = () => { | |
| document.adoptedStyleSheets = [...document.adoptedStyleSheets, stylesheet]; | ||
| }; | ||
|
|
||
| const patchPopup = (Popup: OpenUI5Popup, Dialog: OpenUI5PopupBasedControl, Popover: OpenUI5PopupBasedControl) => { | ||
| const patchPopup = (Popup: OpenUI5PopupClass, Dialog: OpenUI5DialogClass) => { | ||
| insertOpenUI5PopupStyles(); | ||
| patchOpen(Popup); // Popup.prototype.open | ||
| patchClosed(Popup); // Popup.prototype._closed | ||
| createGlobalStyles(); // Ensures correct popover positioning by OpenUI5 (otherwise 0,0 is the center of the screen) | ||
| patchFocusEvent(Popup);// Popup.prototype.onFocusEvent | ||
| patchPopupBasedControl(Dialog); // Dialog.prototype.onsapescape | ||
| patchPopupBasedControl(Popover); // Popover.prototype.onsapescape | ||
| patchDialog(Dialog); // Dialog.prototype.onsapescape | ||
| }; | ||
|
|
||
| export { | ||
|
|
@@ -171,4 +220,4 @@ export { | |
| getTopmostPopup, | ||
| }; | ||
|
|
||
| export type { OpenUI5Popup, OpenUI5PopupBasedControl, PopupInfo }; | ||
| export type { OpenUI5PopupClass, OpenUI5DialogClass, PopupInfo }; | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in getTopmostPopup - AllOpenedPopupsRegistry.openedRegistry potentially could be empty and then the code will fail with error. Add a check and return null if empty