Skip to content
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

feat(framework): Make addCustomCSS dynamic #2083

Merged
merged 4 commits into from
Aug 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/base/src/RenderScheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,18 +142,21 @@ class RenderScheduler {
*
* Usage:
* reRenderAllUI5Elements() -> rerenders all components
* reRenderAllUI5Elements({tag: "ui5-button"}) -> re-renders only instances of ui5-button
* reRenderAllUI5Elements({rtlAware: true}) -> re-renders only rtlAware components
* reRenderAllUI5Elements({languageAware: true}) -> re-renders only languageAware components
* reRenderAllUI5Elements({rtlAware: true, languageAware: true}) -> re-renders components that are rtlAware or languageAware
* etc...
*
* @public
* @param {Object|undefined} filters - Object with keys that can be "rtlAware" or "languageAware"
*/
static reRenderAllUI5Elements(filters) {
registeredElements.forEach(element => {
const tag = element.constructor.getMetadata().getTag();
const rtlAware = isRtlAware(element.constructor);
const languageAware = element.constructor.getMetadata().isLanguageAware();
if (!filters || (filters.rtlAware && rtlAware) || (filters.languageAware && languageAware)) {
if (!filters || (filters.tag === tag) || (filters.rtlAware && rtlAware) || (filters.languageAware && languageAware)) {
RenderScheduler.renderDeferred(element);
}
});
Expand Down
23 changes: 12 additions & 11 deletions packages/base/src/UI5Element.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,6 @@ class UI5Element extends HTMLElement {
// Init Shadow Root
if (needsShadowDOM) {
this.attachShadow({ mode: "open" });

// IE11, Edge
if (window.ShadyDOM) {
createComponentStyleTag(this.constructor);
}

// Chrome
if (document.adoptedStyleSheets) {
const style = getConstructableStyle(this.constructor);
this.shadowRoot.adoptedStyleSheets = [style];
}
}

// Init StaticAreaItem only if needed
Expand Down Expand Up @@ -562,9 +551,21 @@ class UI5Element extends HTMLElement {
let styleToPrepend;
const renderResult = this.constructor.template(this);

// IE11, Edge
if (window.ShadyDOM) {
createComponentStyleTag(this.constructor);
}

// Chrome
if (document.adoptedStyleSheets) {
this.shadowRoot.adoptedStyleSheets = getConstructableStyle(this.constructor);
}

// FF, Safari
if (!document.adoptedStyleSheets && !window.ShadyDOM) {
styleToPrepend = getEffectiveStyle(this.constructor);
}

this.constructor.render(renderResult, this.shadowRoot, styleToPrepend, { eventContext: this });
}

Expand Down
35 changes: 28 additions & 7 deletions packages/base/src/theming/CustomStyle.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
const customCSSFor = {};
import RenderScheduler from "../RenderScheduler.js";
import EventProvider from "../EventProvider.js";

const addCustomCSS = (tag, css, ...rest) => {
if (rest.length) {
throw new Error("addCustomCSS no longer accepts theme specific CSS. new signature is `addCustomCSS(tag, css)`");
}
const eventProvider = new EventProvider();
const CUSTOM_CSS_CHANGE = "CustomCSSChange";

const attachCustomCSSChange = listener => {
eventProvider.attachEvent(CUSTOM_CSS_CHANGE, listener);
};

const detachCustomCSSChange = listener => {
eventProvider.detachEvent(CUSTOM_CSS_CHANGE, listener);
};

const fireCustomCSSChange = tag => {
return eventProvider.fireEvent(CUSTOM_CSS_CHANGE, tag);
};

const customCSSFor = {};

const addCustomCSS = (tag, css) => {
if (!customCSSFor[tag]) {
customCSSFor[tag] = [];
}

customCSSFor[tag].push(css);
fireCustomCSSChange(tag);

RenderScheduler.reRenderAllUI5Elements({ tag });
};

const getCustomCSS = tag => {
return customCSSFor[tag] ? customCSSFor[tag].join("") : "";
};

export { addCustomCSS, getCustomCSS };
export {
addCustomCSS,
getCustomCSS,
attachCustomCSSChange,
detachCustomCSSChange,
};
5 changes: 5 additions & 0 deletions packages/base/src/theming/createComponentStyleTag.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ import createStyleInHead from "../util/createStyleInHead.js";
import getEffectiveStyle from "./getEffectiveStyle.js";
import adaptCSSForIE from "./adaptCSSForIE.js";
import { ponyfillNeeded, schedulePonyfill } from "./CSSVarsPonyfill.js";
import { attachCustomCSSChange } from "./CustomStyle.js";

const IEStyleSet = new Set();

attachCustomCSSChange(tag => {
IEStyleSet.delete(tag);
});

const getStaticStyle = ElementClass => {
let componentStaticStyles = ElementClass.staticAreaStyles;
if (Array.isArray(componentStaticStyles)) {
Expand Down
22 changes: 13 additions & 9 deletions packages/base/src/theming/getConstructableStyle.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import getEffectiveStyle from "./getEffectiveStyle.js";
import { attachCustomCSSChange } from "./CustomStyle.js";

const constructableStyleMap = new Map();

attachCustomCSSChange(tag => {
constructableStyleMap.delete(tag);
});

/**
* Returns (and caches) a constructable style sheet for a web component class
* Note: Chrome
* @param ElementClass
* @returns {*}
*/
const getConstructableStyle = ElementClass => {
const tagName = ElementClass.getMetadata().getTag();
const styleContent = getEffectiveStyle(ElementClass);
if (constructableStyleMap.has(tagName)) {
return constructableStyleMap.get(tagName);
}
const tag = ElementClass.getMetadata().getTag();

const style = new CSSStyleSheet();
style.replaceSync(styleContent);
if (!constructableStyleMap.has(tag)) {
const styleContent = getEffectiveStyle(ElementClass);
const style = new CSSStyleSheet();
style.replaceSync(styleContent);
constructableStyleMap.set(tag, [style]);
}

constructableStyleMap.set(tagName, style);
return style;
return constructableStyleMap.get(tag);
};

export default getConstructableStyle;
19 changes: 15 additions & 4 deletions packages/base/src/theming/getEffectiveStyle.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import { getCustomCSS } from "./CustomStyle.js";
import { getCustomCSS, attachCustomCSSChange } from "./CustomStyle.js";
import getStylesString from "./getStylesString.js";

const effectiveStyleMap = new Map();

attachCustomCSSChange(tag => {
effectiveStyleMap.delete(tag);
});

const getEffectiveStyle = ElementClass => {
const tag = ElementClass.getMetadata().getTag();
const customStyle = getCustomCSS(tag) || "";

const builtInStyles = getStylesString(ElementClass.styles);
return `${builtInStyles} ${customStyle}`;
if (!effectiveStyleMap.has(tag)) {
const customStyle = getCustomCSS(tag) || "";
const builtInStyles = getStylesString(ElementClass.styles);
const effectiveStyle = `${builtInStyles} ${customStyle}`;
effectiveStyleMap.set(tag, effectiveStyle);
}

return effectiveStyleMap.get(tag);
};

export default getEffectiveStyle;
4 changes: 3 additions & 1 deletion packages/main/bundle.es5.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { getFirstDayOfWeek } from "@ui5/webcomponents-base/dist/config/FormatSet
import { getRegisteredNames as getIconNames } from "@ui5/webcomponents-base/dist/SVGIconRegistry.js";
import applyDirection from "@ui5/webcomponents-base/dist/locale/applyDirection.js";
import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
import { addCustomCSS } from "@ui5/webcomponents-base/dist/Theming.js";

const configuration = {
getAnimationMode,
setAnimationMode,
Expand All @@ -25,7 +27,7 @@ const configuration = {
};
export {
configuration,
getIconNames,
applyDirection,
ResizeHandler,
addCustomCSS,
};
3 changes: 2 additions & 1 deletion packages/main/bundle.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ import { getFirstDayOfWeek } from "@ui5/webcomponents-base/dist/config/FormatSet
import { getRegisteredNames as getIconNames } from "@ui5/webcomponents-base/dist/SVGIconRegistry.js";
import applyDirection from "@ui5/webcomponents-base/dist/locale/applyDirection.js";
import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
import { addCustomCSS } from "@ui5/webcomponents-base/dist/Theming";
window["sap-ui-webcomponents-bundle"] = {
configuration : {
getAnimationMode,
Expand All @@ -109,8 +110,8 @@ window["sap-ui-webcomponents-bundle"] = {
getRTL,
getFirstDayOfWeek,
},
getIconNames,
getLocaleData,
applyDirection,
ResizeHandler,
addCustomCSS,
};
53 changes: 53 additions & 0 deletions packages/main/test/pages/CustomCSS.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta charset="utf-8">

<title>Button</title>

<script data-ui5-config type="application/json">
{
"language": "EN",
"noConflict": {
"events": ["click"]
},
"calendarType": "Islamic"
}
</script>

<script src="../../webcomponentsjs/webcomponents-loader.js"></script>
<script src="../../resources/bundle.esm.js" type="module"></script>
<script nomodule src="../../resources/bundle.es5.js"></script>

</head>

<body style="background-color: var(--sapBackgroundColor);">

<ui5-select id="select">
<ui5-option>1</ui5-option>
<ui5-option>2</ui5-option>
</ui5-select>

<br />
<br />

<ui5-button id="btn">Apply custom css and rerender</ui5-button>

<br />
<br />

<script>
const applyCustomCSSAndRerender = function() {
window["sap-ui-webcomponents-bundle"].addCustomCSS("ui5-select", ".ui5-select-root { background-color: red; } ");
};

document
.getElementById("btn")
.addEventListener("click", applyCustomCSSAndRerender);

</script>

</body>
</html>