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.
Files changed (253) hide show
  1. package/README.md +10 -7
  2. package/bin/create-keycloak-email-directory.js +1 -1
  3. package/bin/download-builtin-keycloak-theme.js +1 -1
  4. package/bin/keycloakify/BuildOptions.d.ts +2 -2
  5. package/bin/keycloakify/BuildOptions.js.map +1 -1
  6. package/bin/keycloakify/generateFtl/generateFtl.d.ts +2 -2
  7. package/bin/keycloakify/generateJavaStackFiles.d.ts +1 -1
  8. package/bin/keycloakify/generateKeycloakThemeResources.d.ts +1 -1
  9. package/bin/keycloakify/generateKeycloakThemeResources.js +1 -1
  10. package/bin/keycloakify/generateStartKeycloakTestingContainer.d.ts +1 -1
  11. package/bin/keycloakify/keycloakify.js +1 -1
  12. package/bin/keycloakify/replacers/replaceImportsFromStaticInJsCode.d.ts +1 -1
  13. package/bin/keycloakify/replacers/replaceImportsInCssCode.d.ts +1 -1
  14. package/bin/keycloakify/replacers/replaceImportsInInlineCssCode.d.ts +1 -1
  15. package/bin/promptKeycloakVersion.js +1 -1
  16. package/bin/tools/NpmModuleVersion.d.ts +1 -1
  17. package/bin/tools/cliOptions.d.ts +1 -1
  18. package/bin/tools/deflate.js +1 -1
  19. package/bin/tools/downloadAndUnzip.js +51 -44
  20. package/bin/tools/downloadAndUnzip.js.map +1 -1
  21. package/bin/tools/grant-exec-perms.js +3 -2
  22. package/bin/tools/grant-exec-perms.js.map +1 -1
  23. package/bin/tools/jar.d.ts +1 -1
  24. package/bin/tools/jar.js +1 -1
  25. package/bin/tools/logger.d.ts +2 -2
  26. package/bin/tools/octokit-addons/getLatestsSemVersionedTag.js +34 -27
  27. package/bin/tools/octokit-addons/getLatestsSemVersionedTag.js.map +1 -1
  28. package/bin/tools/octokit-addons/listTags.js +1 -1
  29. package/bin/tools/transformCodebase.d.ts +1 -1
  30. package/bin/tools/walk.js +1 -1
  31. package/bin/tools/zip.d.ts +2 -2
  32. package/bin/tools/zip.js +1 -1
  33. package/bin/tsconfig.tsbuildinfo +1 -1
  34. package/lib/KcApp.d.ts +6 -0
  35. package/lib/{components/KcApp.js → KcApp.js} +25 -26
  36. package/lib/KcApp.js.map +1 -0
  37. package/lib/{components/KcProps.d.ts → KcProps.d.ts} +30 -6
  38. package/lib/{components/KcProps.js → KcProps.js} +1 -1
  39. package/lib/KcProps.js.map +1 -0
  40. package/lib/Template.d.ts +18 -0
  41. package/lib/{components/Template.js → Template.js} +67 -55
  42. package/lib/Template.js.map +1 -0
  43. package/lib/getKcContext/KcContextBase.d.ts +6 -6
  44. package/lib/getKcContext/getKcContextFromWindow.d.ts +1 -1
  45. package/lib/getKcContext/{kcContextMocks/kcContextMocks.d.ts → kcContextMocks.d.ts} +1 -1
  46. package/lib/getKcContext/{kcContextMocks/kcContextMocks.js → kcContextMocks.js} +8 -5
  47. package/lib/getKcContext/kcContextMocks.js.map +1 -0
  48. package/lib/i18n/i18n.d.ts +100 -0
  49. package/lib/i18n/i18n.js +160 -0
  50. package/lib/i18n/i18n.js.map +1 -0
  51. package/lib/i18n/index.d.ts +1 -99
  52. package/lib/i18n/index.js +1 -159
  53. package/lib/i18n/index.js.map +1 -1
  54. package/lib/index.d.ts +3 -3
  55. package/lib/index.js +3 -3
  56. package/lib/index.js.map +1 -1
  57. package/lib/pages/Error.d.ts +5 -0
  58. package/lib/{components → pages}/Error.js +4 -6
  59. package/lib/pages/Error.js.map +1 -0
  60. package/lib/pages/IdpReviewUserProfile.d.ts +5 -0
  61. package/lib/{components → pages}/IdpReviewUserProfile.js +4 -6
  62. package/lib/pages/IdpReviewUserProfile.js.map +1 -0
  63. package/lib/pages/Info.d.ts +5 -0
  64. package/lib/{components → pages}/Info.js +4 -6
  65. package/lib/pages/Info.js.map +1 -0
  66. package/lib/pages/Login.d.ts +5 -0
  67. package/lib/{components → pages}/Login.js +5 -7
  68. package/lib/pages/Login.js.map +1 -0
  69. package/lib/pages/LoginConfigTotp.d.ts +5 -0
  70. package/lib/{components → pages}/LoginConfigTotp.js +4 -6
  71. package/lib/pages/LoginConfigTotp.js.map +1 -0
  72. package/lib/pages/LoginIdpLinkConfirm.d.ts +5 -0
  73. package/lib/{components → pages}/LoginIdpLinkConfirm.js +4 -6
  74. package/lib/pages/LoginIdpLinkConfirm.js.map +1 -0
  75. package/lib/pages/LoginIdpLinkEmail.d.ts +5 -0
  76. package/lib/{components → pages}/LoginIdpLinkEmail.js +4 -6
  77. package/lib/pages/LoginIdpLinkEmail.js.map +1 -0
  78. package/lib/pages/LoginOtp.d.ts +5 -0
  79. package/lib/{components → pages}/LoginOtp.js +4 -6
  80. package/lib/pages/LoginOtp.js.map +1 -0
  81. package/lib/pages/LoginPageExpired.d.ts +5 -0
  82. package/lib/{components → pages}/LoginPageExpired.js +4 -6
  83. package/lib/pages/LoginPageExpired.js.map +1 -0
  84. package/lib/pages/LoginPassword.d.ts +5 -0
  85. package/lib/{components → pages}/LoginPassword.js +5 -7
  86. package/lib/pages/LoginPassword.js.map +1 -0
  87. package/lib/pages/LoginResetPassword.d.ts +5 -0
  88. package/lib/{components → pages}/LoginResetPassword.js +4 -6
  89. package/lib/pages/LoginResetPassword.js.map +1 -0
  90. package/lib/pages/LoginUpdatePassword.d.ts +5 -0
  91. package/lib/{components → pages}/LoginUpdatePassword.js +4 -6
  92. package/lib/pages/LoginUpdatePassword.js.map +1 -0
  93. package/lib/pages/LoginUpdateProfile.d.ts +5 -0
  94. package/lib/{components → pages}/LoginUpdateProfile.js +4 -6
  95. package/lib/pages/LoginUpdateProfile.js.map +1 -0
  96. package/lib/pages/LoginUsername.d.ts +5 -0
  97. package/lib/{components → pages}/LoginUsername.js +5 -7
  98. package/lib/pages/LoginUsername.js.map +1 -0
  99. package/lib/pages/LoginVerifyEmail.d.ts +5 -0
  100. package/lib/{components → pages}/LoginVerifyEmail.js +4 -6
  101. package/lib/pages/LoginVerifyEmail.js.map +1 -0
  102. package/lib/pages/LogoutConfirm.d.ts +5 -0
  103. package/lib/{components → pages}/LogoutConfirm.js +4 -6
  104. package/lib/pages/LogoutConfirm.js.map +1 -0
  105. package/lib/pages/Register.d.ts +5 -0
  106. package/lib/{components → pages}/Register.js +4 -6
  107. package/lib/pages/Register.js.map +1 -0
  108. package/lib/pages/RegisterUserProfile.d.ts +5 -0
  109. package/lib/{components → pages}/RegisterUserProfile.js +4 -6
  110. package/lib/pages/RegisterUserProfile.js.map +1 -0
  111. package/lib/pages/Terms.d.ts +19 -0
  112. package/lib/{components → pages}/Terms.js +20 -22
  113. package/lib/pages/Terms.js.map +1 -0
  114. package/lib/pages/UpdateUserProfile.d.ts +5 -0
  115. package/lib/{components → pages}/UpdateUserProfile.js +4 -6
  116. package/lib/pages/UpdateUserProfile.js.map +1 -0
  117. package/lib/pages/WebauthnAuthenticate.d.ts +5 -0
  118. package/lib/{components → pages}/WebauthnAuthenticate.js +5 -7
  119. package/lib/pages/WebauthnAuthenticate.js.map +1 -0
  120. package/lib/{components → pages}/shared/UserProfileCommons.d.ts +6 -6
  121. package/lib/{components → pages}/shared/UserProfileCommons.js +4 -4
  122. package/lib/pages/shared/UserProfileCommons.js.map +1 -0
  123. package/lib/tools/AndByDiscriminatingKey.d.ts +1 -1
  124. package/lib/tools/DeepPartial.d.ts +1 -1
  125. package/lib/tools/ReactComponent.d.ts +1 -1
  126. package/lib/tools/SetOptional.d.ts +1 -0
  127. package/lib/tools/SetOptional.js +2 -0
  128. package/lib/tools/SetOptional.js.map +1 -0
  129. package/lib/tools/clsx.d.ts +2 -3
  130. package/lib/tools/clsx.js +39 -3
  131. package/lib/tools/clsx.js.map +1 -1
  132. package/lib/tools/deepAssign.js +1 -1
  133. package/lib/tools/deepAssign.js.map +1 -1
  134. package/lib/tools/memoize.d.ts +7 -0
  135. package/lib/tools/memoize.js +38 -0
  136. package/lib/tools/memoize.js.map +1 -0
  137. package/lib/tools/useCallbackFactory.d.ts +15 -0
  138. package/lib/tools/useCallbackFactory.js +28 -0
  139. package/lib/tools/useCallbackFactory.js.map +1 -0
  140. package/lib/tools/useConst.d.ts +5 -0
  141. package/lib/tools/useConst.js +10 -0
  142. package/lib/tools/useConst.js.map +1 -0
  143. package/lib/tools/useConstCallback.d.ts +2 -0
  144. package/lib/tools/useConstCallback.js +8 -0
  145. package/lib/tools/useConstCallback.js.map +1 -0
  146. package/lib/tools/useCssAndCx.d.ts +1 -1
  147. package/lib/tsconfig.tsbuildinfo +1 -1
  148. package/lib/useFormValidationSlice.d.ts +4 -4
  149. package/lib/useFormValidationSlice.js +1 -1
  150. package/lib/useFormValidationSlice.js.map +1 -1
  151. package/package.json +133 -124
  152. package/src/bin/keycloakify/BuildOptions.ts +2 -2
  153. package/src/bin/keycloakify/generateFtl/generateFtl.ts +1 -1
  154. package/src/lib/{components/KcApp.tsx → KcApp.tsx} +29 -38
  155. package/src/lib/{components/KcProps.ts → KcProps.ts} +27 -1
  156. package/src/lib/{components/Template.tsx → Template.tsx} +111 -95
  157. package/src/lib/getKcContext/KcContextBase.ts +1 -1
  158. package/src/lib/getKcContext/{kcContextMocks/kcContextMocks.ts → kcContextMocks.ts} +9 -6
  159. package/src/lib/i18n/i18n.tsx +292 -0
  160. package/src/lib/i18n/index.tsx +1 -290
  161. package/src/lib/index.ts +3 -3
  162. package/src/lib/{components → pages}/Error.tsx +7 -18
  163. package/src/lib/{components → pages}/IdpReviewUserProfile.tsx +7 -18
  164. package/src/lib/{components → pages}/Info.tsx +7 -18
  165. package/src/lib/{components → pages}/Login.tsx +8 -20
  166. package/src/lib/{components → pages}/LoginConfigTotp.tsx +7 -18
  167. package/src/lib/{components → pages}/LoginIdpLinkConfirm.tsx +7 -18
  168. package/src/lib/{components → pages}/LoginIdpLinkEmail.tsx +7 -18
  169. package/src/lib/{components → pages}/LoginOtp.tsx +7 -18
  170. package/src/lib/{components → pages}/LoginPageExpired.tsx +7 -18
  171. package/src/lib/{components → pages}/LoginPassword.tsx +8 -19
  172. package/src/lib/{components → pages}/LoginResetPassword.tsx +7 -18
  173. package/src/lib/{components → pages}/LoginUpdatePassword.tsx +7 -18
  174. package/src/lib/{components → pages}/LoginUpdateProfile.tsx +7 -18
  175. package/src/lib/{components → pages}/LoginUsername.tsx +8 -19
  176. package/src/lib/{components → pages}/LoginVerifyEmail.tsx +7 -18
  177. package/src/lib/{components → pages}/LogoutConfirm.tsx +7 -18
  178. package/src/lib/{components → pages}/Register.tsx +7 -18
  179. package/src/lib/{components → pages}/RegisterUserProfile.tsx +7 -18
  180. package/src/lib/{components → pages}/Terms.tsx +50 -61
  181. package/src/lib/{components → pages}/UpdateUserProfile.tsx +7 -18
  182. package/src/lib/{components → pages}/WebauthnAuthenticate.tsx +9 -19
  183. package/src/lib/pages/shared/UserProfileCommons.tsx +178 -0
  184. package/src/lib/tools/SetOptional.ts +1 -0
  185. package/src/lib/tools/clsx.ts +42 -5
  186. package/src/lib/tools/deepAssign.ts +1 -1
  187. package/src/lib/tools/memoize.ts +55 -0
  188. package/src/lib/tools/useCallbackFactory.ts +45 -0
  189. package/src/lib/tools/useConst.ts +10 -0
  190. package/src/lib/tools/useConstCallback.ts +15 -0
  191. package/src/lib/useFormValidationSlice.tsx +4 -4
  192. package/bin/generate-i18n-messages.d.ts +0 -1
  193. package/bin/generate-i18n-messages.js +0 -126
  194. package/bin/generate-i18n-messages.js.map +0 -1
  195. package/bin/link_in_test_app.d.ts +0 -1
  196. package/bin/link_in_test_app.js +0 -141
  197. package/bin/link_in_test_app.js.map +0 -1
  198. package/lib/components/Error.d.ts +0 -13
  199. package/lib/components/Error.js.map +0 -1
  200. package/lib/components/IdpReviewUserProfile.d.ts +0 -13
  201. package/lib/components/IdpReviewUserProfile.js.map +0 -1
  202. package/lib/components/Info.d.ts +0 -13
  203. package/lib/components/Info.js.map +0 -1
  204. package/lib/components/KcApp.d.ts +0 -13
  205. package/lib/components/KcApp.js.map +0 -1
  206. package/lib/components/KcProps.js.map +0 -1
  207. package/lib/components/Login.d.ts +0 -13
  208. package/lib/components/Login.js.map +0 -1
  209. package/lib/components/LoginConfigTotp.d.ts +0 -13
  210. package/lib/components/LoginConfigTotp.js.map +0 -1
  211. package/lib/components/LoginIdpLinkConfirm.d.ts +0 -13
  212. package/lib/components/LoginIdpLinkConfirm.js.map +0 -1
  213. package/lib/components/LoginIdpLinkEmail.d.ts +0 -13
  214. package/lib/components/LoginIdpLinkEmail.js.map +0 -1
  215. package/lib/components/LoginOtp.d.ts +0 -13
  216. package/lib/components/LoginOtp.js.map +0 -1
  217. package/lib/components/LoginPageExpired.d.ts +0 -13
  218. package/lib/components/LoginPageExpired.js.map +0 -1
  219. package/lib/components/LoginPassword.d.ts +0 -13
  220. package/lib/components/LoginPassword.js.map +0 -1
  221. package/lib/components/LoginResetPassword.d.ts +0 -13
  222. package/lib/components/LoginResetPassword.js.map +0 -1
  223. package/lib/components/LoginUpdatePassword.d.ts +0 -13
  224. package/lib/components/LoginUpdatePassword.js.map +0 -1
  225. package/lib/components/LoginUpdateProfile.d.ts +0 -13
  226. package/lib/components/LoginUpdateProfile.js.map +0 -1
  227. package/lib/components/LoginUsername.d.ts +0 -13
  228. package/lib/components/LoginUsername.js.map +0 -1
  229. package/lib/components/LoginVerifyEmail.d.ts +0 -13
  230. package/lib/components/LoginVerifyEmail.js.map +0 -1
  231. package/lib/components/LogoutConfirm.d.ts +0 -13
  232. package/lib/components/LogoutConfirm.js.map +0 -1
  233. package/lib/components/Register.d.ts +0 -13
  234. package/lib/components/Register.js.map +0 -1
  235. package/lib/components/RegisterUserProfile.d.ts +0 -13
  236. package/lib/components/RegisterUserProfile.js.map +0 -1
  237. package/lib/components/Template.d.ts +0 -25
  238. package/lib/components/Template.js.map +0 -1
  239. package/lib/components/Terms.d.ts +0 -27
  240. package/lib/components/Terms.js.map +0 -1
  241. package/lib/components/UpdateUserProfile.d.ts +0 -13
  242. package/lib/components/UpdateUserProfile.js.map +0 -1
  243. package/lib/components/WebauthnAuthenticate.d.ts +0 -13
  244. package/lib/components/WebauthnAuthenticate.js.map +0 -1
  245. package/lib/components/shared/UserProfileCommons.js.map +0 -1
  246. package/lib/getKcContext/kcContextMocks/index.d.ts +0 -1
  247. package/lib/getKcContext/kcContextMocks/index.js +0 -2
  248. package/lib/getKcContext/kcContextMocks/index.js.map +0 -1
  249. package/lib/getKcContext/kcContextMocks/kcContextMocks.js.map +0 -1
  250. package/src/bin/generate-i18n-messages.ts +0 -86
  251. package/src/bin/link_in_test_app.ts +0 -128
  252. package/src/lib/components/shared/UserProfileCommons.tsx +0 -173
  253. package/src/lib/getKcContext/kcContextMocks/index.ts +0 -1
