import React, { useReducer, useEffect } from "react"; import { assert } from "./tools/assert"; import { headInsert } from "./tools/headInsert"; import { pathJoin } from "./bin/tools/pathJoin"; import { clsx } from "./tools/clsx"; import type { TemplateProps } from "./KcProps"; import type { KcContextBase } from "./kcContext/KcContextBase"; import type { I18nBase } from "./i18n"; export default function Template(props: TemplateProps) { const { displayInfo = false, displayMessage = true, displayRequiredFields = false, displayWide = false, showAnotherWayIfPresent = true, headerNode, showUsernameNode = null, formNode, infoNode = null, kcContext, i18n, doFetchDefaultThemeResources, stylesCommon, styles, scripts, kcHtmlClass } = props; const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n; const { realm, locale, auth, url, message, isAppInitiatedAction } = kcContext; const { isReady } = usePrepareTemplate({ doFetchDefaultThemeResources, stylesCommon, styles, scripts, url, kcHtmlClass }); if (!isReady) { return null; } return (
{msg("loginTitleHtml", realm.displayNameHtml)}
{realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && (
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} {labelBySupportedLanguageTag[currentLanguageTag]}
)} {!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? ( displayRequiredFields ? (
* {msg("requiredFields")}

{headerNode}

) : (

{headerNode}

) ) : displayRequiredFields ? (
* {msg("requiredFields")}
{showUsernameNode}
{msg("restartLoginTooltip")}
) : ( <> {showUsernameNode}
{msg("restartLoginTooltip")}
)}
{/* App-initiated actions should not see warning messages about the need to complete the action during login. */} {displayMessage && message !== undefined && (message.type !== "warning" || !isAppInitiatedAction) && (
{message.type === "success" && } {message.type === "warning" && } {message.type === "error" && } {message.type === "info" && }
)} {formNode} {auth !== undefined && auth.showTryAnotherWayLink && showAnotherWayIfPresent && (
)} {displayInfo && (
{infoNode}
)}
); } export function usePrepareTemplate(params: { doFetchDefaultThemeResources: boolean; stylesCommon: string | readonly string[] | undefined; styles: string | readonly string[] | undefined; scripts: string | readonly string[] | undefined; url: { resourcesCommonPath: string; resourcesPath: string; }; kcHtmlClass: string | readonly string[] | undefined; }) { const { doFetchDefaultThemeResources, stylesCommon, styles, url, scripts, kcHtmlClass } = params; const [isReady, setReady] = useReducer(() => true, !doFetchDefaultThemeResources); useEffect(() => { if (!doFetchDefaultThemeResources) { return; } let isUnmounted = false; const toArr = (x: string | readonly string[] | undefined) => (typeof x === "string" ? x.split(" ") : x ?? []); Promise.all( [ ...toArr(stylesCommon).map(relativePath => pathJoin(url.resourcesCommonPath, relativePath)), ...toArr(styles).map(relativePath => pathJoin(url.resourcesPath, relativePath)) ] .reverse() .map(href => headInsert({ "type": "css", href, "position": "prepend" }) ) ).then(() => { if (isUnmounted) { return; } setReady(); }); toArr(scripts).forEach(relativePath => headInsert({ "type": "javascript", "src": pathJoin(url.resourcesPath, relativePath) }) ); return () => { isUnmounted = true; }; }, [kcHtmlClass]); useEffect(() => { if (kcHtmlClass === undefined) { return; } const htmlClassList = document.getElementsByTagName("html")[0].classList; const tokens = clsx(kcHtmlClass).split(" "); htmlClassList.add(...tokens); return () => { htmlClassList.remove(...tokens); }; }, [kcHtmlClass]); return { isReady }; }