keycloakify 6.11.9 → 6.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -7
- package/bin/create-keycloak-email-directory.js +1 -1
- package/bin/download-builtin-keycloak-theme.js +1 -1
- package/bin/keycloakify/BuildOptions.d.ts +2 -2
- package/bin/keycloakify/BuildOptions.js.map +1 -1
- package/bin/keycloakify/generateFtl/generateFtl.d.ts +2 -2
- package/bin/keycloakify/generateJavaStackFiles.d.ts +1 -1
- package/bin/keycloakify/generateKeycloakThemeResources.d.ts +1 -1
- package/bin/keycloakify/generateKeycloakThemeResources.js +1 -1
- package/bin/keycloakify/generateStartKeycloakTestingContainer.d.ts +1 -1
- package/bin/keycloakify/keycloakify.js +1 -1
- package/bin/keycloakify/replacers/replaceImportsFromStaticInJsCode.d.ts +1 -1
- package/bin/keycloakify/replacers/replaceImportsInCssCode.d.ts +1 -1
- package/bin/keycloakify/replacers/replaceImportsInInlineCssCode.d.ts +1 -1
- package/bin/promptKeycloakVersion.js +1 -1
- package/bin/tools/NpmModuleVersion.d.ts +1 -1
- package/bin/tools/cliOptions.d.ts +1 -1
- package/bin/tools/deflate.js +1 -1
- package/bin/tools/downloadAndUnzip.js +51 -44
- package/bin/tools/downloadAndUnzip.js.map +1 -1
- package/bin/tools/grant-exec-perms.js +3 -2
- package/bin/tools/grant-exec-perms.js.map +1 -1
- package/bin/tools/jar.d.ts +1 -1
- package/bin/tools/jar.js +1 -1
- package/bin/tools/logger.d.ts +2 -2
- package/bin/tools/octokit-addons/getLatestsSemVersionedTag.js +34 -27
- package/bin/tools/octokit-addons/getLatestsSemVersionedTag.js.map +1 -1
- package/bin/tools/octokit-addons/listTags.js +1 -1
- package/bin/tools/transformCodebase.d.ts +1 -1
- package/bin/tools/walk.js +1 -1
- package/bin/tools/zip.d.ts +2 -2
- package/bin/tools/zip.js +1 -1
- package/bin/tsconfig.tsbuildinfo +1 -1
- package/lib/KcApp.d.ts +6 -0
- package/lib/{components/KcApp.js → KcApp.js} +25 -26
- package/lib/KcApp.js.map +1 -0
- package/lib/{components/KcProps.d.ts → KcProps.d.ts} +30 -6
- package/lib/{components/KcProps.js → KcProps.js} +1 -1
- package/lib/KcProps.js.map +1 -0
- package/lib/Template.d.ts +18 -0
- package/lib/{components/Template.js → Template.js} +67 -55
- package/lib/Template.js.map +1 -0
- package/lib/getKcContext/KcContextBase.d.ts +6 -6
- package/lib/getKcContext/getKcContextFromWindow.d.ts +1 -1
- package/lib/getKcContext/{kcContextMocks/kcContextMocks.d.ts → kcContextMocks.d.ts} +1 -1
- package/lib/getKcContext/{kcContextMocks/kcContextMocks.js → kcContextMocks.js} +8 -5
- package/lib/getKcContext/kcContextMocks.js.map +1 -0
- package/lib/i18n/i18n.d.ts +100 -0
- package/lib/i18n/i18n.js +160 -0
- package/lib/i18n/i18n.js.map +1 -0
- package/lib/i18n/index.d.ts +1 -99
- package/lib/i18n/index.js +1 -159
- package/lib/i18n/index.js.map +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.js +3 -3
- package/lib/index.js.map +1 -1
- package/lib/pages/Error.d.ts +5 -0
- package/lib/{components → pages}/Error.js +4 -6
- package/lib/pages/Error.js.map +1 -0
- package/lib/pages/IdpReviewUserProfile.d.ts +5 -0
- package/lib/{components → pages}/IdpReviewUserProfile.js +4 -6
- package/lib/pages/IdpReviewUserProfile.js.map +1 -0
- package/lib/pages/Info.d.ts +5 -0
- package/lib/{components → pages}/Info.js +4 -6
- package/lib/pages/Info.js.map +1 -0
- package/lib/pages/Login.d.ts +5 -0
- package/lib/{components → pages}/Login.js +5 -7
- package/lib/pages/Login.js.map +1 -0
- package/lib/pages/LoginConfigTotp.d.ts +5 -0
- package/lib/{components → pages}/LoginConfigTotp.js +4 -6
- package/lib/pages/LoginConfigTotp.js.map +1 -0
- package/lib/pages/LoginIdpLinkConfirm.d.ts +5 -0
- package/lib/{components → pages}/LoginIdpLinkConfirm.js +4 -6
- package/lib/pages/LoginIdpLinkConfirm.js.map +1 -0
- package/lib/pages/LoginIdpLinkEmail.d.ts +5 -0
- package/lib/{components → pages}/LoginIdpLinkEmail.js +4 -6
- package/lib/pages/LoginIdpLinkEmail.js.map +1 -0
- package/lib/pages/LoginOtp.d.ts +5 -0
- package/lib/{components → pages}/LoginOtp.js +4 -6
- package/lib/pages/LoginOtp.js.map +1 -0
- package/lib/pages/LoginPageExpired.d.ts +5 -0
- package/lib/{components → pages}/LoginPageExpired.js +4 -6
- package/lib/pages/LoginPageExpired.js.map +1 -0
- package/lib/pages/LoginPassword.d.ts +5 -0
- package/lib/{components → pages}/LoginPassword.js +5 -7
- package/lib/pages/LoginPassword.js.map +1 -0
- package/lib/pages/LoginResetPassword.d.ts +5 -0
- package/lib/{components → pages}/LoginResetPassword.js +4 -6
- package/lib/pages/LoginResetPassword.js.map +1 -0
- package/lib/pages/LoginUpdatePassword.d.ts +5 -0
- package/lib/{components → pages}/LoginUpdatePassword.js +4 -6
- package/lib/pages/LoginUpdatePassword.js.map +1 -0
- package/lib/pages/LoginUpdateProfile.d.ts +5 -0
- package/lib/{components → pages}/LoginUpdateProfile.js +4 -6
- package/lib/pages/LoginUpdateProfile.js.map +1 -0
- package/lib/pages/LoginUsername.d.ts +5 -0
- package/lib/{components → pages}/LoginUsername.js +5 -7
- package/lib/pages/LoginUsername.js.map +1 -0
- package/lib/pages/LoginVerifyEmail.d.ts +5 -0
- package/lib/{components → pages}/LoginVerifyEmail.js +4 -6
- package/lib/pages/LoginVerifyEmail.js.map +1 -0
- package/lib/pages/LogoutConfirm.d.ts +5 -0
- package/lib/{components → pages}/LogoutConfirm.js +4 -6
- package/lib/pages/LogoutConfirm.js.map +1 -0
- package/lib/pages/Register.d.ts +5 -0
- package/lib/{components → pages}/Register.js +4 -6
- package/lib/pages/Register.js.map +1 -0
- package/lib/pages/RegisterUserProfile.d.ts +5 -0
- package/lib/{components → pages}/RegisterUserProfile.js +4 -6
- package/lib/pages/RegisterUserProfile.js.map +1 -0
- package/lib/pages/Terms.d.ts +19 -0
- package/lib/{components → pages}/Terms.js +20 -22
- package/lib/pages/Terms.js.map +1 -0
- package/lib/pages/UpdateUserProfile.d.ts +5 -0
- package/lib/{components → pages}/UpdateUserProfile.js +4 -6
- package/lib/pages/UpdateUserProfile.js.map +1 -0
- package/lib/pages/WebauthnAuthenticate.d.ts +5 -0
- package/lib/{components → pages}/WebauthnAuthenticate.js +5 -7
- package/lib/pages/WebauthnAuthenticate.js.map +1 -0
- package/lib/{components → pages}/shared/UserProfileCommons.d.ts +6 -6
- package/lib/{components → pages}/shared/UserProfileCommons.js +4 -4
- package/lib/pages/shared/UserProfileCommons.js.map +1 -0
- package/lib/tools/AndByDiscriminatingKey.d.ts +1 -1
- package/lib/tools/DeepPartial.d.ts +1 -1
- package/lib/tools/ReactComponent.d.ts +1 -1
- package/lib/tools/SetOptional.d.ts +1 -0
- package/lib/tools/SetOptional.js +2 -0
- package/lib/tools/SetOptional.js.map +1 -0
- package/lib/tools/clsx.d.ts +2 -3
- package/lib/tools/clsx.js +39 -3
- package/lib/tools/clsx.js.map +1 -1
- package/lib/tools/deepAssign.js +1 -1
- package/lib/tools/deepAssign.js.map +1 -1
- package/lib/tools/memoize.d.ts +7 -0
- package/lib/tools/memoize.js +38 -0
- package/lib/tools/memoize.js.map +1 -0
- package/lib/tools/useCallbackFactory.d.ts +15 -0
- package/lib/tools/useCallbackFactory.js +28 -0
- package/lib/tools/useCallbackFactory.js.map +1 -0
- package/lib/tools/useConst.d.ts +5 -0
- package/lib/tools/useConst.js +10 -0
- package/lib/tools/useConst.js.map +1 -0
- package/lib/tools/useConstCallback.d.ts +2 -0
- package/lib/tools/useConstCallback.js +8 -0
- package/lib/tools/useConstCallback.js.map +1 -0
- package/lib/tools/useCssAndCx.d.ts +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/useFormValidationSlice.d.ts +4 -4
- package/lib/useFormValidationSlice.js +1 -1
- package/lib/useFormValidationSlice.js.map +1 -1
- package/package.json +133 -124
- package/src/bin/keycloakify/BuildOptions.ts +2 -2
- package/src/bin/keycloakify/generateFtl/generateFtl.ts +1 -1
- package/src/lib/{components/KcApp.tsx → KcApp.tsx} +29 -38
- package/src/lib/{components/KcProps.ts → KcProps.ts} +27 -1
- package/src/lib/{components/Template.tsx → Template.tsx} +111 -95
- package/src/lib/getKcContext/KcContextBase.ts +1 -1
- package/src/lib/getKcContext/{kcContextMocks/kcContextMocks.ts → kcContextMocks.ts} +9 -6
- package/src/lib/i18n/i18n.tsx +292 -0
- package/src/lib/i18n/index.tsx +1 -290
- package/src/lib/index.ts +3 -3
- package/src/lib/{components → pages}/Error.tsx +7 -18
- package/src/lib/{components → pages}/IdpReviewUserProfile.tsx +7 -18
- package/src/lib/{components → pages}/Info.tsx +7 -18
- package/src/lib/{components → pages}/Login.tsx +8 -20
- package/src/lib/{components → pages}/LoginConfigTotp.tsx +7 -18
- package/src/lib/{components → pages}/LoginIdpLinkConfirm.tsx +7 -18
- package/src/lib/{components → pages}/LoginIdpLinkEmail.tsx +7 -18
- package/src/lib/{components → pages}/LoginOtp.tsx +7 -18
- package/src/lib/{components → pages}/LoginPageExpired.tsx +7 -18
- package/src/lib/{components → pages}/LoginPassword.tsx +8 -19
- package/src/lib/{components → pages}/LoginResetPassword.tsx +7 -18
- package/src/lib/{components → pages}/LoginUpdatePassword.tsx +7 -18
- package/src/lib/{components → pages}/LoginUpdateProfile.tsx +7 -18
- package/src/lib/{components → pages}/LoginUsername.tsx +8 -19
- package/src/lib/{components → pages}/LoginVerifyEmail.tsx +7 -18
- package/src/lib/{components → pages}/LogoutConfirm.tsx +7 -18
- package/src/lib/{components → pages}/Register.tsx +7 -18
- package/src/lib/{components → pages}/RegisterUserProfile.tsx +7 -18
- package/src/lib/{components → pages}/Terms.tsx +50 -61
- package/src/lib/{components → pages}/UpdateUserProfile.tsx +7 -18
- package/src/lib/{components → pages}/WebauthnAuthenticate.tsx +9 -19
- package/src/lib/pages/shared/UserProfileCommons.tsx +178 -0
- package/src/lib/tools/SetOptional.ts +1 -0
- package/src/lib/tools/clsx.ts +42 -5
- package/src/lib/tools/deepAssign.ts +1 -1
- package/src/lib/tools/memoize.ts +55 -0
- package/src/lib/tools/useCallbackFactory.ts +45 -0
- package/src/lib/tools/useConst.ts +10 -0
- package/src/lib/tools/useConstCallback.ts +15 -0
- package/src/lib/useFormValidationSlice.tsx +4 -4
- package/bin/generate-i18n-messages.d.ts +0 -1
- package/bin/generate-i18n-messages.js +0 -126
- package/bin/generate-i18n-messages.js.map +0 -1
- package/bin/link_in_test_app.d.ts +0 -1
- package/bin/link_in_test_app.js +0 -141
- package/bin/link_in_test_app.js.map +0 -1
- package/lib/components/Error.d.ts +0 -13
- package/lib/components/Error.js.map +0 -1
- package/lib/components/IdpReviewUserProfile.d.ts +0 -13
- package/lib/components/IdpReviewUserProfile.js.map +0 -1
- package/lib/components/Info.d.ts +0 -13
- package/lib/components/Info.js.map +0 -1
- package/lib/components/KcApp.d.ts +0 -13
- package/lib/components/KcApp.js.map +0 -1
- package/lib/components/KcProps.js.map +0 -1
- package/lib/components/Login.d.ts +0 -13
- package/lib/components/Login.js.map +0 -1
- package/lib/components/LoginConfigTotp.d.ts +0 -13
- package/lib/components/LoginConfigTotp.js.map +0 -1
- package/lib/components/LoginIdpLinkConfirm.d.ts +0 -13
- package/lib/components/LoginIdpLinkConfirm.js.map +0 -1
- package/lib/components/LoginIdpLinkEmail.d.ts +0 -13
- package/lib/components/LoginIdpLinkEmail.js.map +0 -1
- package/lib/components/LoginOtp.d.ts +0 -13
- package/lib/components/LoginOtp.js.map +0 -1
- package/lib/components/LoginPageExpired.d.ts +0 -13
- package/lib/components/LoginPageExpired.js.map +0 -1
- package/lib/components/LoginPassword.d.ts +0 -13
- package/lib/components/LoginPassword.js.map +0 -1
- package/lib/components/LoginResetPassword.d.ts +0 -13
- package/lib/components/LoginResetPassword.js.map +0 -1
- package/lib/components/LoginUpdatePassword.d.ts +0 -13
- package/lib/components/LoginUpdatePassword.js.map +0 -1
- package/lib/components/LoginUpdateProfile.d.ts +0 -13
- package/lib/components/LoginUpdateProfile.js.map +0 -1
- package/lib/components/LoginUsername.d.ts +0 -13
- package/lib/components/LoginUsername.js.map +0 -1
- package/lib/components/LoginVerifyEmail.d.ts +0 -13
- package/lib/components/LoginVerifyEmail.js.map +0 -1
- package/lib/components/LogoutConfirm.d.ts +0 -13
- package/lib/components/LogoutConfirm.js.map +0 -1
- package/lib/components/Register.d.ts +0 -13
- package/lib/components/Register.js.map +0 -1
- package/lib/components/RegisterUserProfile.d.ts +0 -13
- package/lib/components/RegisterUserProfile.js.map +0 -1
- package/lib/components/Template.d.ts +0 -25
- package/lib/components/Template.js.map +0 -1
- package/lib/components/Terms.d.ts +0 -27
- package/lib/components/Terms.js.map +0 -1
- package/lib/components/UpdateUserProfile.d.ts +0 -13
- package/lib/components/UpdateUserProfile.js.map +0 -1
- package/lib/components/WebauthnAuthenticate.d.ts +0 -13
- package/lib/components/WebauthnAuthenticate.js.map +0 -1
- package/lib/components/shared/UserProfileCommons.js.map +0 -1
- package/lib/getKcContext/kcContextMocks/index.d.ts +0 -1
- package/lib/getKcContext/kcContextMocks/index.js +0 -2
- package/lib/getKcContext/kcContextMocks/index.js.map +0 -1
- package/lib/getKcContext/kcContextMocks/kcContextMocks.js.map +0 -1
- package/src/bin/generate-i18n-messages.ts +0 -86
- package/src/bin/link_in_test_app.ts +0 -128
- package/src/lib/components/shared/UserProfileCommons.tsx +0 -173
- package/src/lib/getKcContext/kcContextMocks/index.ts +0 -1
@@ -1,32 +1,13 @@
|
|
1
|
-
import React, { useReducer, useEffect
|
2
|
-
import
|
3
|
-
import
|
4
|
-
import {
|
5
|
-
import {
|
6
|
-
import {
|
7
|
-
import {
|
8
|
-
import {
|
9
|
-
import type { KcTemplateProps } from "./KcProps";
|
10
|
-
import { clsx } from "../tools/clsx";
|
11
|
-
import type { I18n } from "../i18n";
|
12
|
-
|
13
|
-
export type TemplateProps = {
|
14
|
-
displayInfo?: boolean;
|
15
|
-
displayMessage?: boolean;
|
16
|
-
displayRequiredFields?: boolean;
|
17
|
-
displayWide?: boolean;
|
18
|
-
showAnotherWayIfPresent?: boolean;
|
19
|
-
headerNode: ReactNode;
|
20
|
-
showUsernameNode?: ReactNode;
|
21
|
-
formNode: ReactNode;
|
22
|
-
infoNode?: ReactNode;
|
23
|
-
/** If you write your own page you probably want
|
24
|
-
* to avoid pulling the default theme assets.
|
25
|
-
*/
|
26
|
-
doFetchDefaultThemeResources: boolean;
|
27
|
-
} & { kcContext: KcContextBase; i18n: I18n } & KcTemplateProps;
|
1
|
+
import React, { useReducer, useEffect } from "react";
|
2
|
+
import { assert } from "./tools/assert";
|
3
|
+
import { headInsert } from "./tools/headInsert";
|
4
|
+
import { pathJoin } from "../bin/tools/pathJoin";
|
5
|
+
import { clsx } from "./tools/clsx";
|
6
|
+
import type { TemplateProps } from "./KcProps";
|
7
|
+
import type { KcContextBase } from "./getKcContext/KcContextBase";
|
8
|
+
import type { I18nBase } from "./i18n";
|
28
9
|
|
29
|
-
|
10
|
+
export default function Template(props: TemplateProps<KcContextBase.Common, I18nBase>) {
|
30
11
|
const {
|
31
12
|
displayInfo = false,
|
32
13
|
displayMessage = true,
|
@@ -39,76 +20,27 @@ const Template = memo((props: TemplateProps) => {
|
|
39
20
|
infoNode = null,
|
40
21
|
kcContext,
|
41
22
|
i18n,
|
42
|
-
doFetchDefaultThemeResources
|
23
|
+
doFetchDefaultThemeResources,
|
24
|
+
stylesCommon,
|
25
|
+
styles,
|
26
|
+
scripts,
|
27
|
+
kcHtmlClass
|
43
28
|
} = props;
|
44
29
|
|
45
30
|
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n;
|
46
31
|
|
47
|
-
const onChangeLanguageClickFactory = useCallbackFactory(([kcLanguageTag]: [string]) => changeLocale(kcLanguageTag));
|
48
|
-
|
49
|
-
const onTryAnotherWayClick = useConstCallback(() => (document.forms["kc-select-try-another-way-form" as never].submit(), false));
|
50
|
-
|
51
32
|
const { realm, locale, auth, url, message, isAppInitiatedAction } = kcContext;
|
52
33
|
|
53
|
-
const
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
let isUnmounted = false;
|
62
|
-
const cleanups: (() => void)[] = [];
|
63
|
-
|
64
|
-
const toArr = (x: string | readonly string[] | undefined) => (typeof x === "string" ? x.split(" ") : x ?? []);
|
65
|
-
|
66
|
-
Promise.all(
|
67
|
-
[
|
68
|
-
...toArr(props.stylesCommon).map(relativePath => pathJoin(url.resourcesCommonPath, relativePath)),
|
69
|
-
...toArr(props.styles).map(relativePath => pathJoin(url.resourcesPath, relativePath))
|
70
|
-
]
|
71
|
-
.reverse()
|
72
|
-
.map(href =>
|
73
|
-
headInsert({
|
74
|
-
"type": "css",
|
75
|
-
href,
|
76
|
-
"position": "prepend"
|
77
|
-
})
|
78
|
-
)
|
79
|
-
).then(() => {
|
80
|
-
if (isUnmounted) {
|
81
|
-
return;
|
82
|
-
}
|
83
|
-
|
84
|
-
setExtraCssLoaded();
|
85
|
-
});
|
86
|
-
|
87
|
-
toArr(props.scripts).forEach(relativePath =>
|
88
|
-
headInsert({
|
89
|
-
"type": "javascript",
|
90
|
-
"src": pathJoin(url.resourcesPath, relativePath)
|
91
|
-
})
|
92
|
-
);
|
93
|
-
|
94
|
-
if (props.kcHtmlClass !== undefined) {
|
95
|
-
const htmlClassList = document.getElementsByTagName("html")[0].classList;
|
96
|
-
|
97
|
-
const tokens = clsx(props.kcHtmlClass).split(" ");
|
98
|
-
|
99
|
-
htmlClassList.add(...tokens);
|
100
|
-
|
101
|
-
cleanups.push(() => htmlClassList.remove(...tokens));
|
102
|
-
}
|
103
|
-
|
104
|
-
return () => {
|
105
|
-
isUnmounted = true;
|
106
|
-
|
107
|
-
cleanups.forEach(f => f());
|
108
|
-
};
|
109
|
-
}, [props.kcHtmlClass]);
|
34
|
+
const { isReady } = usePrepareTemplate({
|
35
|
+
doFetchDefaultThemeResources,
|
36
|
+
stylesCommon,
|
37
|
+
styles,
|
38
|
+
scripts,
|
39
|
+
url,
|
40
|
+
kcHtmlClass
|
41
|
+
});
|
110
42
|
|
111
|
-
if (!
|
43
|
+
if (!isReady) {
|
112
44
|
return null;
|
113
45
|
}
|
114
46
|
|
@@ -126,13 +58,15 @@ const Template = memo((props: TemplateProps) => {
|
|
126
58
|
<div id="kc-locale">
|
127
59
|
<div id="kc-locale-wrapper" className={clsx(props.kcLocaleWrapperClass)}>
|
128
60
|
<div className="kc-dropdown" id="kc-locale-dropdown">
|
61
|
+
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
129
62
|
<a href="#" id="kc-current-locale-link">
|
130
63
|
{labelBySupportedLanguageTag[currentLanguageTag]}
|
131
64
|
</a>
|
132
65
|
<ul>
|
133
66
|
{locale.supported.map(({ languageTag }) => (
|
134
67
|
<li key={languageTag} className="kc-dropdown-item">
|
135
|
-
|
68
|
+
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
69
|
+
<a href="#" onClick={() => changeLocale(languageTag)}>
|
136
70
|
{labelBySupportedLanguageTag[languageTag]}
|
137
71
|
</a>
|
138
72
|
</li>
|
@@ -225,7 +159,15 @@ const Template = memo((props: TemplateProps) => {
|
|
225
159
|
<div className={clsx(displayWide && [props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass])}>
|
226
160
|
<div className={clsx(props.kcFormGroupClass)}>
|
227
161
|
<input type="hidden" name="tryAnotherWay" value="on" />
|
228
|
-
|
162
|
+
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
163
|
+
<a
|
164
|
+
href="#"
|
165
|
+
id="try-another-way"
|
166
|
+
onClick={() => {
|
167
|
+
document.forms["kc-select-try-another-way-form" as never].submit();
|
168
|
+
return false;
|
169
|
+
}}
|
170
|
+
>
|
229
171
|
{msg("doTryAnotherWay")}
|
230
172
|
</a>
|
231
173
|
</div>
|
@@ -244,6 +186,80 @@ const Template = memo((props: TemplateProps) => {
|
|
244
186
|
</div>
|
245
187
|
</div>
|
246
188
|
);
|
247
|
-
}
|
189
|
+
}
|
190
|
+
|
191
|
+
export function usePrepareTemplate(params: {
|
192
|
+
doFetchDefaultThemeResources: boolean;
|
193
|
+
stylesCommon: string | readonly string[] | undefined;
|
194
|
+
styles: string | readonly string[] | undefined;
|
195
|
+
scripts: string | readonly string[] | undefined;
|
196
|
+
url: {
|
197
|
+
resourcesCommonPath: string;
|
198
|
+
resourcesPath: string;
|
199
|
+
};
|
200
|
+
kcHtmlClass: string | readonly string[] | undefined;
|
201
|
+
}) {
|
202
|
+
const { doFetchDefaultThemeResources, stylesCommon, styles, url, scripts, kcHtmlClass } = params;
|
203
|
+
|
204
|
+
const [isReady, setReady] = useReducer(() => true, !doFetchDefaultThemeResources);
|
205
|
+
|
206
|
+
useEffect(() => {
|
207
|
+
if (!doFetchDefaultThemeResources) {
|
208
|
+
return;
|
209
|
+
}
|
210
|
+
|
211
|
+
let isUnmounted = false;
|
212
|
+
|
213
|
+
const toArr = (x: string | readonly string[] | undefined) => (typeof x === "string" ? x.split(" ") : x ?? []);
|
214
|
+
|
215
|
+
Promise.all(
|
216
|
+
[
|
217
|
+
...toArr(stylesCommon).map(relativePath => pathJoin(url.resourcesCommonPath, relativePath)),
|
218
|
+
...toArr(styles).map(relativePath => pathJoin(url.resourcesPath, relativePath))
|
219
|
+
]
|
220
|
+
.reverse()
|
221
|
+
.map(href =>
|
222
|
+
headInsert({
|
223
|
+
"type": "css",
|
224
|
+
href,
|
225
|
+
"position": "prepend"
|
226
|
+
})
|
227
|
+
)
|
228
|
+
).then(() => {
|
229
|
+
if (isUnmounted) {
|
230
|
+
return;
|
231
|
+
}
|
232
|
+
|
233
|
+
setReady();
|
234
|
+
});
|
235
|
+
|
236
|
+
toArr(scripts).forEach(relativePath =>
|
237
|
+
headInsert({
|
238
|
+
"type": "javascript",
|
239
|
+
"src": pathJoin(url.resourcesPath, relativePath)
|
240
|
+
})
|
241
|
+
);
|
242
|
+
|
243
|
+
return () => {
|
244
|
+
isUnmounted = true;
|
245
|
+
};
|
246
|
+
}, [kcHtmlClass]);
|
247
|
+
|
248
|
+
useEffect(() => {
|
249
|
+
if (kcHtmlClass === undefined) {
|
250
|
+
return;
|
251
|
+
}
|
252
|
+
|
253
|
+
const htmlClassList = document.getElementsByTagName("html")[0].classList;
|
254
|
+
|
255
|
+
const tokens = clsx(kcHtmlClass).split(" ");
|
256
|
+
|
257
|
+
htmlClassList.add(...tokens);
|
258
|
+
|
259
|
+
return () => {
|
260
|
+
htmlClassList.remove(...tokens);
|
261
|
+
};
|
262
|
+
}, [kcHtmlClass]);
|
248
263
|
|
249
|
-
|
264
|
+
return { isReady };
|
265
|
+
}
|
@@ -2,7 +2,7 @@ import type { PageId } from "../../bin/keycloakify/generateFtl";
|
|
2
2
|
import { assert } from "tsafe/assert";
|
3
3
|
import type { Equals } from "tsafe";
|
4
4
|
import type { MessageKeyBase } from "../i18n";
|
5
|
-
import type { KcTemplateClassKey } from "../
|
5
|
+
import type { KcTemplateClassKey } from "../KcProps";
|
6
6
|
|
7
7
|
type ExtractAfterStartingWith<Prefix extends string, StrEnum> = StrEnum extends `${Prefix}${infer U}` ? U : never;
|
8
8
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import "minimal-polyfills/Object.fromEntries";
|
2
|
-
import type { KcContextBase, Attribute } from "
|
2
|
+
import type { KcContextBase, Attribute } from "./KcContextBase";
|
3
3
|
//NOTE: Aside because we want to be able to import them from node
|
4
|
-
import { mockTestingResourcesCommonPath, mockTestingResourcesPath } from "
|
4
|
+
import { mockTestingResourcesCommonPath, mockTestingResourcesPath } from "../../bin/mockTestingResourcesPath";
|
5
5
|
import { id } from "tsafe/id";
|
6
|
-
import { pathJoin } from "
|
6
|
+
import { pathJoin } from "../../bin/tools/pathJoin";
|
7
7
|
|
8
8
|
const PUBLIC_URL = process.env["PUBLIC_URL"] ?? "/";
|
9
9
|
|
@@ -117,10 +117,13 @@ export const kcContextCommonMock: KcContextBase.Common = {
|
|
117
117
|
"registrationEmailAsUsername": false
|
118
118
|
},
|
119
119
|
"messagesPerField": {
|
120
|
-
"printIfExists": (
|
121
|
-
|
120
|
+
"printIfExists": () => {
|
121
|
+
console.log("coucou");
|
122
|
+
return undefined;
|
123
|
+
},
|
124
|
+
"existsError": () => false,
|
122
125
|
"get": key => `Fake error for ${key}`,
|
123
|
-
"exists": () =>
|
126
|
+
"exists": () => false
|
124
127
|
},
|
125
128
|
"locale": {
|
126
129
|
"supported": [
|
@@ -0,0 +1,292 @@
|
|
1
|
+
import "minimal-polyfills/Object.fromEntries";
|
2
|
+
//NOTE for later: https://github.com/remarkjs/react-markdown/blob/236182ecf30bd89c1e5a7652acaf8d0bf81e6170/src/renderers.js#L7-L35
|
3
|
+
import React, { useEffect, useState, useRef } from "react";
|
4
|
+
import type baseMessages from "./generated_messages/18.0.1/login/en";
|
5
|
+
import { assert } from "tsafe/assert";
|
6
|
+
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
7
|
+
import { Markdown } from "../tools/Markdown";
|
8
|
+
|
9
|
+
export const fallbackLanguageTag = "en";
|
10
|
+
|
11
|
+
export type KcContextLike = {
|
12
|
+
locale?: {
|
13
|
+
currentLanguageTag: string;
|
14
|
+
supported: { languageTag: string; url: string; label: string }[];
|
15
|
+
};
|
16
|
+
};
|
17
|
+
|
18
|
+
assert<KcContextBase extends KcContextLike ? true : false>();
|
19
|
+
|
20
|
+
export type MessageKeyBase = keyof typeof baseMessages | keyof (typeof keycloakifyExtraMessages)[typeof fallbackLanguageTag];
|
21
|
+
|
22
|
+
export type I18n<MessageKey extends string> = {
|
23
|
+
/**
|
24
|
+
* e.g: "en", "fr", "zh-CN"
|
25
|
+
*
|
26
|
+
* The current language
|
27
|
+
*/
|
28
|
+
currentLanguageTag: string;
|
29
|
+
/**
|
30
|
+
* To call when the user switch language.
|
31
|
+
* This will cause the page to be reloaded,
|
32
|
+
* on next load currentLanguageTag === newLanguageTag
|
33
|
+
*/
|
34
|
+
changeLocale: (newLanguageTag: string) => never;
|
35
|
+
/**
|
36
|
+
* e.g. "en" => "English", "fr" => "Français", ...
|
37
|
+
*
|
38
|
+
* Used to render a select that enable user to switch language.
|
39
|
+
* ex: https://user-images.githubusercontent.com/6702424/186044799-38801eec-4e89-483b-81dd-8e9233e8c0eb.png
|
40
|
+
* */
|
41
|
+
labelBySupportedLanguageTag: Record<string, string>;
|
42
|
+
/**
|
43
|
+
* Examples assuming currentLanguageTag === "en"
|
44
|
+
*
|
45
|
+
* msg("access-denied") === <span>Access denied</span>
|
46
|
+
* msg("impersonateTitleHtml", "Foo") === <span><strong>Foo</strong> Impersonate User</span>
|
47
|
+
*/
|
48
|
+
msg: (key: MessageKey, ...args: (string | undefined)[]) => JSX.Element;
|
49
|
+
/**
|
50
|
+
* It's the same thing as msg() but instead of returning a JSX.Element it returns a string.
|
51
|
+
* It can be more convenient to manipulate strings but if there are HTML tags it wont render.
|
52
|
+
* msgStr("impersonateTitleHtml", "Foo") === "<strong>Foo</strong> Impersonate User"
|
53
|
+
*/
|
54
|
+
msgStr: (key: MessageKey, ...args: (string | undefined)[]) => string;
|
55
|
+
/**
|
56
|
+
* Examples assuming currentLanguageTag === "en"
|
57
|
+
* advancedMsg("${access-denied} foo bar") === <span>${msgStr("access-denied")} foo bar<span> === <span>Access denied foo bar</span>
|
58
|
+
* advancedMsg("${access-denied}") === advancedMsg("access-denied") === msg("access-denied") === <span>Access denied</span>
|
59
|
+
* advancedMsg("${not-a-message-key}") === advancedMsg(not-a-message-key") === <span>not-a-message-key</span>
|
60
|
+
*/
|
61
|
+
advancedMsg: (key: string, ...args: (string | undefined)[]) => JSX.Element;
|
62
|
+
/**
|
63
|
+
* Examples assuming currentLanguageTag === "en"
|
64
|
+
* advancedMsg("${access-denied} foo bar") === msg("access-denied") + " foo bar" === "Access denied foo bar"
|
65
|
+
* advancedMsg("${not-a-message-key}") === advancedMsg(not-a-message-key") === "not-a-message-key"
|
66
|
+
*/
|
67
|
+
advancedMsgStr: (key: string, ...args: (string | undefined)[]) => string;
|
68
|
+
};
|
69
|
+
|
70
|
+
export type I18nBase = I18n<MessageKeyBase>;
|
71
|
+
|
72
|
+
export function __unsafe_useI18n<ExtraMessageKey extends string = never>(params: {
|
73
|
+
kcContext: KcContextLike;
|
74
|
+
extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } };
|
75
|
+
doSkip: boolean;
|
76
|
+
}): I18n<MessageKeyBase | ExtraMessageKey> | null {
|
77
|
+
const { kcContext, extraMessages, doSkip } = params;
|
78
|
+
|
79
|
+
const [i18n, setI18n] = useState<I18n<ExtraMessageKey | MessageKeyBase> | undefined>(undefined);
|
80
|
+
|
81
|
+
const refHasStartedFetching = useRef(false);
|
82
|
+
|
83
|
+
useEffect(() => {
|
84
|
+
if (doSkip || refHasStartedFetching.current) {
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
|
88
|
+
refHasStartedFetching.current = true;
|
89
|
+
|
90
|
+
(async () => {
|
91
|
+
const { currentLanguageTag = fallbackLanguageTag } = kcContext.locale ?? {};
|
92
|
+
|
93
|
+
const [fallbackMessages, messages] = await Promise.all([
|
94
|
+
import("./generated_messages/18.0.1/login/en"),
|
95
|
+
(() => {
|
96
|
+
switch (currentLanguageTag) {
|
97
|
+
case "ca":
|
98
|
+
return import("./generated_messages/18.0.1/login/ca");
|
99
|
+
case "cs":
|
100
|
+
return import("./generated_messages/18.0.1/login/cs");
|
101
|
+
case "da":
|
102
|
+
return import("./generated_messages/18.0.1/login/da");
|
103
|
+
case "de":
|
104
|
+
return import("./generated_messages/18.0.1/login/de");
|
105
|
+
case "en":
|
106
|
+
return import("./generated_messages/18.0.1/login/en");
|
107
|
+
case "es":
|
108
|
+
return import("./generated_messages/18.0.1/login/es");
|
109
|
+
case "fi":
|
110
|
+
return import("./generated_messages/18.0.1/login/fi");
|
111
|
+
case "fr":
|
112
|
+
return import("./generated_messages/18.0.1/login/fr");
|
113
|
+
case "hu":
|
114
|
+
return import("./generated_messages/18.0.1/login/hu");
|
115
|
+
case "it":
|
116
|
+
return import("./generated_messages/18.0.1/login/it");
|
117
|
+
case "ja":
|
118
|
+
return import("./generated_messages/18.0.1/login/ja");
|
119
|
+
case "lt":
|
120
|
+
return import("./generated_messages/18.0.1/login/lt");
|
121
|
+
case "lv":
|
122
|
+
return import("./generated_messages/18.0.1/login/lv");
|
123
|
+
case "nl":
|
124
|
+
return import("./generated_messages/18.0.1/login/nl");
|
125
|
+
case "no":
|
126
|
+
return import("./generated_messages/18.0.1/login/no");
|
127
|
+
case "pl":
|
128
|
+
return import("./generated_messages/18.0.1/login/pl");
|
129
|
+
case "pt-BR":
|
130
|
+
return import("./generated_messages/18.0.1/login/pt-BR");
|
131
|
+
case "ru":
|
132
|
+
return import("./generated_messages/18.0.1/login/ru");
|
133
|
+
case "sk":
|
134
|
+
return import("./generated_messages/18.0.1/login/sk");
|
135
|
+
case "sv":
|
136
|
+
return import("./generated_messages/18.0.1/login/sv");
|
137
|
+
case "tr":
|
138
|
+
return import("./generated_messages/18.0.1/login/tr");
|
139
|
+
case "zh-CN":
|
140
|
+
return import("./generated_messages/18.0.1/login/zh-CN");
|
141
|
+
default:
|
142
|
+
return { "default": {} };
|
143
|
+
}
|
144
|
+
})()
|
145
|
+
]).then(modules => modules.map(module => module.default));
|
146
|
+
|
147
|
+
setI18n({
|
148
|
+
...createI18nTranslationFunctions({
|
149
|
+
"fallbackMessages": {
|
150
|
+
...fallbackMessages,
|
151
|
+
...(keycloakifyExtraMessages[fallbackLanguageTag] ?? {}),
|
152
|
+
...(extraMessages[fallbackLanguageTag] ?? {})
|
153
|
+
} as any,
|
154
|
+
"messages": {
|
155
|
+
...messages,
|
156
|
+
...((keycloakifyExtraMessages as any)[currentLanguageTag] ?? {}),
|
157
|
+
...(extraMessages[currentLanguageTag] ?? {})
|
158
|
+
} as any
|
159
|
+
}),
|
160
|
+
currentLanguageTag,
|
161
|
+
"changeLocale": newLanguageTag => {
|
162
|
+
const { locale } = kcContext;
|
163
|
+
|
164
|
+
assert(locale !== undefined, "Internationalization not enabled");
|
165
|
+
|
166
|
+
const targetSupportedLocale = locale.supported.find(({ languageTag }) => languageTag === newLanguageTag);
|
167
|
+
|
168
|
+
assert(targetSupportedLocale !== undefined, `${newLanguageTag} need to be enabled in Keycloak admin`);
|
169
|
+
|
170
|
+
window.location.href = targetSupportedLocale.url;
|
171
|
+
|
172
|
+
assert(false, "never");
|
173
|
+
},
|
174
|
+
"labelBySupportedLanguageTag": Object.fromEntries(
|
175
|
+
(kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label])
|
176
|
+
)
|
177
|
+
});
|
178
|
+
})();
|
179
|
+
}, []);
|
180
|
+
|
181
|
+
return i18n ?? null;
|
182
|
+
}
|
183
|
+
|
184
|
+
const useI18n_private = __unsafe_useI18n;
|
185
|
+
|
186
|
+
export function useI18n<ExtraMessageKey extends string = never>(params: {
|
187
|
+
kcContext: KcContextLike;
|
188
|
+
extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } };
|
189
|
+
}): I18n<MessageKeyBase | ExtraMessageKey> | null {
|
190
|
+
return useI18n_private({
|
191
|
+
...params,
|
192
|
+
"doSkip": false
|
193
|
+
});
|
194
|
+
}
|
195
|
+
|
196
|
+
function createI18nTranslationFunctions<MessageKey extends string>(params: {
|
197
|
+
fallbackMessages: Record<MessageKey, string>;
|
198
|
+
messages: Record<MessageKey, string>;
|
199
|
+
}): Pick<I18n<MessageKey>, "msg" | "msgStr" | "advancedMsg" | "advancedMsgStr"> {
|
200
|
+
const { fallbackMessages, messages } = params;
|
201
|
+
|
202
|
+
function resolveMsg(props: { key: string; args: (string | undefined)[]; doRenderMarkdown: boolean }): string | JSX.Element | undefined {
|
203
|
+
const { key, args, doRenderMarkdown } = props;
|
204
|
+
|
205
|
+
const messageOrUndefined: string | undefined = (messages as any)[key] ?? (fallbackMessages as any)[key];
|
206
|
+
|
207
|
+
if (messageOrUndefined === undefined) {
|
208
|
+
return undefined;
|
209
|
+
}
|
210
|
+
|
211
|
+
const message = messageOrUndefined;
|
212
|
+
|
213
|
+
const messageWithArgsInjectedIfAny = (() => {
|
214
|
+
const startIndex = message
|
215
|
+
.match(/{[0-9]+}/g)
|
216
|
+
?.map(g => g.match(/{([0-9]+)}/)![1])
|
217
|
+
.map(indexStr => parseInt(indexStr))
|
218
|
+
.sort((a, b) => a - b)[0];
|
219
|
+
|
220
|
+
if (startIndex === undefined) {
|
221
|
+
// No {0} in message (no arguments expected)
|
222
|
+
return message;
|
223
|
+
}
|
224
|
+
|
225
|
+
let messageWithArgsInjected = message;
|
226
|
+
|
227
|
+
args.forEach((arg, i) => {
|
228
|
+
if (arg === undefined) {
|
229
|
+
return;
|
230
|
+
}
|
231
|
+
|
232
|
+
messageWithArgsInjected = messageWithArgsInjected.replace(new RegExp(`\\{${i + startIndex}\\}`, "g"), arg);
|
233
|
+
});
|
234
|
+
|
235
|
+
return messageWithArgsInjected;
|
236
|
+
})();
|
237
|
+
|
238
|
+
return doRenderMarkdown ? (
|
239
|
+
<Markdown allowDangerousHtml renderers={{ "paragraph": "span" }}>
|
240
|
+
{messageWithArgsInjectedIfAny}
|
241
|
+
</Markdown>
|
242
|
+
) : (
|
243
|
+
messageWithArgsInjectedIfAny
|
244
|
+
);
|
245
|
+
}
|
246
|
+
|
247
|
+
function resolveMsgAdvanced(props: { key: string; args: (string | undefined)[]; doRenderMarkdown: boolean }): JSX.Element | string {
|
248
|
+
const { key, args, doRenderMarkdown } = props;
|
249
|
+
|
250
|
+
const match = key.match(/^\$\{([^{]+)\}$/);
|
251
|
+
|
252
|
+
const keyUnwrappedFromCurlyBraces = match === null ? key : match[1];
|
253
|
+
|
254
|
+
const out = resolveMsg({
|
255
|
+
"key": keyUnwrappedFromCurlyBraces,
|
256
|
+
args,
|
257
|
+
doRenderMarkdown
|
258
|
+
});
|
259
|
+
|
260
|
+
return (out !== undefined ? out : doRenderMarkdown ? <span>{keyUnwrappedFromCurlyBraces}</span> : keyUnwrappedFromCurlyBraces) as any;
|
261
|
+
}
|
262
|
+
|
263
|
+
return {
|
264
|
+
"msgStr": (key, ...args) => resolveMsg({ key, args, "doRenderMarkdown": false }) as string,
|
265
|
+
"msg": (key, ...args) => resolveMsg({ key, args, "doRenderMarkdown": true }) as JSX.Element,
|
266
|
+
"advancedMsg": (key, ...args) => resolveMsgAdvanced({ key, args, "doRenderMarkdown": true }) as JSX.Element,
|
267
|
+
"advancedMsgStr": (key, ...args) => resolveMsgAdvanced({ key, args, "doRenderMarkdown": false }) as string
|
268
|
+
};
|
269
|
+
}
|
270
|
+
|
271
|
+
const keycloakifyExtraMessages = {
|
272
|
+
"en": {
|
273
|
+
"shouldBeEqual": "{0} should be equal to {1}",
|
274
|
+
"shouldBeDifferent": "{0} should be different to {1}",
|
275
|
+
"shouldMatchPattern": "Pattern should match: `/{0}/`",
|
276
|
+
"mustBeAnInteger": "Must be an integer",
|
277
|
+
"notAValidOption": "Not a valid option"
|
278
|
+
},
|
279
|
+
"fr": {
|
280
|
+
/* spell-checker: disable */
|
281
|
+
"shouldBeEqual": "{0} doit être égal à {1}",
|
282
|
+
"shouldBeDifferent": "{0} doit être différent de {1}",
|
283
|
+
"shouldMatchPattern": "Dois respecter le schéma: `/{0}/`",
|
284
|
+
"mustBeAnInteger": "Doit être un nombre entier",
|
285
|
+
"notAValidOption": "N'est pas une option valide",
|
286
|
+
|
287
|
+
"logoutConfirmTitle": "Déconnexion",
|
288
|
+
"logoutConfirmHeader": "Êtes-vous sûr(e) de vouloir vous déconnecter ?",
|
289
|
+
"doLogout": "Se déconnecter"
|
290
|
+
/* spell-checker: enable */
|
291
|
+
}
|
292
|
+
};
|