@@ -1,32 +1,13 @@
1
- import React, { useReducer, useEffect, memo } from "react";
2
- import type { ReactNode } from "react";
3
- import type { KcContextBase } from "../getKcContext/KcContextBase";
4
- import { assert } from "../tools/assert";
5
- import { useCallbackFactory } from "powerhooks/useCallbackFactory";
6
- import { headInsert } from "../tools/headInsert";
7
- import { pathJoin } from "../../bin/tools/pathJoin";
8
- import { useConstCallback } from "powerhooks/useConstCallback";
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
- const Template = memo((props: TemplateProps) => {
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 [isExtraCssLoaded, setExtraCssLoaded] = useReducer(() => true, false);
54
-
55
- useEffect(() => {
56
- if (!doFetchDefaultThemeResources) {
57
- setExtraCssLoaded();
58
- return;
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 (!isExtraCssLoaded) {
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
- <a href="#" onClick={onChangeLanguageClickFactory(languageTag)}>
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
- <a href="#" id="try-another-way" onClick={onTryAnotherWayClick}>
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
- export default Template;
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 "../components/KcProps";
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 "../KcContextBase";
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 "../../../bin/mockTestingResourcesPath";
4
+ import { mockTestingResourcesCommonPath, mockTestingResourcesPath } from "../../bin/mockTestingResourcesPath";
5
5
  import { id } from "tsafe/id";
6
- import { pathJoin } from "../../../bin/tools/pathJoin";
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": (...[, x]) => x,
121
- "existsError": () => true,
120
+ "printIfExists": () => {
121
+ console.log("coucou");
122
+ return undefined;
123
+ },
124
+ "existsError": () => false,
122
125
  "get": key => `Fake error for ${key}`,
123
- "exists": () => true
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
+ };