276 lines
12 KiB
TypeScript
Raw Normal View History

2021-03-02 01:05:15 +01:00
2021-03-04 13:56:51 +01:00
import { useState, useReducer ,useEffect, memo } from "react";
2021-03-02 01:05:15 +01:00
import type { ReactNode } from "react";
2021-03-02 23:48:31 +01:00
import { useKcTranslation } from "../i18n/useKcTranslation";
import { kcContext } from "../kcContext";
2021-03-02 01:05:15 +01:00
import { assert } from "evt/tools/typeSafety/assert";
import { cx } from "tss-react";
2021-03-02 23:48:31 +01:00
import { useKcLanguageTag } from "../i18n/useKcLanguageTag";
import type { KcLanguageTag } from "../i18n/KcLanguageTag";
import { getKcLanguageTagLabel } from "../i18n/KcLanguageTag";
2021-03-02 01:05:15 +01:00
import { useCallbackFactory } from "powerhooks";
2021-03-04 13:56:51 +01:00
import { appendHead } from "../tools/appendHead";
2021-03-02 01:05:15 +01:00
import { join as pathJoin } from "path";
import { useConstCallback } from "powerhooks";
2021-03-02 23:48:31 +01:00
import type { KcTemplateProperties } from "./KcProperties";
import { defaultKcTemplateProperties } from "./KcProperties";
2021-03-02 12:17:24 +01:00
2021-03-02 23:48:31 +01:00
export type TemplateProps = {
kcProperties: KcTemplateProperties;
2021-03-02 12:17:24 +01:00
displayInfo?: boolean;
2021-03-02 22:48:36 +01:00
displayMessage?: boolean;
displayRequiredFields?: boolean;
displayWide?: boolean;
showAnotherWayIfPresent?: boolean;
2021-03-02 01:05:15 +01:00
headerNode: ReactNode;
showUsernameNode: ReactNode;
formNode: ReactNode;
displayInfoNode: ReactNode;
};
2021-03-02 22:48:36 +01:00
2021-03-02 23:48:31 +01:00
export const Template = memo((props: TemplateProps) => {
2021-03-02 01:05:15 +01:00
const {
displayInfo = false,
displayMessage = true,
displayRequiredFields = false,
displayWide = false,
showAnotherWayIfPresent = true,
2021-03-02 23:48:31 +01:00
kcProperties = {},
2021-03-02 01:05:15 +01:00
headerNode,
showUsernameNode,
formNode,
displayInfoNode
} = props;
2021-03-02 23:48:31 +01:00
const { t } = useKcTranslation();
2021-03-02 01:05:15 +01:00
2021-03-02 23:48:31 +01:00
Object.assign(kcProperties, defaultKcTemplateProperties);
2021-03-02 22:48:36 +01:00
2021-03-02 23:48:31 +01:00
const { kcLanguageTag, setKcLanguageTag } = useKcLanguageTag();
2021-03-02 01:05:15 +01:00
const onChangeLanguageClickFactory = useCallbackFactory(
2021-03-02 23:48:31 +01:00
([languageTag]: [KcLanguageTag]) =>
setKcLanguageTag(languageTag)
2021-03-02 01:05:15 +01:00
);
const onTryAnotherWayClick = useConstCallback(() => {
document.forms["kc-select-try-another-way-form" as never].submit();
return false;
});
2021-03-02 23:48:31 +01:00
const [{ realm, locale, auth, url, message, isAppInitiatedAction }] = useState(() => (
assert(kcContext !== undefined, "App is not currently being served by KeyCloak"),
kcContext
));
2021-03-02 01:05:15 +01:00
2021-03-04 13:56:51 +01:00
const [isExtraCssLoaded, setExtraCssLoaded] = useReducer(() => true, false);
2021-03-02 01:05:15 +01:00
useEffect(() => {
2021-03-04 13:56:51 +01:00
let isUnmounted = false;
2021-03-02 01:05:15 +01:00
2021-03-04 13:56:51 +01:00
Promise.all(
[
...(kcProperties.stylesCommon ?? []).map(relativePath => pathJoin(url.resourcesCommonPath, relativePath)),
...(kcProperties.styles ?? []).map(relativePath => pathJoin(url.resourcesPath, relativePath))
].map(href => appendHead({
"type": "css",
href
}))).then(() => {
if (isUnmounted) {
return;
}
setExtraCssLoaded();
});
2021-03-02 01:05:15 +01:00
2021-03-02 23:48:31 +01:00
kcProperties.scripts?.forEach(
2021-03-04 13:56:51 +01:00
relativePath => appendHead({
"type": "javascript",
"src": pathJoin(url.resourcesPath, relativePath)
})
2021-03-02 01:05:15 +01:00
);
2021-03-03 03:17:56 +01:00
document.getElementsByTagName("html")[0]
.classList
.add(cx(kcProperties.kcHtmlClass));
2021-03-04 13:56:51 +01:00
return () => { isUnmounted = true; };
2021-03-02 01:05:15 +01:00
}, []);
2021-03-04 13:56:51 +01:00
if (!isExtraCssLoaded) {
return null;
}
2021-03-02 01:05:15 +01:00
return (
2021-03-02 23:48:31 +01:00
<div className={cx(kcProperties.kcLoginClass)}>
2021-03-02 01:05:15 +01:00
2021-03-02 23:48:31 +01:00
<div id="kc-header" className={cx(kcProperties.kcHeaderClass)}>
<div id="kc-header-wrapper" className={cx(kcProperties.kcHeaderWrapperClass)}>
2021-03-02 01:05:15 +01:00
{t("loginTitleHtml", realm.displayNameHtml)}
</div>
</div>
2021-03-04 13:56:51 +01:00
<div className={cx(kcProperties.kcFormCardClass, displayWide && kcProperties.kcFormCardAccountClass)}>
2021-03-02 23:48:31 +01:00
<header className={cx(kcProperties.kcFormHeaderClass)}>
2021-03-02 01:05:15 +01:00
{
(
realm.internationalizationEnabled &&
(assert(locale !== undefined), true) &&
locale.supported.length > 1
) &&
<div id="kc-locale">
2021-03-02 23:48:31 +01:00
<div id="kc-locale-wrapper" className={cx(kcProperties.kcLocaleWrapperClass)}>
2021-03-02 01:05:15 +01:00
<div className="kc-dropdown" id="kc-locale-dropdown">
<a href="#" id="kc-current-locale-link">
2021-03-02 23:48:31 +01:00
{getKcLanguageTagLabel(kcLanguageTag)}
2021-03-02 01:05:15 +01:00
</a>
<ul>
{
locale.supported.map(
({ languageTag }) =>
<li className="kc-dropdown-item">
<a href="#" onClick={onChangeLanguageClickFactory(languageTag)}>
2021-03-02 23:48:31 +01:00
{getKcLanguageTagLabel(languageTag)}
2021-03-02 01:05:15 +01:00
</a>
</li>
)
}
</ul>
</div>
</div>
</div>
}
{
2021-03-04 13:56:51 +01:00
!(
2021-03-02 01:05:15 +01:00
auth !== undefined &&
auth.showUsername &&
!auth.showResetCredentials
) ?
(
displayRequiredFields ?
(
2021-03-02 23:48:31 +01:00
<div className={cx(kcProperties.kcContentWrapperClass)}>
<div className={cx(kcProperties.kcLabelWrapperClass, "subtitle")}>
2021-03-02 01:05:15 +01:00
<span className="subtitle">
<span className="required">*</span>
{t("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 ? (
2021-03-02 23:48:31 +01:00
<div className={cx(kcProperties.kcContentWrapperClass)}>
<div className={cx(kcProperties.kcLabelWrapperClass, "subtitle")}>
2021-03-02 01:05:15 +01:00
<span className="subtitle"><span className="required">*</span> {t("requiredFields")}</span>
</div>
<div className="col-md-10">
{showUsernameNode}
2021-03-02 23:48:31 +01:00
<div className={cx(kcProperties.kcFormGroupClass)}>
2021-03-02 01:05:15 +01:00
<div id="kc-username">
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl}>
<div className="kc-login-tooltip">
2021-03-02 23:48:31 +01:00
<i className={cx(kcProperties.kcResetFlowIcon)}></i>
2021-03-02 01:05:15 +01:00
<span className="kc-tooltip-text">{t("restartLoginTooltip")}</span>
</div>
</a>
</div>
</div>
</div>
</div>
) : (
<>
{showUsernameNode}
2021-03-02 23:48:31 +01:00
<div className={cx(kcProperties.kcFormGroupClass)}>
2021-03-02 01:05:15 +01:00
<div id="kc-username">
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl}>
<div className="kc-login-tooltip">
2021-03-02 23:48:31 +01:00
<i className={cx(kcProperties.kcResetFlowIcon)}></i>
2021-03-02 01:05:15 +01:00
<span className="kc-tooltip-text">{t("restartLoginTooltip")}</span>
</div>
</a>
</div>
</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={cx("alert", `alert-${message.type}`)}>
2021-03-02 23:48:31 +01:00
{message.type === "success" && <span className={cx(kcProperties.kcFeedbackSuccessIcon)}></span>}
{message.type === "warning" && <span className={cx(kcProperties.kcFeedbackWarningIcon)}></span>}
{message.type === "error" && <span className={cx(kcProperties.kcFeedbackErrorIcon)}></span>}
{message.type === "info" && <span className={cx(kcProperties.kcFeedbackInfoIcon)}></span>}
2021-03-02 01:05:15 +01:00
<span className="kc-feedback-text">{message.summary}</span>
</div>
}
{formNode}
{
(
auth !== undefined &&
auth.showTryAnotherWayLink &&
showAnotherWayIfPresent
) &&
2021-03-02 23:48:31 +01:00
<form id="kc-select-try-another-way-form" action={url.loginAction} method="post" className={cx(displayWide && kcProperties.kcContentWrapperClass)} >
<div className={cx(displayWide && [kcProperties.kcFormSocialAccountContentClass, kcProperties.kcFormSocialAccountClass])} >
<div className={cx(kcProperties.kcFormGroupClass)}>
2021-03-02 01:05:15 +01:00
<input type="hidden" name="tryAnotherWay" value="on" />
<a href="#" id="try-another-way" onClick={onTryAnotherWayClick}>{t("doTryAnotherWay")}</a>
</div>
</div >
</form>
}
{
displayInfo &&
2021-03-02 23:48:31 +01:00
<div id="kc-info" className={cx(kcProperties.kcSignUpClass)}>
<div id="kc-info-wrapper" className={cx(kcProperties.kcInfoAreaWrapperClass)}>
2021-03-02 01:05:15 +01:00
{displayInfoNode}
</div>
</div>
}
2021-03-02 23:48:31 +01:00
</div>
</div>
</div>
</div>
2021-03-02 01:05:15 +01:00
);
2021-03-02 22:48:36 +01:00
});