keycloakify 10.0.0-rc.37 → 10.0.0-rc.38

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 (268) hide show
  1. package/account/Fallback.d.ts +1 -2
  2. package/account/Fallback.js.map +1 -1
  3. package/account/KcContext/KcContext.d.ts +6 -6
  4. package/account/KcContext/getKcContextMock.d.ts +9 -9
  5. package/account/KcContext/getKcContextMock.js +3 -3
  6. package/account/KcContext/getKcContextMock.js.map +1 -1
  7. package/account/Template.d.ts +1 -2
  8. package/account/Template.js +7 -6
  9. package/account/Template.js.map +1 -1
  10. package/account/TemplateProps.d.ts +1 -3
  11. package/account/i18n/i18n.d.ts +9 -4
  12. package/account/i18n/i18n.js +132 -87
  13. package/account/i18n/i18n.js.map +1 -1
  14. package/account/i18n/index.d.ts +9 -2
  15. package/account/i18n/index.js +5 -1
  16. package/account/i18n/index.js.map +1 -1
  17. package/account/lib/kcClsx.d.ts +9 -0
  18. package/account/lib/{useGetClassName.js → kcClsx.js} +3 -3
  19. package/account/lib/kcClsx.js.map +1 -0
  20. package/account/pages/Account.d.ts +1 -2
  21. package/account/pages/Account.js +9 -7
  22. package/account/pages/Account.js.map +1 -1
  23. package/account/pages/Applications.d.ts +1 -2
  24. package/account/pages/Applications.js +7 -7
  25. package/account/pages/Applications.js.map +1 -1
  26. package/account/pages/FederatedIdentity.d.ts +1 -2
  27. package/account/pages/FederatedIdentity.js +4 -3
  28. package/account/pages/FederatedIdentity.js.map +1 -1
  29. package/account/pages/Log.d.ts +1 -2
  30. package/account/pages/Log.js +6 -5
  31. package/account/pages/Log.js.map +1 -1
  32. package/account/pages/PageProps.d.ts +2 -4
  33. package/account/pages/Password.d.ts +1 -2
  34. package/account/pages/Password.js +10 -9
  35. package/account/pages/Password.js.map +1 -1
  36. package/account/pages/Sessions.d.ts +1 -2
  37. package/account/pages/Sessions.js +6 -6
  38. package/account/pages/Sessions.js.map +1 -1
  39. package/account/pages/Totp.d.ts +1 -2
  40. package/account/pages/Totp.js +6 -5
  41. package/account/pages/Totp.js.map +1 -1
  42. package/bin/{314.index.js → 21.index.js} +162 -4
  43. package/bin/{430.index.js → 214.index.js} +161 -2
  44. package/bin/3.index.js +81 -65
  45. package/bin/526.index.js +3 -2
  46. package/bin/538.index.js +552 -0
  47. package/bin/795.index.js +3 -2
  48. package/bin/{890.index.js → 941.index.js} +2 -159
  49. package/bin/main.js +19 -7
  50. package/lib/getKcClsx.d.ts +11 -0
  51. package/lib/getKcClsx.js +44 -0
  52. package/lib/getKcClsx.js.map +1 -0
  53. package/login/Fallback.d.ts +1 -2
  54. package/login/Fallback.js.map +1 -1
  55. package/login/KcContext/KcContext.d.ts +6 -6
  56. package/login/KcContext/getKcContextMock.d.ts +9 -9
  57. package/login/KcContext/getKcContextMock.js +3 -3
  58. package/login/KcContext/getKcContextMock.js.map +1 -1
  59. package/login/Template.d.ts +1 -2
  60. package/login/Template.js +10 -9
  61. package/login/Template.js.map +1 -1
  62. package/login/TemplateProps.d.ts +1 -3
  63. package/login/UserProfileFormFields.d.ts +5 -5
  64. package/login/UserProfileFormFields.js +35 -34
  65. package/login/UserProfileFormFields.js.map +1 -1
  66. package/login/i18n/i18n.d.ts +9 -4
  67. package/login/i18n/i18n.js +136 -91
  68. package/login/i18n/i18n.js.map +1 -1
  69. package/login/i18n/index.d.ts +9 -2
  70. package/login/i18n/index.js +5 -1
  71. package/login/i18n/index.js.map +1 -1
  72. package/login/lib/kcClsx.d.ts +9 -0
  73. package/login/lib/{useGetClassName.js → kcClsx.js} +3 -3
  74. package/login/lib/kcClsx.js.map +1 -0
  75. package/login/lib/useUserProfileForm.d.ts +9 -6
  76. package/login/lib/useUserProfileForm.js +7 -5
  77. package/login/lib/useUserProfileForm.js.map +1 -1
  78. package/login/pages/Code.d.ts +1 -2
  79. package/login/pages/Code.js +6 -5
  80. package/login/pages/Code.js.map +1 -1
  81. package/login/pages/DeleteAccountConfirm.d.ts +1 -2
  82. package/login/pages/DeleteAccountConfirm.js +7 -7
  83. package/login/pages/DeleteAccountConfirm.js.map +1 -1
  84. package/login/pages/DeleteCredential.d.ts +1 -2
  85. package/login/pages/DeleteCredential.js +6 -6
  86. package/login/pages/DeleteCredential.js.map +1 -1
  87. package/login/pages/Error.d.ts +1 -2
  88. package/login/pages/Error.js +4 -3
  89. package/login/pages/Error.js.map +1 -1
  90. package/login/pages/FrontchannelLogout.d.ts +1 -2
  91. package/login/pages/FrontchannelLogout.js +4 -3
  92. package/login/pages/FrontchannelLogout.js.map +1 -1
  93. package/login/pages/IdpReviewUserProfile.d.ts +1 -2
  94. package/login/pages/IdpReviewUserProfile.js +6 -6
  95. package/login/pages/IdpReviewUserProfile.js.map +1 -1
  96. package/login/pages/Info.d.ts +1 -2
  97. package/login/pages/Info.js +4 -3
  98. package/login/pages/Info.js.map +1 -1
  99. package/login/pages/Login.d.ts +1 -2
  100. package/login/pages/Login.js +10 -8
  101. package/login/pages/Login.js.map +1 -1
  102. package/login/pages/LoginConfigTotp.d.ts +1 -2
  103. package/login/pages/LoginConfigTotp.js +8 -7
  104. package/login/pages/LoginConfigTotp.js.map +1 -1
  105. package/login/pages/LoginIdpLinkConfirm.d.ts +1 -2
  106. package/login/pages/LoginIdpLinkConfirm.js +6 -6
  107. package/login/pages/LoginIdpLinkConfirm.js.map +1 -1
  108. package/login/pages/LoginIdpLinkEmail.d.ts +1 -2
  109. package/login/pages/LoginIdpLinkEmail.js +4 -3
  110. package/login/pages/LoginIdpLinkEmail.js.map +1 -1
  111. package/login/pages/LoginOauth2DeviceVerifyUserCode.d.ts +1 -2
  112. package/login/pages/LoginOauth2DeviceVerifyUserCode.js +6 -6
  113. package/login/pages/LoginOauth2DeviceVerifyUserCode.js.map +1 -1
  114. package/login/pages/LoginOauthGrant.d.ts +1 -2
  115. package/login/pages/LoginOauthGrant.js +7 -7
  116. package/login/pages/LoginOauthGrant.js.map +1 -1
  117. package/login/pages/LoginOtp.d.ts +1 -2
  118. package/login/pages/LoginOtp.js +6 -6
  119. package/login/pages/LoginOtp.js.map +1 -1
  120. package/login/pages/LoginPageExpired.d.ts +1 -2
  121. package/login/pages/LoginPageExpired.js +4 -3
  122. package/login/pages/LoginPageExpired.js.map +1 -1
  123. package/login/pages/LoginPassword.d.ts +1 -2
  124. package/login/pages/LoginPassword.js +9 -7
  125. package/login/pages/LoginPassword.js.map +1 -1
  126. package/login/pages/LoginRecoveryAuthnCodeConfig.d.ts +1 -2
  127. package/login/pages/LoginRecoveryAuthnCodeConfig.js +9 -7
  128. package/login/pages/LoginRecoveryAuthnCodeConfig.js.map +1 -1
  129. package/login/pages/LoginRecoveryAuthnCodeInput.d.ts +1 -2
  130. package/login/pages/LoginRecoveryAuthnCodeInput.js +6 -6
  131. package/login/pages/LoginRecoveryAuthnCodeInput.js.map +1 -1
  132. package/login/pages/LoginResetOtp.d.ts +1 -2
  133. package/login/pages/LoginResetOtp.js +6 -6
  134. package/login/pages/LoginResetOtp.js.map +1 -1
  135. package/login/pages/LoginResetPassword.d.ts +1 -2
  136. package/login/pages/LoginResetPassword.js +7 -7
  137. package/login/pages/LoginResetPassword.js.map +1 -1
  138. package/login/pages/LoginUpdatePassword.d.ts +1 -2
  139. package/login/pages/LoginUpdatePassword.js +10 -9
  140. package/login/pages/LoginUpdatePassword.js.map +1 -1
  141. package/login/pages/LoginUpdateProfile.d.ts +1 -2
  142. package/login/pages/LoginUpdateProfile.js +7 -12
  143. package/login/pages/LoginUpdateProfile.js.map +1 -1
  144. package/login/pages/LoginUsername.d.ts +1 -2
  145. package/login/pages/LoginUsername.js +8 -7
  146. package/login/pages/LoginUsername.js.map +1 -1
  147. package/login/pages/LoginVerifyEmail.d.ts +1 -2
  148. package/login/pages/LoginVerifyEmail.js +4 -3
  149. package/login/pages/LoginVerifyEmail.js.map +1 -1
  150. package/login/pages/LoginX509Info.d.ts +1 -2
  151. package/login/pages/LoginX509Info.js +6 -6
  152. package/login/pages/LoginX509Info.js.map +1 -1
  153. package/login/pages/LogoutConfirm.d.ts +1 -2
  154. package/login/pages/LogoutConfirm.js +6 -6
  155. package/login/pages/LogoutConfirm.js.map +1 -1
  156. package/login/pages/PageProps.d.ts +2 -4
  157. package/login/pages/Register.d.ts +1 -2
  158. package/login/pages/Register.js +8 -16
  159. package/login/pages/Register.js.map +1 -1
  160. package/login/pages/SamlPostForm.d.ts +1 -2
  161. package/login/pages/SamlPostForm.js +4 -3
  162. package/login/pages/SamlPostForm.js.map +1 -1
  163. package/login/pages/SelectAuthenticator.d.ts +1 -2
  164. package/login/pages/SelectAuthenticator.js +6 -9
  165. package/login/pages/SelectAuthenticator.js.map +1 -1
  166. package/login/pages/Terms.d.ts +1 -2
  167. package/login/pages/Terms.js +6 -6
  168. package/login/pages/Terms.js.map +1 -1
  169. package/login/pages/UpdateEmail.d.ts +1 -2
  170. package/login/pages/UpdateEmail.js +8 -12
  171. package/login/pages/UpdateEmail.js.map +1 -1
  172. package/login/pages/WebauthnAuthenticate.d.ts +1 -2
  173. package/login/pages/WebauthnAuthenticate.js +13 -12
  174. package/login/pages/WebauthnAuthenticate.js.map +1 -1
  175. package/login/pages/WebauthnError.d.ts +1 -2
  176. package/login/pages/WebauthnError.js +7 -7
  177. package/login/pages/WebauthnError.js.map +1 -1
  178. package/login/pages/WebauthnRegister.d.ts +1 -2
  179. package/login/pages/WebauthnRegister.js +9 -8
  180. package/login/pages/WebauthnRegister.js.map +1 -1
  181. package/package.json +24 -16
  182. package/src/account/Fallback.tsx +1 -2
  183. package/src/account/KcContext/KcContext.ts +7 -7
  184. package/src/account/KcContext/getKcContextMock.ts +13 -24
  185. package/src/account/Template.tsx +8 -8
  186. package/src/account/TemplateProps.ts +1 -6
  187. package/src/account/i18n/i18n.tsx +204 -125
  188. package/src/account/i18n/index.ts +10 -2
  189. package/src/account/lib/{useGetClassName.ts → kcClsx.ts} +6 -2
  190. package/src/account/pages/Account.tsx +15 -21
  191. package/src/account/pages/Applications.tsx +8 -9
  192. package/src/account/pages/FederatedIdentity.tsx +5 -5
  193. package/src/account/pages/Log.tsx +8 -8
  194. package/src/account/pages/PageProps.ts +2 -4
  195. package/src/account/pages/Password.tsx +13 -16
  196. package/src/account/pages/Sessions.tsx +9 -10
  197. package/src/account/pages/Totp.tsx +19 -28
  198. package/src/bin/keycloakify/generateSrcMainResources/generateMessageProperties.ts +2 -66
  199. package/src/bin/keycloakify/generateSrcMainResources/generateSrcMainResourcesForMainTheme.ts +7 -1
  200. package/src/bin/main.ts +15 -0
  201. package/src/bin/shared/buildOptions.ts +5 -2
  202. package/src/bin/shared/generateKcGenTs.ts +61 -0
  203. package/src/bin/tools/escapeStringForPropertiesFile.ts +64 -0
  204. package/src/bin/update-kc-gen.ts +13 -0
  205. package/src/lib/getKcClsx.ts +75 -0
  206. package/src/login/Fallback.tsx +1 -2
  207. package/src/login/KcContext/KcContext.ts +7 -7
  208. package/src/login/KcContext/getKcContextMock.ts +13 -24
  209. package/src/login/Template.tsx +36 -37
  210. package/src/login/TemplateProps.ts +1 -6
  211. package/src/login/UserProfileFormFields.tsx +66 -81
  212. package/src/login/i18n/i18n.tsx +208 -129
  213. package/src/login/i18n/index.ts +10 -2
  214. package/src/login/lib/{useGetClassName.ts → kcClsx.ts} +6 -2
  215. package/src/login/lib/useUserProfileForm.tsx +29 -21
  216. package/src/login/pages/Code.tsx +10 -8
  217. package/src/login/pages/DeleteAccountConfirm.tsx +9 -10
  218. package/src/login/pages/DeleteCredential.tsx +11 -10
  219. package/src/login/pages/Error.tsx +5 -5
  220. package/src/login/pages/FrontchannelLogout.tsx +7 -5
  221. package/src/login/pages/IdpReviewUserProfile.tsx +16 -25
  222. package/src/login/pages/Info.tsx +7 -5
  223. package/src/login/pages/Login.tsx +34 -53
  224. package/src/login/pages/LoginConfigTotp.tsx +30 -38
  225. package/src/login/pages/LoginIdpLinkConfirm.tsx +10 -21
  226. package/src/login/pages/LoginIdpLinkEmail.tsx +5 -5
  227. package/src/login/pages/LoginOauth2DeviceVerifyUserCode.tsx +19 -24
  228. package/src/login/pages/LoginOauthGrant.tsx +14 -21
  229. package/src/login/pages/LoginOtp.tsx +29 -33
  230. package/src/login/pages/LoginPageExpired.tsx +5 -5
  231. package/src/login/pages/LoginPassword.tsx +23 -33
  232. package/src/login/pages/LoginRecoveryAuthnCodeConfig.tsx +21 -25
  233. package/src/login/pages/LoginRecoveryAuthnCodeInput.tsx +21 -25
  234. package/src/login/pages/LoginResetOtp.tsx +21 -25
  235. package/src/login/pages/LoginResetPassword.tsx +21 -25
  236. package/src/login/pages/LoginUpdatePassword.tsx +42 -52
  237. package/src/login/pages/LoginUpdateProfile.tsx +26 -33
  238. package/src/login/pages/LoginUsername.tsx +23 -35
  239. package/src/login/pages/LoginVerifyEmail.tsx +7 -5
  240. package/src/login/pages/LoginX509Info.tsx +27 -36
  241. package/src/login/pages/LogoutConfirm.tsx +11 -17
  242. package/src/login/pages/PageProps.ts +2 -4
  243. package/src/login/pages/Register.tsx +24 -49
  244. package/src/login/pages/SamlPostForm.tsx +5 -5
  245. package/src/login/pages/SelectAuthenticator.tsx +24 -26
  246. package/src/login/pages/Terms.tsx +9 -16
  247. package/src/login/pages/UpdateEmail.tsx +26 -36
  248. package/src/login/pages/WebauthnAuthenticate.tsx +26 -32
  249. package/src/login/pages/WebauthnError.tsx +11 -22
  250. package/src/login/pages/WebauthnRegister.tsx +20 -28
  251. package/src/tools/clsx.ts +6 -48
  252. package/src/tools/clsx_withTransform.ts +55 -0
  253. package/src/vite-plugin/vite-plugin.ts +14 -6
  254. package/tools/clsx.d.ts +3 -2
  255. package/tools/clsx.js +5 -41
  256. package/tools/clsx.js.map +1 -1
  257. package/tools/clsx_withTransform.d.ts +5 -0
  258. package/tools/clsx_withTransform.js +43 -0
  259. package/tools/clsx_withTransform.js.map +1 -0
  260. package/vite-plugin/index.js +167 -37
  261. package/account/lib/useGetClassName.d.ts +0 -7
  262. package/account/lib/useGetClassName.js.map +0 -1
  263. package/lib/useGetClassName.d.ts +0 -10
  264. package/lib/useGetClassName.js +0 -14
  265. package/lib/useGetClassName.js.map +0 -1
  266. package/login/lib/useGetClassName.d.ts +0 -7
  267. package/login/lib/useGetClassName.js.map +0 -1
  268. package/src/lib/useGetClassName.ts +0 -27
