keycloak_theme/src/login/Template.tsx

295 lines
15 KiB
TypeScript
Raw Normal View History

2024-05-10 22:15:33 +02:00
import { useEffect } from "react";
2023-03-17 20:40:29 +01:00
import { assert } from "keycloakify/tools/assert";
import { clsx } from "keycloakify/tools/clsx";
2023-03-21 05:27:31 +01:00
import { type TemplateProps } from "keycloakify/login/TemplateProps";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
2024-05-10 22:15:33 +02:00
import { createUseInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
import { createUseInsertLinkTags } from "keycloakify/tools/useInsertLinkTags";
import { useSetClassName } from "keycloakify/tools/useSetClassName";
2023-03-19 14:48:01 +01:00
import type { KcContext } from "./kcContext";
import type { I18n } from "./i18n";
2023-03-17 20:40:29 +01:00
2024-05-10 22:15:33 +02:00
const { useInsertLinkTags } = createUseInsertLinkTags();
const { useInsertScriptTags } = createUseInsertScriptTags();
2023-03-17 20:40:29 +01:00
export default function Template(props: TemplateProps<KcContext, I18n>) {
const {
displayInfo = false,
displayMessage = true,
displayRequiredFields = false,
headerNode,
showUsernameNode = null,
socialProvidersNode = null,
infoNode = null,
2024-05-11 00:05:58 +02:00
documentTitle,
kcContext,
i18n,
2023-03-17 20:40:29 +01:00
doUseDefaultCss,
2023-03-21 02:36:13 +01:00
classes,
children
} = props;
2023-03-21 05:27:31 +01:00
const { getClassName } = useGetClassName({ doUseDefaultCss, classes });
2023-03-17 20:40:29 +01:00
const { msg, msgStr, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n;
const { realm, locale, auth, url, message, isAppInitiatedAction, authenticationSession, scripts } = kcContext;
2024-05-10 22:15:33 +02:00
useEffect(() => {
2024-05-11 00:05:58 +02:00
document.title = documentTitle ?? msgStr("loginTitle", kcContext.realm.displayName);
2024-05-10 22:15:33 +02:00
}, []);
useSetClassName({
"qualifiedName": "html",
"className": getClassName("kcHtmlClass")
});
useSetClassName({
"qualifiedName": "body",
"className": getClassName("kcBodyClass")
});
useEffect(() => {
const { currentLanguageTag } = locale ?? {};
if (currentLanguageTag === undefined) {
return;
}
const html = document.querySelector("html");
assert(html !== null);
html.lang = currentLanguageTag;
}, []);
const { areAllStyleSheetsLoaded } = useInsertLinkTags({
"hrefs": !doUseDefaultCss
? []
: [
`${url.resourcesCommonPath}/node_modules/@patternfly/patternfly/patternfly.min.css`,
`${url.resourcesCommonPath}/node_modules/patternfly/dist/css/patternfly.min.css`,
`${url.resourcesCommonPath}/node_modules/patternfly/dist/css/patternfly-additions.min.css`,
`${url.resourcesCommonPath}/lib/pficon/pficon.css`,
`${url.resourcesPath}/css/login.css`
2024-05-10 22:15:33 +02:00
]
});
const { insertScriptTags } = useInsertScriptTags({
"scriptTags": [
{
"type": "module",
"src": `${url.resourcesPath}/js/menu-button-links.js`
},
...(authenticationSession === undefined
? []
: [
{
"type": "module",
"textContent": [
`import { checkCookiesAndSetTimer } from "${url.resourcesPath}/js/authChecker.js";`,
``,
`checkCookiesAndSetTimer(`,
` "${authenticationSession.authSessionId}",`,
` "${authenticationSession.tabId}",`,
` "${url.ssoLoginInOtherTabsUrl}"`,
`);`
].join("\n")
} as const
]),
...scripts.map(
script =>
({
"type": "text/javascript",
"src": script
} as const)
)
2024-05-10 22:15:33 +02:00
]
2023-02-27 10:39:22 +01:00
});
2024-05-10 22:15:33 +02:00
useEffect(() => {
if (areAllStyleSheetsLoaded) {
insertScriptTags();
}
}, [areAllStyleSheetsLoaded]);
if (!areAllStyleSheetsLoaded) {
return null;
}
return (
2023-03-17 20:40:29 +01:00
<div className={getClassName("kcLoginClass")}>
<div id="kc-header" className={getClassName("kcHeaderClass")}>
<div id="kc-header-wrapper" className={getClassName("kcHeaderWrapperClass")}>
{msg("loginTitleHtml", realm.displayNameHtml)}
</div>
</div>
<div className={getClassName("kcFormCardClass")}>
2023-03-17 20:40:29 +01:00
<header className={getClassName("kcFormHeaderClass")}>
{realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && (
<div className={getClassName("kcLocaleMainClass")} id="kc-locale">
2023-03-17 20:40:29 +01:00
<div id="kc-locale-wrapper" className={getClassName("kcLocaleWrapperClass")}>
<div id="kc-locale-dropdown" className={clsx("menu-button-links", getClassName("kcLocaleDropDownClass"))}>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a href="#" id="kc-current-locale-link">
{labelBySupportedLanguageTag[currentLanguageTag]}
</a>
<ul>
{locale.supported.map(({ languageTag }) => (
<li key={languageTag} className="kc-dropdown-item">
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
2023-02-25 20:11:55 +01:00
<a href="#" onClick={() => changeLocale(languageTag)}>
{labelBySupportedLanguageTag[languageTag]}
</a>
</li>
))}
</ul>
<button
tabIndex={1}
id="kc-current-locale-link"
aria-label={msgStr("languages" as any)}
aria-haspopup={true}
aria-expanded={false}
aria-controls="language-switch1"
>
{labelBySupportedLanguageTag[currentLanguageTag]}
</button>
<ul
role="menu"
tabIndex={-1}
aria-labelledby="kc-current-locale-link"
aria-activedescendant=""
id="language-switch1"
className={getClassName("kcLocaleListClass")}
>
{locale.supported.map(({ languageTag }, i) => (
<li key={languageTag} className={getClassName("kcLocaleListItemClass")} role="none">
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a
role="menuitem"
id={`language-${i}`}
className={getClassName("kcLocaleItemClass")}
href="#"
onClick={() => changeLocale(languageTag)}
>
{labelBySupportedLanguageTag[languageTag]}
</a>
</li>
))}
</ul>
</div>
</div>
</div>
)}
{!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? (
displayRequiredFields ? (
2023-03-17 20:40:29 +01:00
<div className={getClassName("kcContentWrapperClass")}>
<div className={clsx(getClassName("kcLabelWrapperClass"), "subtitle")}>
<span className="subtitle">
<span className="required">*</span>
{msg("requiredFields")}
</span>
</div>
<div className="col-md-10">
<h1 id="kc-page-title">{headerNode}</h1>
</div>
</div>
) : (
<h1 id="kc-page-title">{headerNode}</h1>
)
) : displayRequiredFields ? (
2023-03-17 20:40:29 +01:00
<div className={getClassName("kcContentWrapperClass")}>
<div className={clsx(getClassName("kcLabelWrapperClass"), "subtitle")}>
<span className="subtitle">
<span className="required">*</span> {msg("requiredFields")}
</span>
</div>
<div className="col-md-10">
{showUsernameNode}
<div id="kc-username" className={getClassName("kcFormGroupClass")}>
<label id="kc-attempted-username">{auth.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl} aria-label={msgStr("restartLoginTooltip")}>
<div className="kc-login-tooltip">
2023-03-17 20:40:29 +01:00
<i className={getClassName("kcResetFlowIcon")}></i>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
</div>
</div>
</div>
) : (
<>
{showUsernameNode}
<div id="kc-username" className={getClassName("kcFormGroupClass")}>
<label id="kc-attempted-username">{auth.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl} aria-label={msgStr("restartLoginTooltip")}>
<div className="kc-login-tooltip">
<i className={getClassName("kcResetFlowIcon")}></i>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
</div>
</>
)}
</header>
<div id="kc-content">
<div id="kc-content-wrapper">
{/* App-initiated actions should not see warning messages about the need to complete the action during login. */}
{displayMessage && message !== undefined && (message.type !== "warning" || !isAppInitiatedAction) && (
<div
className={clsx(
`alert-${message.type}`,
getClassName("kcAlertClass"),
`pf-m-${message?.type === "error" ? "danger" : message.type}`
)}
>
<div className="pf-c-alert__icon">
{message.type === "success" && <span className={getClassName("kcFeedbackSuccessIcon")}></span>}
{message.type === "warning" && <span className={getClassName("kcFeedbackWarningIcon")}></span>}
{message.type === "error" && <span className={getClassName("kcFeedbackErrorIcon")}></span>}
{message.type === "info" && <span className={getClassName("kcFeedbackInfoIcon")}></span>}
</div>
<span
className={getClassName("kcAlertTitleClass")}
dangerouslySetInnerHTML={{
"__html": message.summary
}}
/>
</div>
)}
2023-03-21 02:36:13 +01:00
{children}
{auth !== undefined && auth.showTryAnotherWayLink && (
<form id="kc-select-try-another-way-form" action={url.loginAction} method="post">
<div className={getClassName("kcFormGroupClass")}>
2023-03-17 20:40:29 +01:00
<div className={getClassName("kcFormGroupClass")}>
<input type="hidden" name="tryAnotherWay" value="on" />
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
2023-02-25 20:11:55 +01:00
<a
href="#"
id="try-another-way"
onClick={() => {
document.forms["kc-select-try-another-way-form" as never].submit();
return false;
}}
>
{msg("doTryAnotherWay")}
</a>
</div>
</div>
</form>
)}
{socialProvidersNode}
{displayInfo && (
2023-03-17 20:40:29 +01:00
<div id="kc-info" className={getClassName("kcSignUpClass")}>
<div id="kc-info-wrapper" className={getClassName("kcInfoAreaWrapperClass")}>
{infoNode}
</div>
</div>
)}
</div>
</div>
</div>
</div>
);
}