Skip to content

Commit

Permalink
feat(framework): Maintain a common z-index for all UI5 Web Components…
Browse files Browse the repository at this point in the history
… instances and OpenUI5 (#2980)
  • Loading branch information
vladitasev committed Mar 22, 2021
1 parent 9c156aa commit df36b64
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 84 deletions.
47 changes: 41 additions & 6 deletions packages/base/src/features/OpenUI5Support.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,41 @@
import { registerFeature } from "../FeaturesRegistry.js";
import { setTheme } from "../config/Theme.js";
import { getCurrentZIndex } from "../util/PopupUtils.js";

const sap = window.sap;
const core = sap && sap.ui && typeof sap.ui.getCore === "function" && sap.ui.getCore();
const getCore = () => {
const sap = window.sap;
const core = sap && sap.ui && typeof sap.ui.getCore === "function" && sap.ui.getCore();
return core;
};

const isLoaded = () => {
return !!core;
return !!getCore();
};

const init = () => {
const core = getCore();
if (!core) {
return Promise.resolve();
}

return new Promise(resolve => {
core.attachInit(() => {
sap.ui.require(["sap/ui/core/LocaleData"], resolve);
window.sap.ui.require(["sap/ui/core/LocaleData", "sap/ui/core/Popup"], (LocaleData, Popup) => {
Popup.setInitialZIndex(getCurrentZIndex());
resolve();
});
});
});
};

const getConfigurationSettingsObject = () => {
const core = getCore();
if (!core) {
return;
}

const config = core.getConfiguration();
const LocaleData = sap.ui.require("sap/ui/core/LocaleData");
const LocaleData = window.sap.ui.require("sap/ui/core/LocaleData");

return {
animationMode: config.getAnimationMode(),
Expand All @@ -41,23 +50,26 @@ const getConfigurationSettingsObject = () => {
};

const getLocaleDataObject = () => {
const core = getCore();
if (!core) {
return;
}

const config = core.getConfiguration();
const LocaleData = sap.ui.require("sap/ui/core/LocaleData");
const LocaleData = window.sap.ui.require("sap/ui/core/LocaleData");
return LocaleData.getInstance(config.getLocale())._get();
};

const listenForThemeChange = () => {
const core = getCore();
const config = core.getConfiguration();
core.attachThemeChanged(async () => {
await setTheme(config.getTheme());
});
};

const attachListeners = () => {
const core = getCore();
if (!core) {
return;
}
Expand All @@ -66,6 +78,7 @@ const attachListeners = () => {
};

const cssVariablesLoaded = () => {
const core = getCore();
if (!core) {
return;
}
Expand All @@ -78,13 +91,35 @@ const cssVariablesLoaded = () => {
return !!link.href.match(/\/css(-|_)variables\.css/);
};

const getNextZIndex = () => {
const core = getCore();
if (!core) {
return;
}

const Popup = window.sap.ui.require("sap/ui/core/Popup");
return Popup.getNextZIndex();
};

const setInitialZIndex = () => {
const core = getCore();
if (!core) {
return;
}

const Popup = window.sap.ui.require("sap/ui/core/Popup");
Popup.setInitialZIndex(getCurrentZIndex());
};

const OpenUI5Support = {
isLoaded,
init,
getConfigurationSettingsObject,
getLocaleDataObject,
attachListeners,
cssVariablesLoaded,
getNextZIndex,
setInitialZIndex,
};

registerFeature("OpenUI5Support", OpenUI5Support);
94 changes: 94 additions & 0 deletions packages/base/src/util/PopupUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import getSharedResource from "../getSharedResource.js";
import { getFeature } from "../FeaturesRegistry.js";
import getActiveElement from "./getActiveElement.js";

const PopupUtilsData = getSharedResource("PopupUtilsData", {});
PopupUtilsData.currentZIndex = PopupUtilsData.currentZIndex || 100;

const getFocusedElement = () => {
const element = getActiveElement();
return (element && typeof element.focus === "function") ? element : null;
};

const isFocusedElementWithinNode = node => {
const fe = getFocusedElement();

if (fe) {
return isNodeContainedWithin(node, fe);
}

return false;
};

const isNodeContainedWithin = (parent, child) => {
let currentNode = parent;

if (currentNode.shadowRoot) {
currentNode = Array.from(currentNode.shadowRoot.children).find(n => n.localName !== "style");
}

if (currentNode === child) {
return true;
}

const childNodes = currentNode.localName === "slot" ? currentNode.assignedNodes() : currentNode.children;

if (childNodes) {
return Array.from(childNodes).some(n => isNodeContainedWithin(n, child));
}
};

const isPointInRect = (x, y, rect) => {
return x >= rect.left && x <= rect.right
&& y >= rect.top && y <= rect.bottom;
};

const isClickInRect = (event, rect) => {
let x;
let y;

if (event.touches) {
const touch = event.touches[0];
x = touch.clientX;
y = touch.clientY;
} else {
x = event.clientX;
y = event.clientY;
}

return isPointInRect(x, y, rect);
};

const getClosedPopupParent = el => {
const parent = el.parentElement || (el.getRootNode && el.getRootNode().host);

if (parent && ((parent.openBy && parent.isUI5Element) || (parent.open && parent.isUI5Element) || parent === document.documentElement)) {
return parent;
}

return getClosedPopupParent(parent);
};


const getNextZIndex = () => {
const OpenUI5Support = getFeature("OpenUI5Support");
if (OpenUI5Support && OpenUI5Support.isLoaded()) { // use OpenUI5 for getting z-index values, if loaded
return OpenUI5Support.getNextZIndex();
}

PopupUtilsData.currentZIndex += 2;
return PopupUtilsData.currentZIndex;
};

const getCurrentZIndex = () => {
return PopupUtilsData.currentZIndex;
};

export {
getFocusedElement,
isClickInRect,
getClosedPopupParent,
getNextZIndex,
getCurrentZIndex,
isFocusedElementWithinNode,
};
2 changes: 1 addition & 1 deletion packages/main/src/Popover.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
import { getClosedPopupParent } from "@ui5/webcomponents-base/dist/util/PopupUtils.js";
import Popup from "./Popup.js";
import PopoverPlacementType from "./types/PopoverPlacementType.js";
import PopoverVerticalAlign from "./types/PopoverVerticalAlign.js";
import PopoverHorizontalAlign from "./types/PopoverHorizontalAlign.js";
import { addOpenedPopover, removeOpenedPopover } from "./popup-utils/PopoverRegistry.js";
import { getClosedPopupParent } from "./popup-utils/PopupUtils.js";

// Template
import PopoverTemplate from "./generated/templates/PopoverTemplate.lit.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/Popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import { getFirstFocusableElement, getLastFocusableElement } from "@ui5/webcomponents-base/dist/util/FocusableElements.js";
import createStyleInHead from "@ui5/webcomponents-base/dist/util/createStyleInHead.js";
import { isTabPrevious } from "@ui5/webcomponents-base/dist/Keys.js";
import { getNextZIndex, getFocusedElement, isFocusedElementWithinNode } from "@ui5/webcomponents-base/dist/util/PopupUtils.js";
import PopupTemplate from "./generated/templates/PopupTemplate.lit.js";
import PopupBlockLayer from "./generated/templates/PopupBlockLayerTemplate.lit.js";
import { getNextZIndex, getFocusedElement, isFocusedElementWithinNode } from "./popup-utils/PopupUtils.js";
import { addOpenedPopup, removeOpenedPopup } from "./popup-utils/OpenedPopupsRegistry.js";

// Styles
Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/ResponsivePopover.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isPhone } from "@ui5/webcomponents-base/dist/Device.js";
import { getNextZIndex } from "./popup-utils/PopupUtils.js";
import { getNextZIndex } from "@ui5/webcomponents-base/dist/util/PopupUtils.js";
import ResponsivePopoverTemplate from "./generated/templates/ResponsivePopoverTemplate.lit.js";
import Popover from "./Popover.js";
import Dialog from "./Dialog.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/Toast.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import { getNextZIndex } from "@ui5/webcomponents-base/dist/util/PopupUtils.js";
import ToastPlacement from "./types/ToastPlacement.js";
import { getNextZIndex } from "./popup-utils/PopupUtils.js";

// Template
import ToastTemplate from "./generated/templates/ToastTemplate.lit.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/popup-utils/PopoverRegistry.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isClickInRect } from "./PopupUtils.js";
import { isClickInRect } from "@ui5/webcomponents-base/dist/util/PopupUtils.js";
import { getOpenedPopups, addOpenedPopup, removeOpenedPopup } from "./OpenedPopupsRegistry.js";


Expand Down
82 changes: 9 additions & 73 deletions packages/main/src/popup-utils/PopupUtils.js
Original file line number Diff line number Diff line change
@@ -1,81 +1,17 @@
import getActiveElement from "@ui5/webcomponents-base/dist/util/getActiveElement.js";

let currentZIndex = 100;

const getFocusedElement = () => {
const element = getActiveElement();
return (element && typeof element.focus === "function") ? element : null;
};

const isFocusedElementWithinNode = node => {
const fe = getFocusedElement();

if (fe) {
return isNodeContainedWithin(node, fe);
}

return false;
};

const isNodeContainedWithin = (parent, child) => {
let currentNode = parent;

if (currentNode.shadowRoot) {
currentNode = Array.from(currentNode.shadowRoot.children).find(n => n.localName !== "style");
}

if (currentNode === child) {
return true;
}

const childNodes = currentNode.localName === "slot" ? currentNode.assignedNodes() : currentNode.children;

if (childNodes) {
return Array.from(childNodes).some(n => isNodeContainedWithin(n, child));
}
};

const isPointInRect = (x, y, rect) => {
return x >= rect.left && x <= rect.right
&& y >= rect.top && y <= rect.bottom;
};

const isClickInRect = (event, rect) => {
let x;
let y;

if (event.touches) {
const touch = event.touches[0];
x = touch.clientX;
y = touch.clientY;
} else {
x = event.clientX;
y = event.clientY;
}

return isPointInRect(x, y, rect);
};

const getClosedPopupParent = el => {
const parent = el.parentElement || (el.getRootNode && el.getRootNode().host);

if (parent && ((parent.openBy && parent.isUI5Element) || (parent.open && parent.isUI5Element) || parent === document.documentElement)) {
return parent;
}

return getClosedPopupParent(parent);
};


const getNextZIndex = () => {
currentZIndex += 2;
return currentZIndex;
};
import {
getFocusedElement,
isClickInRect,
getClosedPopupParent,
getNextZIndex,
getCurrentZIndex,
isFocusedElementWithinNode,
} from "@ui5/webcomponents-base/dist/util/PopupUtils.js";

export {
getFocusedElement,
isClickInRect,
getClosedPopupParent,
getNextZIndex,
getCurrentZIndex,
isFocusedElementWithinNode,
};

0 comments on commit df36b64

Please sign in to comment.