diff --git a/client-src/overlay.js b/client-src/overlay.js index 9c92e88b67..ca506c8ac8 100644 --- a/client-src/overlay.js +++ b/client-src/overlay.js @@ -3,6 +3,15 @@ import ansiHTML from "ansi-html-community"; import { encode } from "html-entities"; +import { + containerStyle, + dismissButtonStyle, + headerStyle, + iframeStyle, + msgStyles, + msgTextStyle, + msgTypeStyle, +} from "./overlay/styles.js"; const colors = { reset: ["transparent", "transparent"], @@ -28,6 +37,17 @@ let overlayTrustedTypesPolicy; ansiHTML.setColors(colors); +/** + * + * @param {HTMLElement} element + * @param {CSSStyleDeclaration} style + */ +function applyStyle(element, style) { + Object.keys(style).forEach((prop) => { + element.style[prop] = style[prop]; + }); +} + /** * @param {string | null} trustedTypesPolicyName */ @@ -45,15 +65,7 @@ function createContainer(trustedTypesPolicyName) { iframeContainerElement = document.createElement("iframe"); iframeContainerElement.id = "webpack-dev-server-client-overlay"; iframeContainerElement.src = "about:blank"; - iframeContainerElement.style.position = "fixed"; - iframeContainerElement.style.left = 0; - iframeContainerElement.style.top = 0; - iframeContainerElement.style.right = 0; - iframeContainerElement.style.bottom = 0; - iframeContainerElement.style.width = "100vw"; - iframeContainerElement.style.height = "100vh"; - iframeContainerElement.style.border = "none"; - iframeContainerElement.style.zIndex = 9999999999; + applyStyle(iframeContainerElement, iframeStyle); iframeContainerElement.onload = () => { containerElement = /** @type {Document} */ @@ -61,48 +73,27 @@ function createContainer(trustedTypesPolicyName) { /** @type {HTMLIFrameElement} */ (iframeContainerElement).contentDocument ).createElement("div"); + containerElement.id = "webpack-dev-server-client-overlay-div"; - containerElement.style.position = "fixed"; - containerElement.style.boxSizing = "border-box"; - containerElement.style.left = 0; - containerElement.style.top = 0; - containerElement.style.right = 0; - containerElement.style.bottom = 0; - containerElement.style.width = "100vw"; - containerElement.style.height = "100vh"; - containerElement.style.backgroundColor = "rgba(0, 0, 0, 0.85)"; - containerElement.style.color = "#E8E8E8"; - containerElement.style.fontFamily = "Menlo, Consolas, monospace"; - containerElement.style.fontSize = "large"; - containerElement.style.padding = "2rem"; - containerElement.style.lineHeight = "1.2"; - containerElement.style.whiteSpace = "pre-wrap"; - containerElement.style.overflow = "auto"; - - const headerElement = document.createElement("span"); + applyStyle(containerElement, containerStyle); + + const headerElement = document.createElement("div"); headerElement.innerText = "Compiled with problems:"; + applyStyle(headerElement, headerStyle); const closeButtonElement = document.createElement("button"); - closeButtonElement.innerText = "X"; - closeButtonElement.style.background = "transparent"; - closeButtonElement.style.border = "none"; - closeButtonElement.style.fontSize = "20px"; - closeButtonElement.style.fontWeight = "bold"; - closeButtonElement.style.color = "white"; - closeButtonElement.style.cursor = "pointer"; - closeButtonElement.style.cssFloat = "right"; - // @ts-ignore - closeButtonElement.style.styleFloat = "right"; + applyStyle(closeButtonElement, dismissButtonStyle); + + closeButtonElement.innerText = "×"; + closeButtonElement.ariaLabel = "Dismiss"; closeButtonElement.addEventListener("click", () => { hide(); }); containerElement.appendChild(headerElement); containerElement.appendChild(closeButtonElement); - containerElement.appendChild(document.createElement("br")); - containerElement.appendChild(document.createElement("br")); /** @type {Document} */ ( @@ -200,26 +191,29 @@ function show(type, messages, trustedTypesPolicyName) { ensureOverlayExists(() => { messages.forEach((message) => { const entryElement = document.createElement("div"); - const typeElement = document.createElement("span"); + const msgStyle = type === "warning" ? msgStyles.warning : msgStyles.error; + applyStyle(entryElement, { + ...msgStyle, + padding: "1rem 1rem 1.5rem 1rem", + }); + + const typeElement = document.createElement("div"); const { header, body } = formatProblem(type, message); typeElement.innerText = header; - typeElement.style.color = `#${colors.red}`; + applyStyle(typeElement, msgTypeStyle); // Make it look similar to our terminal. const text = ansiHTML(encode(body)); const messageTextNode = document.createElement("div"); + applyStyle(messageTextNode, msgTextStyle); messageTextNode.innerHTML = overlayTrustedTypesPolicy ? overlayTrustedTypesPolicy.createHTML(text) : text; entryElement.appendChild(typeElement); - entryElement.appendChild(document.createElement("br")); - entryElement.appendChild(document.createElement("br")); entryElement.appendChild(messageTextNode); - entryElement.appendChild(document.createElement("br")); - entryElement.appendChild(document.createElement("br")); /** @type {HTMLDivElement} */ (containerElement).appendChild(entryElement); diff --git a/client-src/overlay/styles.js b/client-src/overlay/styles.js new file mode 100644 index 0000000000..4786c1b62e --- /dev/null +++ b/client-src/overlay/styles.js @@ -0,0 +1,89 @@ +// styles are inspired by `react-error-overlay` + +const msgStyles = { + error: { + backgroundColor: "rgba(206, 17, 38, 0.1)", + color: "#fccfcf", + }, + warning: { + backgroundColor: "rgba(251, 245, 180, 0.1)", + color: "#fbf5b4", + }, +}; + +const iframeStyle = { + position: "fixed", + top: 0, + left: 0, + right: 0, + bottom: 0, + width: "100vw", + height: "100vh", + border: "none", + "z-index": 9999999999, +}; + +const containerStyle = { + position: "fixed", + boxSizing: "border-box", + left: 0, + top: 0, + right: 0, + bottom: 0, + width: "100vw", + height: "100vh", + fontSize: "large", + padding: "2rem 2rem 4rem 2rem", + lineHeight: "1.2", + whiteSpace: "pre-wrap", + overflow: "auto", + backgroundColor: "rgba(0, 0, 0, 0.9)", + color: "white", +}; + +const headerStyle = { + color: "#e83b46", + fontSize: "2em", + whiteSpace: "pre-wrap", + fontFamily: "sans-serif", + margin: "0 2rem 2rem 0", + flex: "0 0 auto", + maxHeight: "50%", + overflow: "auto", +}; + +const dismissButtonStyle = { + color: "#ffffff", + lineHeight: "1rem", + fontSize: "1.5rem", + padding: "1rem", + cursor: "pointer", + position: "absolute", + right: 0, + top: 0, + backgroundColor: "transparent", + border: "none", +}; + +const msgTypeStyle = { + color: "#e83b46", + fontSize: "1.2em", + marginBottom: "1rem", + fontFamily: "sans-serif", +}; + +const msgTextStyle = { + lineHeight: "1.5", + fontSize: "1rem", + fontFamily: "Menlo, Consolas, monospace", +}; + +export { + msgStyles, + iframeStyle, + containerStyle, + headerStyle, + dismissButtonStyle, + msgTypeStyle, + msgTextStyle, +};