@@ -1,27 +1,29 @@
1
1
  import { useState } from "react";
2
2
  import { clsx } from "keycloakify/tools/clsx";
3
- import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
3
+ import { getKcClsx } from "keycloakify/login/lib/kcClsx";
4
4
  import type { PageProps } from "keycloakify/login/pages/PageProps";
5
5
  import type { KcContext } from "../KcContext";
6
- import type { I18n } from "../i18n";
6
+ import { useI18n } from "../i18n";
7
7
 
8
- export default function LoginUsername(props: PageProps<Extract<KcContext, { pageId: "login-username.ftl" }>, I18n>) {
9
- const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
8
+ export default function LoginUsername(props: PageProps<Extract<KcContext, { pageId: "login-username.ftl" }>>) {
9
+ const { kcContext, doUseDefaultCss, Template, classes } = props;
10
10
 
11
- const { getClassName } = useGetClassName({
11
+ const { kcClsx } = getKcClsx({
12
12
  doUseDefaultCss,
13
13
  classes
14
14
  });
15
15
 
16
16
  const { social, realm, url, usernameHidden, login, registrationDisabled, messagesPerField } = kcContext;
17
17
 
18
- const { msg, msgStr } = i18n;
18
+ const { msg, msgStr } = useI18n({ kcContext });
19
19
 
20
20
  const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
21
21
 
22
22
  return (
23
23
  <Template
24
- {...{ kcContext, i18n, doUseDefaultCss, classes }}
24
+ kcContext={kcContext}
25
+ doUseDefaultCss={doUseDefaultCss}
26
+ classes={classes}
25
27
  displayMessage={!messagesPerField.existsError("username")}
26
28
  displayInfo={realm.password && realm.registrationAllowed && !registrationDisabled}
27
29
  infoNode={
@@ -38,32 +40,23 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
38
40
  socialProvidersNode={
39
41
  <>
40
42
  {realm.password && social.providers?.length && (
41
- <div id="kc-social-providers" className={getClassName("kcFormSocialAccountSectionClass")}>
43
+ <div id="kc-social-providers" className={kcClsx("kcFormSocialAccountSectionClass")}>
42
44
  <hr />
43
45
  <h2>{msg("identity-provider-login-label")}</h2>
44
- <ul
45
- className={clsx(
46
- getClassName("kcFormSocialAccountListClass"),
47
- social.providers.length > 3 && getClassName("kcFormSocialAccountListGridClass")
48
- )}
49
- >
46
+ <ul className={kcClsx("kcFormSocialAccountListClass", social.providers.length > 3 && "kcFormSocialAccountListGridClass")}>
50
47
  {social.providers.map((...[p, , providers]) => (
51
48
  <li key={p.alias}>
52
49
  <a
53
50
  id={`social-${p.alias}`}
54
- className={clsx(
55
- getClassName("kcFormSocialAccountListButtonClass"),
56
- providers.length > 3 && getClassName("kcFormSocialAccountGridItem")
51
+ className={kcClsx(
52
+ "kcFormSocialAccountListButtonClass",
53
+ providers.length > 3 && "kcFormSocialAccountGridItem"
57
54
  )}
58
55
  type="button"
59
56
  href={p.loginUrl}
60
57
  >
61
- {p.iconClasses && (
62
- <i className={clsx(getClassName("kcCommonLogoIdP"), p.iconClasses)} aria-hidden="true"></i>
63
- )}
64
- <span
65
- className={clsx(getClassName("kcFormSocialAccountNameClass"), p.iconClasses && "kc-social-icon-text")}
66
- >
58
+ {p.iconClasses && <i className={clsx(kcClsx("kcCommonLogoIdP"), p.iconClasses)} aria-hidden="true"></i>}
59
+ <span className={clsx(kcClsx("kcFormSocialAccountNameClass"), p.iconClasses && "kc-social-icon-text")}>
67
60
  {p.displayName}
68
61
  </span>
69
62
  </a>
@@ -88,8 +81,8 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
88
81
  method="post"
89
82
  >
90
83
  {!usernameHidden && (
91
- <div className={getClassName("kcFormGroupClass")}>
92
- <label htmlFor="username" className={getClassName("kcLabelClass")}>
84
+ <div className={kcClsx("kcFormGroupClass")}>
85
+ <label htmlFor="username" className={kcClsx("kcLabelClass")}>
93
86
  {!realm.loginWithEmailAllowed
94
87
  ? msg("username")
95
88
  : !realm.registrationEmailAsUsername
@@ -99,7 +92,7 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
99
92
  <input
100
93
  tabIndex={2}
101
94
  id="username"
102
- className={getClassName("kcInputClass")}
95
+ className={kcClsx("kcInputClass")}
103
96
  name="username"
104
97
  defaultValue={login.username ?? ""}
105
98
  type="text"
@@ -108,14 +101,14 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
108
101
  aria-invalid={messagesPerField.existsError("username")}
109
102
  />
110
103
  {messagesPerField.existsError("username") && (
111
- <span id="input-error" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
104
+ <span id="input-error" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
112
105
  {messagesPerField.getFirstError("username")}
113
106
  </span>
114
107
  )}
115
108
  </div>
116
109
  )}
117
110
 
118
- <div className={clsx(getClassName("kcFormGroupClass"), getClassName("kcFormSettingClass"))}>
111
+ <div className={kcClsx("kcFormGroupClass", "kcFormSettingClass")}>
119
112
  <div id="kc-form-options">
120
113
  {realm.rememberMe && !usernameHidden && (
121
114
  <div className="checkbox">
@@ -134,16 +127,11 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
134
127
  </div>
135
128
  </div>
136
129
 
137
- <div id="kc-form-buttons" className={getClassName("kcFormGroupClass")}>
130
+ <div id="kc-form-buttons" className={kcClsx("kcFormGroupClass")}>
138
131
  <input
139
132
  tabIndex={4}
140
133
  disabled={isLoginButtonDisabled}
141
- className={clsx(
142
- getClassName("kcButtonClass"),
143
- getClassName("kcButtonPrimaryClass"),
144
- getClassName("kcButtonBlockClass"),
145
- getClassName("kcButtonLargeClass")
146
- )}
134
+ className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
147
135
  name="login"
148
136
  id="kc-login"
149
137
  type="submit"
@@ -1,17 +1,19 @@
1
1
  import type { PageProps } from "keycloakify/login/pages/PageProps";
2
2
  import type { KcContext } from "../KcContext";
3
- import type { I18n } from "../i18n";
3
+ import { useI18n } from "../i18n";
4
4
 
5
- export default function LoginVerifyEmail(props: PageProps<Extract<KcContext, { pageId: "login-verify-email.ftl" }>, I18n>) {
6
- const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
5
+ export default function LoginVerifyEmail(props: PageProps<Extract<KcContext, { pageId: "login-verify-email.ftl" }>>) {
6
+ const { kcContext, doUseDefaultCss, Template, classes } = props;
7
7
 
8
- const { msg } = i18n;
8
+ const { msg } = useI18n({ kcContext });
9
9
 
10
10
  const { url, user } = kcContext;
11
11
 
12
12
  return (
13
13
  <Template
14
- {...{ kcContext, i18n, doUseDefaultCss, classes }}
14
+ kcContext={kcContext}
15
+ doUseDefaultCss={doUseDefaultCss}
16
+ classes={classes}
15
17
  displayInfo
16
18
  headerNode={msg("emailVerifyTitle")}
17
19
  infoNode={
@@ -1,72 +1,67 @@
1
- import { clsx } from "keycloakify/tools/clsx";
2
- import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
1
+ import { getKcClsx } from "keycloakify/login/lib/kcClsx";
3
2
  import type { PageProps } from "keycloakify/login/pages/PageProps";
4
3
  import type { KcContext } from "../KcContext";
5
- import type { I18n } from "../i18n";
4
+ import { useI18n } from "../i18n";
6
5
 
7
- export default function LoginX509Info(props: PageProps<Extract<KcContext, { pageId: "login-x509-info.ftl" }>, I18n>) {
8
- const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
6
+ export default function LoginX509Info(props: PageProps<Extract<KcContext, { pageId: "login-x509-info.ftl" }>>) {
7
+ const { kcContext, doUseDefaultCss, Template, classes } = props;
9
8
 
10
- const { getClassName } = useGetClassName({
9
+ const { kcClsx } = getKcClsx({
11
10
  doUseDefaultCss,
12
11
  classes
13
12
  });
14
13
 
15
14
  const { url, x509 } = kcContext;
16
15
 
17
- const { msg, msgStr } = i18n;
16
+ const { msg, msgStr } = useI18n({ kcContext });
18
17
 
19
18
  return (
20
- <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} headerNode={msg("doLogIn")}>
21
- <form id="kc-x509-login-info" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
22
- <div className={getClassName("kcFormGroupClass")}>
23
- <div className={getClassName("kcLabelWrapperClass")}>
24
- <label htmlFor="certificate_subjectDN" className={getClassName("kcLabelClass")}>
19
+ <Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("doLogIn")}>
20
+ <form id="kc-x509-login-info" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
21
+ <div className={kcClsx("kcFormGroupClass")}>
22
+ <div className={kcClsx("kcLabelWrapperClass")}>
23
+ <label htmlFor="certificate_subjectDN" className={kcClsx("kcLabelClass")}>
25
24
  {msg("clientCertificate")}
26
25
  </label>
27
26
  </div>
28
27
  {x509.formData.subjectDN ? (
29
- <div className={getClassName("kcLabelWrapperClass")}>
30
- <label id="certificate_subjectDN" className={getClassName("kcLabelClass")}>
28
+ <div className={kcClsx("kcLabelWrapperClass")}>
29
+ <label id="certificate_subjectDN" className={kcClsx("kcLabelClass")}>
31
30
  {x509.formData.subjectDN}
32
31
  </label>
33
32
  </div>
34
33
  ) : (
35
- <div className={getClassName("kcLabelWrapperClass")}>
36
- <label id="certificate_subjectDN" className={getClassName("kcLabelClass")}>
34
+ <div className={kcClsx("kcLabelWrapperClass")}>
35
+ <label id="certificate_subjectDN" className={kcClsx("kcLabelClass")}>
37
36
  {msg("noCertificate")}
38
37
  </label>
39
38
  </div>
40
39
  )}
41
40
  </div>
42
- <div className={getClassName("kcFormGroupClass")}>
41
+ <div className={kcClsx("kcFormGroupClass")}>
43
42
  {x509.formData.isUserEnabled && (
44
43
  <>
45
- <div className={getClassName("kcLabelWrapperClass")}>
46
- <label htmlFor="username" className={getClassName("kcLabelClass")}>
44
+ <div className={kcClsx("kcLabelWrapperClass")}>
45
+ <label htmlFor="username" className={kcClsx("kcLabelClass")}>
47
46
  {msg("doX509Login")}
48
47
  </label>
49
48
  </div>
50
- <div className={getClassName("kcLabelWrapperClass")}>
51
- <label id="username" className={getClassName("kcLabelClass")}>
49
+ <div className={kcClsx("kcLabelWrapperClass")}>
50
+ <label id="username" className={kcClsx("kcLabelClass")}>
52
51
  {x509.formData.username}
53
52
  </label>
54
53
  </div>
55
54
  </>
56
55
  )}
57
56
  </div>
58
- <div className={getClassName("kcFormGroupClass")}>
59
- <div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
60
- <div className={getClassName("kcFormOptionsWrapperClass")} />
57
+ <div className={kcClsx("kcFormGroupClass")}>
58
+ <div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
59
+ <div className={kcClsx("kcFormOptionsWrapperClass")} />
61
60
  </div>
62
- <div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
63
- <div className={getClassName("kcFormButtonsWrapperClass")}>
61
+ <div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
62
+ <div className={kcClsx("kcFormButtonsWrapperClass")}>
64
63
  <input
65
- className={clsx(
66
- getClassName("kcButtonClass"),
67
- getClassName("kcButtonPrimaryClass"),
68
- getClassName("kcButtonLargeClass")
69
- )}
64
+ className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
70
65
  name="login"
71
66
  id="kc-login"
72
67
  type="submit"
@@ -74,11 +69,7 @@ export default function LoginX509Info(props: PageProps<Extract<KcContext, { page
74
69
  />
75
70
  {x509.formData.isUserEnabled && (
76
71
  <input
77
- className={clsx(
78
- getClassName("kcButtonClass"),
79
- getClassName("kcButtonDefaultClass"),
80
- getClassName("kcButtonLargeClass")
81
- )}
72
+ className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
82
73
  name="cancel"
83
74
  id="kc-cancel"
84
75
  type="submit"
@@ -1,40 +1,34 @@
1
- import { clsx } from "keycloakify/tools/clsx";
2
- import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
1
+ import { getKcClsx } from "keycloakify/login/lib/kcClsx";
3
2
  import type { PageProps } from "keycloakify/login/pages/PageProps";
4
3
  import type { KcContext } from "../KcContext";
5
- import type { I18n } from "../i18n";
4
+ import { useI18n } from "../i18n";
6
5
 
7
- export default function LogoutConfirm(props: PageProps<Extract<KcContext, { pageId: "logout-confirm.ftl" }>, I18n>) {
8
- const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
6
+ export default function LogoutConfirm(props: PageProps<Extract<KcContext, { pageId: "logout-confirm.ftl" }>>) {
7
+ const { kcContext, doUseDefaultCss, Template, classes } = props;
9
8
 
10
- const { getClassName } = useGetClassName({
9
+ const { kcClsx } = getKcClsx({
11
10
  doUseDefaultCss,
12
11
  classes
13
12
  });
14
13
 
15
14
  const { url, client, logoutConfirm } = kcContext;
16
15
 
17
- const { msg, msgStr } = i18n;
16
+ const { msg, msgStr } = useI18n({ kcContext });
18
17
 
19
18
  return (
20
- <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} headerNode={msg("logoutConfirmTitle")}>
19
+ <Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("logoutConfirmTitle")}>
21
20
  <div id="kc-logout-confirm" className="content-area">
22
21
  <p className="instruction">{msg("logoutConfirmHeader")}</p>
23
22
  <form className="form-actions" action={url.logoutConfirmAction} method="POST">
24
23
  <input type="hidden" name="session_code" value={logoutConfirm.code} />
25
- <div className={getClassName("kcFormGroupClass")}>
24
+ <div className={kcClsx("kcFormGroupClass")}>
26
25
  <div id="kc-form-options">
27
- <div className={getClassName("kcFormOptionsWrapperClass")}></div>
26
+ <div className={kcClsx("kcFormOptionsWrapperClass")}></div>
28
27
  </div>
29
- <div id="kc-form-buttons" className={getClassName("kcFormGroupClass")}>
28
+ <div id="kc-form-buttons" className={kcClsx("kcFormGroupClass")}>
30
29
  <input
31
30
  tabIndex={4}
32
- className={clsx(
33
- getClassName("kcButtonClass"),
34
- getClassName("kcButtonPrimaryClass"),
35
- getClassName("kcButtonBlockClass"),
36
- getClassName("kcButtonLargeClass")
37
- )}
31
+ className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
38
32
  name="confirmLogout"
39
33
  id="kc-logout"
40
34
  type="submit"
@@ -1,12 +1,10 @@
1
- import type { I18n } from "keycloakify/login/i18n";
2
1
  import { type TemplateProps, type ClassKey } from "keycloakify/login/TemplateProps";
3
2
  import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
4
3
  import type { KcContext } from "keycloakify/account/KcContext";
5
4
 
6
- export type PageProps<NarowedKcContext = KcContext, I18nExtended extends I18n = I18n> = {
7
- Template: LazyOrNot<(props: TemplateProps<any, any>) => JSX.Element | null>;
5
+ export type PageProps<NarowedKcContext = KcContext> = {
6
+ Template: LazyOrNot<(props: TemplateProps<any>) => JSX.Element | null>;
8
7
  kcContext: NarowedKcContext;
9
- i18n: I18nExtended;
10
8
  doUseDefaultCss: boolean;
11
9
  classes?: Partial<Record<ClassKey, string>>;
12
10
  };
@@ -1,77 +1,56 @@
1
1
  import { useState } from "react";
2
- import { clsx } from "keycloakify/tools/clsx";
3
2
  import { Markdown } from "keycloakify/tools/Markdown";
4
3
  import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
5
4
  import { useTermsMarkdown } from "keycloakify/login/lib/useDownloadTerms";
6
- import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
5
+ import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
7
6
  import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFields";
8
7
  import type { PageProps } from "keycloakify/login/pages/PageProps";
9
8
  import type { KcContext } from "../KcContext";
10
- import type { I18n } from "../i18n";
9
+ import { useI18n, type I18n } from "../i18n";
11
10
 
12
- type RegisterProps = PageProps<Extract<KcContext, { pageId: "register.ftl" }>, I18n> & {
11
+ type RegisterProps = PageProps<Extract<KcContext, { pageId: "register.ftl" }>> & {
13
12
  UserProfileFormFields: LazyOrNot<(props: UserProfileFormFieldsProps) => JSX.Element>;
14
13
  };
15
14
 
16
15
  export default function Register(props: RegisterProps) {
17
- const { kcContext, i18n, doUseDefaultCss, Template, classes, UserProfileFormFields } = props;
16
+ const { kcContext, doUseDefaultCss, Template, classes, UserProfileFormFields } = props;
18
17
 
19
- const { getClassName } = useGetClassName({
18
+ const { kcClsx } = getKcClsx({
20
19
  doUseDefaultCss,
21
20
  classes
22
21
  });
23
22
 
24
23
  const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey, termsAcceptanceRequired } = kcContext;
25
24
 
25
+ const i18n = useI18n({ kcContext });
26
26
  const { msg, msgStr } = i18n;
27
27
 
28
28
  const [isFormSubmittable, setIsFormSubmittable] = useState(false);
29
29
 
30
30
  return (
31
- <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} headerNode={msg("registerTitle")} displayRequiredFields>
32
- <form id="kc-register-form" className={getClassName("kcFormClass")} action={url.registrationAction} method="post">
33
- <UserProfileFormFields
34
- {...{
35
- kcContext,
36
- i18n,
37
- getClassName,
38
- messagesPerField
39
- }}
40
- onIsFormSubmittableValueChange={setIsFormSubmittable}
41
- />
42
- {termsAcceptanceRequired && (
43
- <TermsAcceptance
44
- {...{
45
- i18n,
46
- getClassName,
47
- messagesPerField
48
- }}
49
- />
50
- )}
31
+ <Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("registerTitle")} displayRequiredFields>
32
+ <form id="kc-register-form" className={kcClsx("kcFormClass")} action={url.registrationAction} method="post">
33
+ <UserProfileFormFields kcContext={kcContext} kcClsx={kcClsx} onIsFormSubmittableValueChange={setIsFormSubmittable} />
34
+ {termsAcceptanceRequired && <TermsAcceptance i18n={i18n} kcClsx={kcClsx} messagesPerField={messagesPerField} />}
51
35
  {recaptchaRequired && (
52
36
  <div className="form-group">
53
- <div className={getClassName("kcInputWrapperClass")}>
37
+ <div className={kcClsx("kcInputWrapperClass")}>
54
38
  <div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey}></div>
55
39
  </div>
56
40
  </div>
57
41
  )}
58
- <div className={getClassName("kcFormGroupClass")}>
59
- <div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
60
- <div className={getClassName("kcFormOptionsWrapperClass")}>
42
+ <div className={kcClsx("kcFormGroupClass")}>
43
+ <div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
44
+ <div className={kcClsx("kcFormOptionsWrapperClass")}>
61
45
  <span>
62
46
  <a href={url.loginUrl}>{msg("backToLogin")}</a>
63
47
  </span>
64
48
  </div>
65
49
  </div>
66
- <div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
50
+ <div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
67
51
  <input
68
52
  disabled={!isFormSubmittable}
69
- className={clsx(
70
- getClassName("kcButtonClass"),
71
- getClassName("kcButtonPrimaryClass"),
72
- getClassName("kcButtonBlockClass"),
73
- getClassName("kcButtonLargeClass")
74
- )}
53
+ className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
75
54
  type="submit"
76
55
  value={msgStr("doRegister")}
77
56
  />
@@ -82,12 +61,8 @@ export default function Register(props: RegisterProps) {
82
61
  );
83
62
  }
84
63
 
85
- function TermsAcceptance(props: {
86
- i18n: I18n;
87
- getClassName: ReturnType<typeof useGetClassName>["getClassName"];
88
- messagesPerField: Pick<KcContext["messagesPerField"], "existsError" | "get">;
89
- }) {
90
- const { i18n, getClassName, messagesPerField } = props;
64
+ function TermsAcceptance(props: { i18n: I18n; kcClsx: KcClsx; messagesPerField: Pick<KcContext["messagesPerField"], "existsError" | "get"> }) {
65
+ const { i18n, kcClsx, messagesPerField } = props;
91
66
 
92
67
  const { msg } = i18n;
93
68
 
@@ -101,7 +76,7 @@ function TermsAcceptance(props: {
101
76
  return (
102
77
  <>
103
78
  <div className="form-group">
104
- <div className={getClassName("kcInputWrapperClass")}>
79
+ <div className={kcClsx("kcInputWrapperClass")}>
105
80
  {msg("termsTitle")}
106
81
  <div id="kc-registration-terms-text">
107
82
  <Markdown>{termsMarkdown}</Markdown>
@@ -109,21 +84,21 @@ function TermsAcceptance(props: {
109
84
  </div>
110
85
  </div>
111
86
  <div className="form-group">
112
- <div className={getClassName("kcLabelWrapperClass")}>
87
+ <div className={kcClsx("kcLabelWrapperClass")}>
113
88
  <input
114
89
  type="checkbox"
115
90
  id="termsAccepted"
116
91
  name="termsAccepted"
117
- className={getClassName("kcCheckboxInputClass")}
92
+ className={kcClsx("kcCheckboxInputClass")}
118
93
  aria-invalid={messagesPerField.existsError("termsAccepted")}
119
94
  />
120
- <label htmlFor="termsAccepted" className={getClassName("kcLabelClass")}>
95
+ <label htmlFor="termsAccepted" className={kcClsx("kcLabelClass")}>
121
96
  {msg("acceptTerms")}
122
97
  </label>
123
98
  </div>
124
99
  {messagesPerField.existsError("termsAccepted") && (
125
- <div className={getClassName("kcLabelWrapperClass")}>
126
- <span id="input-error-terms-accepted" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
100
+ <div className={kcClsx("kcLabelWrapperClass")}>
101
+ <span id="input-error-terms-accepted" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
127
102
  {messagesPerField.get("termsAccepted")}
128
103
  </span>
129
104
  </div>
@@ -1,12 +1,12 @@
1
1
  import { useEffect, useState } from "react";
2
2
  import type { PageProps } from "keycloakify/login/pages/PageProps";
3
3
  import type { KcContext } from "../KcContext";
4
- import type { I18n } from "../i18n";
4
+ import { useI18n } from "../i18n";
5
5
 
6
- export default function SamlPostForm(props: PageProps<Extract<KcContext, { pageId: "saml-post-form.ftl" }>, I18n>) {
7
- const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
6
+ export default function SamlPostForm(props: PageProps<Extract<KcContext, { pageId: "saml-post-form.ftl" }>>) {
7
+ const { kcContext, doUseDefaultCss, Template, classes } = props;
8
8
 
9
- const { msgStr, msg } = i18n;
9
+ const { msgStr, msg } = useI18n({ kcContext });
10
10
 
11
11
  const { samlPost } = kcContext;
12
12
 
@@ -26,7 +26,7 @@ export default function SamlPostForm(props: PageProps<Extract<KcContext, { pageI
26
26
  htmlFormElement.submit();
27
27
  }, [htmlFormElement]);
28
28
  return (
29
- <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} headerNode={msg("saml.post-form.title")}>
29
+ <Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("saml.post-form.title")}>
30
30
  <p>{msg("saml.post-form.message")}</p>
31
31
  <form name="saml-post-binding" method="post" action={samlPost.url} ref={setHtmlFormElement}>
32
32
  {samlPost.SAMLRequest && <input type="hidden" name="SAMLRequest" value={samlPost.SAMLRequest} />}
@@ -1,45 +1,43 @@
1
- import { clsx } from "keycloakify/tools/clsx";
2
- import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
1
+ import { getKcClsx } from "keycloakify/login/lib/kcClsx";
3
2
  import type { PageProps } from "keycloakify/login/pages/PageProps";
4
3
  import type { KcContext } from "../KcContext";
5
- import type { I18n } from "../i18n";
4
+ import { useI18n } from "../i18n";
6
5
 
7
- export default function SelectAuthenticator(props: PageProps<Extract<KcContext, { pageId: "select-authenticator.ftl" }>, I18n>) {
8
- const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
6
+ export default function SelectAuthenticator(props: PageProps<Extract<KcContext, { pageId: "select-authenticator.ftl" }>>) {
7
+ const { kcContext, doUseDefaultCss, Template, classes } = props;
9
8
  const { url, auth } = kcContext;
10
9
 
11
- const { getClassName } = useGetClassName({ doUseDefaultCss, classes });
12
- const { msg } = i18n;
10
+ const { kcClsx } = getKcClsx({ doUseDefaultCss, classes });
11
+ const { msg } = useI18n({ kcContext });
13
12
 
14
13
  return (
15
- <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} displayInfo={false} headerNode={msg("loginChooseAuthenticator")}>
16
- <form id="kc-select-credential-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
17
- <div className={getClassName("kcSelectAuthListClass")}>
14
+ <Template
15
+ kcContext={kcContext}
16
+ doUseDefaultCss={doUseDefaultCss}
17
+ classes={classes}
18
+ displayInfo={false}
19
+ headerNode={msg("loginChooseAuthenticator")}
20
+ >
21
+ <form id="kc-select-credential-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
22
+ <div className={kcClsx("kcSelectAuthListClass")}>
18
23
  {auth.authenticationSelections.map((authenticationSelection, i) => (
19
24
  <button
20
25
  key={i}
21
- className={getClassName("kcSelectAuthListItemClass")}
26
+ className={kcClsx("kcSelectAuthListItemClass")}
22
27
  type="submit"
23
28
  name="authenticationExecution"
24
29
  value={authenticationSelection.authExecId}
25
30
  >
26
- <div className={getClassName("kcSelectAuthListItemIconClass")}>
27
- <i
28
- className={clsx(
29
- // @ts-expect-error: iconCssClass is a string and not a class key
30
- // however getClassName gracefully handles this case at runtime
31
- getClassName(authenticationSelection.iconCssClass),
32
- getClassName("kcSelectAuthListItemIconPropertyClass")
33
- )}
34
- />
31
+ <div className={kcClsx("kcSelectAuthListItemIconClass")}>
32
+ <i className={kcClsx(authenticationSelection.iconCssClass, "kcSelectAuthListItemIconPropertyClass")} />
35
33
  </div>
36
- <div className={getClassName("kcSelectAuthListItemBodyClass")}>
37
- <div className={getClassName("kcSelectAuthListItemHeadingClass")}>{msg(authenticationSelection.displayName)}</div>
38
- <div className={getClassName("kcSelectAuthListItemDescriptionClass")}>{msg(authenticationSelection.helpText)}</div>
34
+ <div className={kcClsx("kcSelectAuthListItemBodyClass")}>
35
+ <div className={kcClsx("kcSelectAuthListItemHeadingClass")}>{msg(authenticationSelection.displayName)}</div>
36
+ <div className={kcClsx("kcSelectAuthListItemDescriptionClass")}>{msg(authenticationSelection.helpText)}</div>
39
37
  </div>
40
- <div className={getClassName("kcSelectAuthListItemFillClass")} />
41
- <div className={getClassName("kcSelectAuthListItemArrowClass")}>
42
- <i className={getClassName("kcSelectAuthListItemArrowIconClass")} />
38
+ <div className={kcClsx("kcSelectAuthListItemFillClass")} />
39
+ <div className={kcClsx("kcSelectAuthListItemArrowClass")}>
40
+ <i className={kcClsx("kcSelectAuthListItemArrowIconClass")} />
43
41
  </div>
44
42
  </button>
45
43
  ))}