From 4b89d15c1e5490525f9a2b9118d88eada912217e Mon Sep 17 00:00:00 2001 From: garronej Date: Fri, 17 Mar 2023 20:40:29 +0100 Subject: [PATCH] progressing --- src/KcProps.ts | 239 ----------------------------- src/Template.tsx | 168 ++++++-------------- src/TemplateProps.ts | 64 ++++++++ src/kcContext/KcContextBase.ts | 225 +++++++++++++++------------ src/lib/getClassName.ts | 14 ++ src/{ => lib}/keycloakJsAdapter.ts | 0 src/lib/useDownloadTerms.ts | 45 ++++++ src/lib/usePrepareTemplate.ts | 74 +++++++++ src/pages/PageProps.ts | 159 +++++++++++++++++++ src/pages/Terms.tsx | 39 ----- src/pages/index.ts | 178 +++++++++++++++++++++ src/tsconfig.json | 6 +- 12 files changed, 712 insertions(+), 499 deletions(-) delete mode 100644 src/KcProps.ts create mode 100644 src/TemplateProps.ts create mode 100644 src/lib/getClassName.ts rename src/{ => lib}/keycloakJsAdapter.ts (100%) create mode 100644 src/lib/useDownloadTerms.ts create mode 100644 src/lib/usePrepareTemplate.ts create mode 100644 src/pages/PageProps.ts create mode 100644 src/pages/index.ts diff --git a/src/KcProps.ts b/src/KcProps.ts deleted file mode 100644 index 21a3e40c..00000000 --- a/src/KcProps.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { allPropertiesValuesToUndefined } from "./tools/allPropertiesValuesToUndefined"; -import { assert } from "tsafe/assert"; -import type { KcContextBase } from "./kcContext"; -import type { ReactNode } from "react"; -import { I18nBase } from "./i18n"; - -/** Class names can be provided as an array or separated by whitespace */ -export type KcPropsGeneric = { - [key in CssClasses]: readonly string[] | string | undefined; -}; - -export type KcTemplateClassKey = - | "stylesCommon" - | "styles" - | "scripts" - | "kcHtmlClass" - | "kcLoginClass" - | "kcHeaderClass" - | "kcHeaderWrapperClass" - | "kcFormCardClass" - | "kcFormCardAccountClass" - | "kcFormHeaderClass" - | "kcLocaleWrapperClass" - | "kcContentWrapperClass" - | "kcLabelWrapperClass" - | "kcFormGroupClass" - | "kcResetFlowIcon" - | "kcFeedbackSuccessIcon" - | "kcFeedbackWarningIcon" - | "kcFeedbackErrorIcon" - | "kcFeedbackInfoIcon" - | "kcFormSocialAccountContentClass" - | "kcFormSocialAccountClass" - | "kcSignUpClass" - | "kcInfoAreaWrapperClass"; - -export type KcTemplateProps = KcPropsGeneric; - -export const defaultKcTemplateProps = { - "stylesCommon": [ - "node_modules/patternfly/dist/css/patternfly.min.css", - "node_modules/patternfly/dist/css/patternfly-additions.min.css", - "lib/zocial/zocial.css" - ], - "styles": ["css/login.css"], - "scripts": [], - "kcHtmlClass": ["login-pf"], - "kcLoginClass": ["login-pf-page"], - "kcContentWrapperClass": ["row"], - "kcHeaderClass": ["login-pf-page-header"], - "kcHeaderWrapperClass": [], - "kcFormCardClass": ["card-pf"], - "kcFormCardAccountClass": ["login-pf-accounts"], - "kcFormSocialAccountClass": ["login-pf-social-section"], - "kcFormSocialAccountContentClass": ["col-xs-12", "col-sm-6"], - "kcFormHeaderClass": ["login-pf-header"], - "kcLocaleWrapperClass": [], - "kcFeedbackErrorIcon": ["pficon", "pficon-error-circle-o"], - "kcFeedbackWarningIcon": ["pficon", "pficon-warning-triangle-o"], - "kcFeedbackSuccessIcon": ["pficon", "pficon-ok"], - "kcFeedbackInfoIcon": ["pficon", "pficon-info"], - "kcResetFlowIcon": ["pficon", "pficon-arrow fa-2x"], - "kcFormGroupClass": ["form-group"], - "kcLabelWrapperClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"], - "kcSignUpClass": ["login-pf-signup"], - "kcInfoAreaWrapperClass": [] -} as const; - -assert(); - -/** Tu use if you don't want any default */ -export const allClearKcTemplateProps = allPropertiesValuesToUndefined(defaultKcTemplateProps); - -assert(); - -export type KcProps = KcPropsGeneric< - | KcTemplateClassKey - | "kcLogoLink" - | "kcLogoClass" - | "kcContainerClass" - | "kcContentClass" - | "kcFeedbackAreaClass" - | "kcLocaleClass" - | "kcAlertIconClasserror" - | "kcFormAreaClass" - | "kcFormSocialAccountListClass" - | "kcFormSocialAccountDoubleListClass" - | "kcFormSocialAccountListLinkClass" - | "kcWebAuthnKeyIcon" - | "kcWebAuthnDefaultIcon" - | "kcFormClass" - | "kcFormGroupErrorClass" - | "kcLabelClass" - | "kcInputClass" - | "kcInputErrorMessageClass" - | "kcInputWrapperClass" - | "kcFormOptionsClass" - | "kcFormButtonsClass" - | "kcFormSettingClass" - | "kcTextareaClass" - | "kcInfoAreaClass" - | "kcFormGroupHeader" - | "kcButtonClass" - | "kcButtonPrimaryClass" - | "kcButtonDefaultClass" - | "kcButtonLargeClass" - | "kcButtonBlockClass" - | "kcInputLargeClass" - | "kcSrOnlyClass" - | "kcSelectAuthListClass" - | "kcSelectAuthListItemClass" - | "kcSelectAuthListItemFillClass" - | "kcSelectAuthListItemInfoClass" - | "kcSelectAuthListItemLeftClass" - | "kcSelectAuthListItemBodyClass" - | "kcSelectAuthListItemDescriptionClass" - | "kcSelectAuthListItemHeadingClass" - | "kcSelectAuthListItemHelpTextClass" - | "kcSelectAuthListItemIconPropertyClass" - | "kcSelectAuthListItemIconClass" - | "kcSelectAuthListItemTitle" - | "kcAuthenticatorDefaultClass" - | "kcAuthenticatorPasswordClass" - | "kcAuthenticatorOTPClass" - | "kcAuthenticatorWebAuthnClass" - | "kcAuthenticatorWebAuthnPasswordlessClass" - | "kcSelectOTPListClass" - | "kcSelectOTPListItemClass" - | "kcAuthenticatorOtpCircleClass" - | "kcSelectOTPItemHeadingClass" - | "kcFormOptionsWrapperClass" ->; - -export const defaultKcProps = { - ...defaultKcTemplateProps, - "kcLogoLink": "http://www.keycloak.org", - "kcLogoClass": "login-pf-brand", - "kcContainerClass": "container-fluid", - "kcContentClass": ["col-sm-8", "col-sm-offset-2", "col-md-6", "col-md-offset-3", "col-lg-6", "col-lg-offset-3"], - "kcFeedbackAreaClass": ["col-md-12"], - "kcLocaleClass": ["col-xs-12", "col-sm-1"], - "kcAlertIconClasserror": ["pficon", "pficon-error-circle-o"], - - "kcFormAreaClass": ["col-sm-10", "col-sm-offset-1", "col-md-8", "col-md-offset-2", "col-lg-8", "col-lg-offset-2"], - "kcFormSocialAccountListClass": ["login-pf-social", "list-unstyled", "login-pf-social-all"], - "kcFormSocialAccountDoubleListClass": ["login-pf-social-double-col"], - "kcFormSocialAccountListLinkClass": ["login-pf-social-link"], - "kcWebAuthnKeyIcon": ["pficon", "pficon-key"], - "kcWebAuthnDefaultIcon": ["pficon", "pficon-key"], - - "kcFormClass": ["form-horizontal"], - "kcFormGroupErrorClass": ["has-error"], - "kcLabelClass": ["control-label"], - "kcInputClass": ["form-control"], - "kcInputErrorMessageClass": ["pf-c-form__helper-text", "pf-m-error", "required", "kc-feedback-text"], - "kcInputWrapperClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"], - "kcFormOptionsClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"], - "kcFormButtonsClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"], - "kcFormSettingClass": ["login-pf-settings"], - "kcTextareaClass": ["form-control"], - - "kcInfoAreaClass": ["col-xs-12", "col-sm-4", "col-md-4", "col-lg-5", "details"], - - // user-profile grouping - "kcFormGroupHeader": ["pf-c-form__group"], - - // css classes for form buttons main class used for all buttons - "kcButtonClass": ["btn"], - // classes defining priority of the button - primary or default (there is typically only one priority button for the form) - "kcButtonPrimaryClass": ["btn-primary"], - "kcButtonDefaultClass": ["btn-default"], - // classes defining size of the button - "kcButtonLargeClass": ["btn-lg"], - "kcButtonBlockClass": ["btn-block"], - - // css classes for input - "kcInputLargeClass": ["input-lg"], - - // css classes for form accessability - "kcSrOnlyClass": ["sr-only"], - - // css classes for select-authenticator form - "kcSelectAuthListClass": ["list-group", "list-view-pf"], - "kcSelectAuthListItemClass": ["list-group-item", "list-view-pf-stacked"], - "kcSelectAuthListItemFillClass": ["pf-l-split__item", "pf-m-fill"], - "kcSelectAuthListItemIconPropertyClass": ["fa-2x", "select-auth-box-icon-properties"], - "kcSelectAuthListItemIconClass": ["pf-l-split__item", "select-auth-box-icon"], - "kcSelectAuthListItemTitle": ["select-auth-box-paragraph"], - "kcSelectAuthListItemInfoClass": ["list-view-pf-main-info"], - "kcSelectAuthListItemLeftClass": ["list-view-pf-left"], - "kcSelectAuthListItemBodyClass": ["list-view-pf-body"], - "kcSelectAuthListItemDescriptionClass": ["list-view-pf-description"], - "kcSelectAuthListItemHeadingClass": ["list-group-item-heading"], - "kcSelectAuthListItemHelpTextClass": ["list-group-item-text"], - - // css classes for the authenticators - "kcAuthenticatorDefaultClass": ["fa", "list-view-pf-icon-lg"], - "kcAuthenticatorPasswordClass": ["fa", "fa-unlock list-view-pf-icon-lg"], - "kcAuthenticatorOTPClass": ["fa", "fa-mobile", "list-view-pf-icon-lg"], - "kcAuthenticatorWebAuthnClass": ["fa", "fa-key", "list-view-pf-icon-lg"], - "kcAuthenticatorWebAuthnPasswordlessClass": ["fa", "fa-key", "list-view-pf-icon-lg"], - - //css classes for the OTP Login Form - "kcSelectOTPListClass": ["card-pf", "card-pf-view", "card-pf-view-select", "card-pf-view-single-select"], - "kcSelectOTPListItemClass": ["card-pf-body", "card-pf-top-element"], - "kcAuthenticatorOtpCircleClass": ["fa", "fa-mobile", "card-pf-icon-circle"], - "kcSelectOTPItemHeadingClass": ["card-pf-title", "text-center"], - "kcFormOptionsWrapperClass": [] -} as const; - -export type TemplateProps = { - kcContext: KcContext; - i18n: I18n; - doFetchDefaultThemeResources: boolean; -} & { - displayInfo?: boolean; - displayMessage?: boolean; - displayRequiredFields?: boolean; - displayWide?: boolean; - showAnotherWayIfPresent?: boolean; - headerNode: ReactNode; - showUsernameNode?: ReactNode; - formNode: ReactNode; - infoNode?: ReactNode; -} & KcTemplateProps; - -export type PageProps = { - kcContext: KcContext; - i18n: I18n; - doFetchDefaultThemeResources?: boolean; - Template: (props: TemplateProps) => JSX.Element | null; -} & KcProps; - -assert(); - -/** Tu use if you don't want any default */ -export const allClearKcProps = allPropertiesValuesToUndefined(defaultKcProps); - -assert(); diff --git a/src/Template.tsx b/src/Template.tsx index e8894044..481a4e91 100644 --- a/src/Template.tsx +++ b/src/Template.tsx @@ -1,13 +1,12 @@ -import React, { useReducer, useEffect } from "react"; -import { assert } from "./tools/assert"; -import { headInsert } from "./tools/headInsert"; -import { pathJoin } from "./bin/tools/pathJoin"; -import { clsx } from "./tools/clsx"; -import type { TemplateProps } from "./KcProps"; -import type { KcContextBase } from "./kcContext/KcContextBase"; -import type { I18nBase } from "./i18n"; +import { assert } from "keycloakify/tools/assert"; +import { clsx } from "keycloakify/tools/clsx"; +import { usePrepareTemplate } from "keycloakify/lib/usePrepareTemplate"; +import { type TemplateProps, defaultTemplateClasses } from "keycloakify/TemplateProps"; +import { useGetClassName } from "keycloakify/lib/getClassName"; +type KcContext = import("./kcContext/KcContextBase").KcContextBase.Common.Login; +import type { I18nBase as I18n } from "./i18n"; -export default function Template(props: TemplateProps) { +export default function Template(props: TemplateProps) { const { displayInfo = false, displayMessage = true, @@ -20,24 +19,29 @@ export default function Template(props: TemplateProps -
-
+
+
+
{msg("loginTitleHtml", realm.displayNameHtml)}
-
-
+
+
{realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && (
-
+
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} @@ -78,8 +82,8 @@ export default function Template(props: TemplateProps -
+
+
* {msg("requiredFields")} @@ -93,20 +97,20 @@ export default function Template(props: TemplateProps{headerNode} ) ) : displayRequiredFields ? ( -
-
+
+
* {msg("requiredFields")}
{showUsernameNode} -
+
- + {msg("restartLoginTooltip")}
@@ -117,12 +121,12 @@ export default function Template(props: TemplateProps {showUsernameNode} -
+
- + {msg("restartLoginTooltip")}
@@ -136,10 +140,10 @@ export default function Template(props: TemplateProps - {message.type === "success" && } - {message.type === "warning" && } - {message.type === "error" && } - {message.type === "info" && } + {message.type === "success" && } + {message.type === "warning" && } + {message.type === "error" && } + {message.type === "info" && } -
-
+
+
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} )} {displayInfo && ( -
-
+
+
{infoNode}
@@ -187,79 +195,3 @@ export default function Template(props: TemplateProps ); } - -export function usePrepareTemplate(params: { - doFetchDefaultThemeResources: boolean; - stylesCommon: string | readonly string[] | undefined; - styles: string | readonly string[] | undefined; - scripts: string | readonly string[] | undefined; - url: { - resourcesCommonPath: string; - resourcesPath: string; - }; - kcHtmlClass: string | readonly string[] | undefined; -}) { - const { doFetchDefaultThemeResources, stylesCommon, styles, url, scripts, kcHtmlClass } = params; - - const [isReady, setReady] = useReducer(() => true, !doFetchDefaultThemeResources); - - useEffect(() => { - if (!doFetchDefaultThemeResources) { - return; - } - - let isUnmounted = false; - - const toArr = (x: string | readonly string[] | undefined) => (typeof x === "string" ? x.split(" ") : x ?? []); - - Promise.all( - [ - ...toArr(stylesCommon).map(relativePath => pathJoin(url.resourcesCommonPath, relativePath)), - ...toArr(styles).map(relativePath => pathJoin(url.resourcesPath, relativePath)) - ] - .reverse() - .map(href => - headInsert({ - "type": "css", - href, - "position": "prepend" - }) - ) - ).then(() => { - if (isUnmounted) { - return; - } - - setReady(); - }); - - toArr(scripts).forEach(relativePath => - headInsert({ - "type": "javascript", - "src": pathJoin(url.resourcesPath, relativePath) - }) - ); - - return () => { - isUnmounted = true; - }; - }, [kcHtmlClass]); - - useEffect(() => { - if (kcHtmlClass === undefined) { - return; - } - - const htmlClassList = document.getElementsByTagName("html")[0].classList; - - const tokens = clsx(kcHtmlClass).split(" "); - - htmlClassList.add(...tokens); - - return () => { - htmlClassList.remove(...tokens); - }; - }, [kcHtmlClass]); - - return { isReady }; -} diff --git a/src/TemplateProps.ts b/src/TemplateProps.ts new file mode 100644 index 00000000..172a0668 --- /dev/null +++ b/src/TemplateProps.ts @@ -0,0 +1,64 @@ +import type { ReactNode } from "react"; +import type { KcContextBase } from "keycloakify/kcContext"; +import type { I18nBase } from "keycloakify/i18n"; + +export type TemplateProps = { + kcContext: KcContext; + i18n: I18n; + displayInfo?: boolean; + displayMessage?: boolean; + displayRequiredFields?: boolean; + displayWide?: boolean; + showAnotherWayIfPresent?: boolean; + headerNode: ReactNode; + showUsernameNode?: ReactNode; + formNode: ReactNode; + infoNode?: ReactNode; + doUseDefaultCss: boolean; + classes?: Partial>; +}; + +export type TemplateClassKey = + | "kcHtmlClass" + | "kcLoginClass" + | "kcHeaderClass" + | "kcHeaderWrapperClass" + | "kcFormCardClass" + | "kcFormCardAccountClass" + | "kcFormHeaderClass" + | "kcLocaleWrapperClass" + | "kcContentWrapperClass" + | "kcLabelWrapperClass" + | "kcFormGroupClass" + | "kcResetFlowIcon" + | "kcFeedbackSuccessIcon" + | "kcFeedbackWarningIcon" + | "kcFeedbackErrorIcon" + | "kcFeedbackInfoIcon" + | "kcFormSocialAccountContentClass" + | "kcFormSocialAccountClass" + | "kcSignUpClass" + | "kcInfoAreaWrapperClass"; + +export const defaultTemplateClasses: Record = { + "kcHtmlClass": "login-pf", + "kcLoginClass": "login-pf-page", + "kcContentWrapperClass": "row", + "kcHeaderClass": "login-pf-page-header", + "kcHeaderWrapperClass": undefined, + "kcFormCardClass": "card-pf", + "kcFormCardAccountClass": "login-pf-accounts", + "kcFormSocialAccountClass": "login-pf-social-section", + "kcFormSocialAccountContentClass": "col-xs-12 col-sm-6", + "kcFormHeaderClass": "login-pf-header", + "kcLocaleWrapperClass": undefined, + "kcFeedbackErrorIcon": "pficon pficon-error-circle-o", + "kcFeedbackWarningIcon": "pficon pficon-warning-triangle-o", + "kcFeedbackSuccessIcon": "pficon pficon-ok", + "kcFeedbackInfoIcon": "pficon pficon-info", + "kcResetFlowIcon": "pficon pficon-arrow fa-2x", + "kcFormGroupClass": "form-group", + "kcLabelWrapperClass": "col-xs-12 col-sm-12 col-md-12 col-lg-12", + "kcSignUpClass": "login-pf-signup", + "kcInfoAreaWrapperClass": undefined +}; diff --git a/src/kcContext/KcContextBase.ts b/src/kcContext/KcContextBase.ts index 2f0e2733..f42f6b51 100644 --- a/src/kcContext/KcContextBase.ts +++ b/src/kcContext/KcContextBase.ts @@ -1,8 +1,8 @@ -import type { LoginThemePageId } from "../bin/keycloakify/generateFtl"; +import type { LoginThemePageId, AccountThemePageId } from "../bin/keycloakify/generateFtl"; import { assert } from "tsafe/assert"; import type { Equals } from "tsafe"; import type { MessageKeyBase } from "../i18n"; -import type { KcTemplateClassKey } from "../KcProps"; +import type { KcTemplateClassKey } from "../templates/LoginThemeTemplate"; type ExtractAfterStartingWith = StrEnum extends `${Prefix}${infer U}` ? U : never; @@ -31,69 +31,69 @@ export type KcContextBase = | KcContextBase.LoginConfigTotp | KcContextBase.LogoutConfirm | KcContextBase.UpdateUserProfile - | KcContextBase.IdpReviewUserProfile; - -export type WebauthnAuthenticator = { - credentialId: string; - transports: { - iconClass: KcTemplateClassKey; - displayNameProperties: MessageKeyBase[]; - }; - label: string; - createdAt: string; -}; + | KcContextBase.IdpReviewUserProfile + | KcContextBase.Password; export declare namespace KcContextBase { - export type Common = { - url: { - loginAction: string; - resourcesPath: string; - resourcesCommonPath: string; - loginRestartFlowUrl: string; - loginUrl: string; + export namespace Common { + export type Login = { + url: { + loginAction: string; + resourcesPath: string; + resourcesCommonPath: string; + loginRestartFlowUrl: string; + loginUrl: string; + }; + realm: { + name: string; + displayName?: string; + displayNameHtml?: string; + internationalizationEnabled: boolean; + registrationEmailAsUsername: boolean; + }; + /** Undefined if !realm.internationalizationEnabled */ + locale?: { + supported: { + url: string; + label: string; + languageTag: string; + }[]; + currentLanguageTag: string; + }; + auth?: { + showUsername?: boolean; + showResetCredentials?: boolean; + showTryAnotherWayLink?: boolean; + attemptedUsername?: string; + }; + scripts: string[]; + message?: { + type: "success" | "warning" | "error" | "info"; + summary: string; + }; + client: { + clientId: string; + name?: string; + description?: string; + }; + isAppInitiatedAction: boolean; + messagesPerField: { + printIfExists: (fieldName: string, x: T) => T | undefined; + existsError: (fieldName: string) => boolean; + get: (fieldName: string) => string; + exists: (fieldName: string) => boolean; + }; }; - realm: { - name: string; - displayName?: string; - displayNameHtml?: string; - internationalizationEnabled: boolean; - registrationEmailAsUsername: boolean; - }; - /** Undefined if !realm.internationalizationEnabled */ - locale?: { - supported: { - url: string; - label: string; - languageTag: string; - }[]; - currentLanguageTag: string; - }; - auth?: { - showUsername?: boolean; - showResetCredentials?: boolean; - showTryAnotherWayLink?: boolean; - attemptedUsername?: string; - }; - scripts: string[]; - message?: { - type: "success" | "warning" | "error" | "info"; - summary: string; - }; - client: { - clientId: string; - name?: string; - description?: string; - }; - isAppInitiatedAction: boolean; - messagesPerField: { - printIfExists: (fieldName: string, x: T) => T | undefined; - existsError: (fieldName: string) => boolean; - get: (fieldName: string) => string; - exists: (fieldName: string) => boolean; - }; - }; - export type Login = Common & { + export type Account = { + url: { + resourcesPath: string; + resourcesCommonPath: string; + }; + }; + } + + export type Login = Common.Login & { pageId: "login.ftl"; url: { loginResetCredentialsUrl: string; @@ -126,25 +126,7 @@ export declare namespace KcContextBase { }; }; - export type RegisterCommon = Common & { - url: { - registrationAction: string; - }; - passwordRequired: boolean; - recaptchaRequired: boolean; - recaptchaSiteKey?: string; - social: { - displayInfo: boolean; - providers?: { - loginUrl: string; - alias: string; - providerId: string; - displayName: string; - }[]; - }; - }; - - export type Register = RegisterCommon & { + export type Register = RegisterUserProfile.CommonWithLegacy & { pageId: "register.ftl"; register: { formData: { @@ -157,7 +139,7 @@ export declare namespace KcContextBase { }; }; - export type RegisterUserProfile = RegisterCommon & { + export type RegisterUserProfile = RegisterUserProfile.CommonWithLegacy & { pageId: "register-user-profile.ftl"; profile: { context: "REGISTRATION_PROFILE"; @@ -166,7 +148,27 @@ export declare namespace KcContextBase { }; }; - export type Info = Common & { + export namespace RegisterUserProfile { + export type CommonWithLegacy = Common.Login & { + url: { + registrationAction: string; + }; + passwordRequired: boolean; + recaptchaRequired: boolean; + recaptchaSiteKey?: string; + social: { + displayInfo: boolean; + providers?: { + loginUrl: string; + alias: string; + providerId: string; + displayName: string; + }[]; + }; + }; + } + + export type Info = Common.Login & { pageId: "info.ftl"; messageHeader?: string; requiredActions?: ExtractAfterStartingWith<"requiredAction.", MessageKeyBase>[]; @@ -178,22 +180,22 @@ export declare namespace KcContextBase { }; }; - export type Error = Common & { + export type Error = Common.Login & { pageId: "error.ftl"; client?: { baseUrl?: string; }; - message: NonNullable; + message: NonNullable; }; - export type LoginResetPassword = Common & { + export type LoginResetPassword = Common.Login & { pageId: "login-reset-password.ftl"; realm: { loginWithEmailAllowed: boolean; }; }; - export type LoginVerifyEmail = Common & { + export type LoginVerifyEmail = Common.Login & { pageId: "login-verify-email.ftl"; //NOTE: Optional because maybe it wasn't defined in older keycloak versions. user?: { @@ -201,18 +203,18 @@ export declare namespace KcContextBase { }; }; - export type Terms = Common & { + export type Terms = Common.Login & { pageId: "terms.ftl"; }; - export type LoginOtp = Common & { + export type LoginOtp = Common.Login & { pageId: "login-otp.ftl"; otpLogin: { userOtpCredentials: { id: string; userLabel: string }[]; }; }; - export type LoginUsername = Common & { + export type LoginUsername = Common.Login & { pageId: "login-username.ftl"; url: { loginResetCredentialsUrl: string; @@ -242,7 +244,7 @@ export declare namespace KcContextBase { }; }; - export type LoginPassword = Common & { + export type LoginPassword = Common.Login & { pageId: "login-password.ftl"; url: { loginResetCredentialsUrl: string; @@ -265,10 +267,10 @@ export declare namespace KcContextBase { }; }; - export type WebauthnAuthenticate = Common & { + export type WebauthnAuthenticate = Common.Login & { pageId: "webauthn-authenticate.ftl"; authenticators: { - authenticators: WebauthnAuthenticator[]; + authenticators: WebauthnAuthenticate.WebauthnAuthenticator[]; }; challenge: string; // I hate this: @@ -283,12 +285,24 @@ export declare namespace KcContextBase { login: {}; }; - export type LoginUpdatePassword = Common & { + export namespace WebauthnAuthenticate { + export type WebauthnAuthenticator = { + credentialId: string; + transports: { + iconClass: KcTemplateClassKey; + displayNameProperties: MessageKeyBase[]; + }; + label: string; + createdAt: string; + }; + } + + export type LoginUpdatePassword = Common.Login & { pageId: "login-update-password.ftl"; username: string; }; - export type LoginUpdateProfile = Common & { + export type LoginUpdateProfile = Common.Login & { pageId: "login-update-profile.ftl"; user: { editUsernameAllowed: boolean; @@ -299,12 +313,12 @@ export declare namespace KcContextBase { }; }; - export type LoginIdpLinkConfirm = Common & { + export type LoginIdpLinkConfirm = Common.Login & { pageId: "login-idp-link-confirm.ftl"; idpAlias: string; }; - export type LoginIdpLinkEmail = Common & { + export type LoginIdpLinkEmail = Common.Login & { pageId: "login-idp-link-email.ftl"; brokerContext: { username: string; @@ -312,11 +326,11 @@ export declare namespace KcContextBase { idpAlias: string; }; - export type LoginPageExpired = Common & { + export type LoginPageExpired = Common.Login & { pageId: "login-page-expired.ftl"; }; - export type LoginConfigTotp = Common & { + export type LoginConfigTotp = Common.Login & { pageId: "login-config-totp.ftl"; mode?: "qr" | "manual" | undefined | null; totp: { @@ -344,7 +358,7 @@ export declare namespace KcContextBase { }; }; - export type LogoutConfirm = Common & { + export type LogoutConfirm = Common.Login & { pageId: "logout-confirm.ftl"; url: { logoutConfirmAction: string; @@ -358,7 +372,7 @@ export declare namespace KcContextBase { }; }; - export type UpdateUserProfile = Common & { + export type UpdateUserProfile = Common.Login & { pageId: "update-user-profile.ftl"; profile: { attributes: Attribute[]; @@ -366,7 +380,7 @@ export declare namespace KcContextBase { }; }; - export type IdpReviewUserProfile = Common & { + export type IdpReviewUserProfile = Common.Login & { pageId: "idp-review-user-profile.ftl"; profile: { context: "IDP_REVIEW"; @@ -374,6 +388,13 @@ export declare namespace KcContextBase { attributesByName: Record; }; }; + + export type Password = Common.Account & { + pageId: "password.ftl"; + url: { + passwordUrl: string; + }; + }; } export type Attribute = { @@ -493,4 +514,4 @@ export declare namespace Validators { }; } -assert>(); +assert>(); diff --git a/src/lib/getClassName.ts b/src/lib/getClassName.ts new file mode 100644 index 00000000..39cafd6c --- /dev/null +++ b/src/lib/getClassName.ts @@ -0,0 +1,14 @@ +import { clsx } from "keycloakify/tools/clsx"; + +export function useGetClassName(params: { + defaultClasses?: Record; + classes?: Partial>; +}) { + const { defaultClasses, classes } = params; + + const getClassName = (classKey: ClassKey): string => { + return clsx(classKey, defaultClasses?.[classKey], classes?.[classKey]); + }; + + return { getClassName }; +} diff --git a/src/keycloakJsAdapter.ts b/src/lib/keycloakJsAdapter.ts similarity index 100% rename from src/keycloakJsAdapter.ts rename to src/lib/keycloakJsAdapter.ts diff --git a/src/lib/useDownloadTerms.ts b/src/lib/useDownloadTerms.ts new file mode 100644 index 00000000..a0a5da06 --- /dev/null +++ b/src/lib/useDownloadTerms.ts @@ -0,0 +1,45 @@ +import { useEffect } from "react"; +import { memoize } from "../tools/memoize"; +import { fallbackLanguageTag } from "../i18n"; +import { useConst } from "../tools/useConst"; +import { useConstCallback } from "../tools/useConstCallback"; +import { assert } from "tsafe/assert"; + +export type KcContextLike = { + pageId: KcContextBase["pageId"]; + locale?: { + currentLanguageTag: string; + }; +}; + +assert(); + +/** Allow to avoid bundling the terms and download it on demand*/ +export function useDownloadTerms(params: { + kcContext: KcContextLike; + downloadTermMarkdown: (params: { currentLanguageTag: string }) => Promise; +}) { + const { kcContext } = params; + + const { downloadTermMarkdownMemoized } = (function useClosure() { + const { downloadTermMarkdown } = params; + + const downloadTermMarkdownConst = useConstCallback(downloadTermMarkdown); + + const downloadTermMarkdownMemoized = useConst(() => + memoize((currentLanguageTag: string) => downloadTermMarkdownConst({ currentLanguageTag })) + ); + + return { downloadTermMarkdownMemoized }; + })(); + + useEffect(() => { + if (kcContext.pageId !== "terms.ftl") { + return; + } + + downloadTermMarkdownMemoized(kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag).then( + thermMarkdown => (evtTermMarkdown.state = thermMarkdown) + ); + }, []); +} diff --git a/src/lib/usePrepareTemplate.ts b/src/lib/usePrepareTemplate.ts new file mode 100644 index 00000000..f02d8108 --- /dev/null +++ b/src/lib/usePrepareTemplate.ts @@ -0,0 +1,74 @@ +import { useReducer, useEffect } from "react"; +import { headInsert } from "../tools/headInsert"; +import { pathJoin } from "../bin/tools/pathJoin"; +import { clsx } from "../tools/clsx"; + +export function usePrepareTemplate(params: { + doFetchDefaultThemeResources: boolean; + stylesCommon?: string[]; + styles?: string[]; + scripts?: string[]; + url: { + resourcesCommonPath: string; + resourcesPath: string; + }; + htmlClassName: string; +}) { + const { doFetchDefaultThemeResources, stylesCommon, styles, url, scripts, htmlClassName } = params; + + const [isReady, setReady] = useReducer(() => true, !doFetchDefaultThemeResources); + + useEffect(() => { + if (!doFetchDefaultThemeResources) { + return; + } + + let isUnmounted = false; + + Promise.all( + [ + ...(stylesCommon ?? []).map(relativePath => pathJoin(url.resourcesCommonPath, relativePath)), + ...(styles ?? []).map(relativePath => pathJoin(url.resourcesPath, relativePath)) + ] + .reverse() + .map(href => + headInsert({ + "type": "css", + href, + "position": "prepend" + }) + ) + ).then(() => { + if (isUnmounted) { + return; + } + + setReady(); + }); + + (scripts ?? []).forEach(relativePath => + headInsert({ + "type": "javascript", + "src": pathJoin(url.resourcesPath, relativePath) + }) + ); + + return () => { + isUnmounted = true; + }; + }, []); + + useEffect(() => { + const htmlClassList = document.getElementsByTagName("html")[0].classList; + + const tokens = clsx(htmlClassName).split(" "); + + htmlClassList.add(...tokens); + + return () => { + htmlClassList.remove(...tokens); + }; + }, [htmlClassName]); + + return { isReady }; +} diff --git a/src/pages/PageProps.ts b/src/pages/PageProps.ts new file mode 100644 index 00000000..220a9f85 --- /dev/null +++ b/src/pages/PageProps.ts @@ -0,0 +1,159 @@ +import type { LazyExoticComponent } from "react"; +import { allPropertiesValuesToUndefined } from "../tools/allPropertiesValuesToUndefined"; +import { assert } from "tsafe/assert"; +import type { I18nBase } from "../i18n"; +import type { ThemeType } from "../bin/keycloakify/generateFtl"; +import type { Equals } from "tsafe"; +import type { TemplateProps } from "../templates/TemplateProps"; + +export const defaultKcProps = { + "login": { + ...defaultKcTemplateProps.login, + "kcLogoLink": "http://www.keycloak.org", + "kcLogoClass": "login-pf-brand", + "kcContainerClass": "container-fluid", + "kcContentClass": ["col-sm-8", "col-sm-offset-2", "col-md-6", "col-md-offset-3", "col-lg-6", "col-lg-offset-3"], + "kcFeedbackAreaClass": ["col-md-12"], + "kcLocaleClass": ["col-xs-12", "col-sm-1"], + "kcAlertIconClasserror": ["pficon", "pficon-error-circle-o"], + + "kcFormAreaClass": ["col-sm-10", "col-sm-offset-1", "col-md-8", "col-md-offset-2", "col-lg-8", "col-lg-offset-2"], + "kcFormSocialAccountListClass": ["login-pf-social", "list-unstyled", "login-pf-social-all"], + "kcFormSocialAccountDoubleListClass": ["login-pf-social-double-col"], + "kcFormSocialAccountListLinkClass": ["login-pf-social-link"], + "kcWebAuthnKeyIcon": ["pficon", "pficon-key"], + "kcWebAuthnDefaultIcon": ["pficon", "pficon-key"], + + "kcFormClass": ["form-horizontal"], + "kcFormGroupErrorClass": ["has-error"], + "kcLabelClass": ["control-label"], + "kcInputClass": ["form-control"], + "kcInputErrorMessageClass": ["pf-c-form__helper-text", "pf-m-error", "required", "kc-feedback-text"], + "kcInputWrapperClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"], + "kcFormOptionsClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"], + "kcFormButtonsClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"], + "kcFormSettingClass": ["login-pf-settings"], + "kcTextareaClass": ["form-control"], + + "kcInfoAreaClass": ["col-xs-12", "col-sm-4", "col-md-4", "col-lg-5", "details"], + + // user-profile grouping + "kcFormGroupHeader": ["pf-c-form__group"], + + // css classes for form buttons main class used for all buttons + "kcButtonClass": ["btn"], + // classes defining priority of the button - primary or default (there is typically only one priority button for the form) + "kcButtonPrimaryClass": ["btn-primary"], + "kcButtonDefaultClass": ["btn-default"], + // classes defining size of the button + "kcButtonLargeClass": ["btn-lg"], + "kcButtonBlockClass": ["btn-block"], + + // css classes for input + "kcInputLargeClass": ["input-lg"], + + // css classes for form accessability + "kcSrOnlyClass": ["sr-only"], + + // css classes for select-authenticator form + "kcSelectAuthListClass": ["list-group", "list-view-pf"], + "kcSelectAuthListItemClass": ["list-group-item", "list-view-pf-stacked"], + "kcSelectAuthListItemFillClass": ["pf-l-split__item", "pf-m-fill"], + "kcSelectAuthListItemIconPropertyClass": ["fa-2x", "select-auth-box-icon-properties"], + "kcSelectAuthListItemIconClass": ["pf-l-split__item", "select-auth-box-icon"], + "kcSelectAuthListItemTitle": ["select-auth-box-paragraph"], + "kcSelectAuthListItemInfoClass": ["list-view-pf-main-info"], + "kcSelectAuthListItemLeftClass": ["list-view-pf-left"], + "kcSelectAuthListItemBodyClass": ["list-view-pf-body"], + "kcSelectAuthListItemDescriptionClass": ["list-view-pf-description"], + "kcSelectAuthListItemHeadingClass": ["list-group-item-heading"], + "kcSelectAuthListItemHelpTextClass": ["list-group-item-text"], + + // css classes for the authenticators + "kcAuthenticatorDefaultClass": ["fa", "list-view-pf-icon-lg"], + "kcAuthenticatorPasswordClass": ["fa", "fa-unlock list-view-pf-icon-lg"], + "kcAuthenticatorOTPClass": ["fa", "fa-mobile", "list-view-pf-icon-lg"], + "kcAuthenticatorWebAuthnClass": ["fa", "fa-key", "list-view-pf-icon-lg"], + "kcAuthenticatorWebAuthnPasswordlessClass": ["fa", "fa-key", "list-view-pf-icon-lg"], + + //css classes for the OTP Login Form + "kcSelectOTPListClass": ["card-pf", "card-pf-view", "card-pf-view-select", "card-pf-view-single-select"], + "kcSelectOTPListItemClass": ["card-pf-body", "card-pf-top-element"], + "kcAuthenticatorOtpCircleClass": ["fa", "fa-mobile", "card-pf-icon-circle"], + "kcSelectOTPItemHeadingClass": ["card-pf-title", "text-center"], + "kcFormOptionsWrapperClass": [] + }, + "account": { + ...defaultKcTemplateProps.account + } +}; + +assert>(); +assert(); +assert(); + +export type PageProps = { + Template: LazyExoticComponent<(props: TemplateProps) => JSX.Element | null>; + kcContext: KcContext; + i18n: I18n; + templateProps: Pick, "doUseDefaultCss" | "classes">; + classes?: Partial< + Record< + | "kcLogoLink" + | "kcLogoClass" + | "kcContainerClass" + | "kcContentClass" + | "kcFeedbackAreaClass" + | "kcLocaleClass" + | "kcAlertIconClasserror" + | "kcFormAreaClass" + | "kcFormSocialAccountListClass" + | "kcFormSocialAccountDoubleListClass" + | "kcFormSocialAccountListLinkClass" + | "kcWebAuthnKeyIcon" + | "kcWebAuthnDefaultIcon" + | "kcFormClass" + | "kcFormGroupErrorClass" + | "kcLabelClass" + | "kcInputClass" + | "kcInputErrorMessageClass" + | "kcInputWrapperClass" + | "kcFormOptionsClass" + | "kcFormButtonsClass" + | "kcFormSettingClass" + | "kcTextareaClass" + | "kcInfoAreaClass" + | "kcFormGroupHeader" + | "kcButtonClass" + | "kcButtonPrimaryClass" + | "kcButtonDefaultClass" + | "kcButtonLargeClass" + | "kcButtonBlockClass" + | "kcInputLargeClass" + | "kcSrOnlyClass" + | "kcSelectAuthListClass" + | "kcSelectAuthListItemClass" + | "kcSelectAuthListItemFillClass" + | "kcSelectAuthListItemInfoClass" + | "kcSelectAuthListItemLeftClass" + | "kcSelectAuthListItemBodyClass" + | "kcSelectAuthListItemDescriptionClass" + | "kcSelectAuthListItemHeadingClass" + | "kcSelectAuthListItemHelpTextClass" + | "kcSelectAuthListItemIconPropertyClass" + | "kcSelectAuthListItemIconClass" + | "kcSelectAuthListItemTitle" + | "kcAuthenticatorDefaultClass" + | "kcAuthenticatorPasswordClass" + | "kcAuthenticatorOTPClass" + | "kcAuthenticatorWebAuthnClass" + | "kcAuthenticatorWebAuthnPasswordlessClass" + | "kcSelectOTPListClass" + | "kcSelectOTPListItemClass" + | "kcAuthenticatorOtpCircleClass" + | "kcSelectOTPItemHeadingClass" + | "kcFormOptionsWrapperClass", + string + > + >; +}; diff --git a/src/pages/Terms.tsx b/src/pages/Terms.tsx index cae27529..62cf8755 100644 --- a/src/pages/Terms.tsx +++ b/src/pages/Terms.tsx @@ -64,42 +64,3 @@ export default function Terms(props: PageProps(undefined); - -export type KcContextLike = { - pageId: KcContextBase["pageId"]; - locale?: { - currentLanguageTag: string; - }; -}; - -assert>(); - -/** Allow to avoid bundling the terms and download it on demand*/ -export function useDownloadTerms(params: { - kcContext: KcContextLike; - downloadTermMarkdown: (params: { currentLanguageTag: string }) => Promise; -}) { - const { kcContext } = params; - - const { downloadTermMarkdownMemoized } = (function useClosure() { - const { downloadTermMarkdown } = params; - - const downloadTermMarkdownConst = useConstCallback(downloadTermMarkdown); - - const downloadTermMarkdownMemoized = useConst(() => - memoize((currentLanguageTag: string) => downloadTermMarkdownConst({ currentLanguageTag })) - ); - - return { downloadTermMarkdownMemoized }; - })(); - - useEffect(() => { - if (kcContext.pageId !== "terms.ftl") { - return; - } - - downloadTermMarkdownMemoized(kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag).then( - thermMarkdown => (evtTermMarkdown.state = thermMarkdown) - ); - }, []); -} diff --git a/src/pages/index.ts b/src/pages/index.ts new file mode 100644 index 00000000..903a9471 --- /dev/null +++ b/src/pages/index.ts @@ -0,0 +1,178 @@ +import { allPropertiesValuesToUndefined } from "../tools/allPropertiesValuesToUndefined"; +import { assert } from "tsafe/assert"; +import type { I18nBase } from "../i18n"; +import type { ThemeType } from "../bin/keycloakify/generateFtl"; +import type { Equals } from "tsafe"; +import type { LoginThemeTemplateProps } from "../templates/LoginThemeTemplate"; + +export namespace KcProps { + export type Login = KcPropsGeneric< + | KcTemplateClassKey.Login + | "kcLogoLink" + | "kcLogoClass" + | "kcContainerClass" + | "kcContentClass" + | "kcFeedbackAreaClass" + | "kcLocaleClass" + | "kcAlertIconClasserror" + | "kcFormAreaClass" + | "kcFormSocialAccountListClass" + | "kcFormSocialAccountDoubleListClass" + | "kcFormSocialAccountListLinkClass" + | "kcWebAuthnKeyIcon" + | "kcWebAuthnDefaultIcon" + | "kcFormClass" + | "kcFormGroupErrorClass" + | "kcLabelClass" + | "kcInputClass" + | "kcInputErrorMessageClass" + | "kcInputWrapperClass" + | "kcFormOptionsClass" + | "kcFormButtonsClass" + | "kcFormSettingClass" + | "kcTextareaClass" + | "kcInfoAreaClass" + | "kcFormGroupHeader" + | "kcButtonClass" + | "kcButtonPrimaryClass" + | "kcButtonDefaultClass" + | "kcButtonLargeClass" + | "kcButtonBlockClass" + | "kcInputLargeClass" + | "kcSrOnlyClass" + | "kcSelectAuthListClass" + | "kcSelectAuthListItemClass" + | "kcSelectAuthListItemFillClass" + | "kcSelectAuthListItemInfoClass" + | "kcSelectAuthListItemLeftClass" + | "kcSelectAuthListItemBodyClass" + | "kcSelectAuthListItemDescriptionClass" + | "kcSelectAuthListItemHeadingClass" + | "kcSelectAuthListItemHelpTextClass" + | "kcSelectAuthListItemIconPropertyClass" + | "kcSelectAuthListItemIconClass" + | "kcSelectAuthListItemTitle" + | "kcAuthenticatorDefaultClass" + | "kcAuthenticatorPasswordClass" + | "kcAuthenticatorOTPClass" + | "kcAuthenticatorWebAuthnClass" + | "kcAuthenticatorWebAuthnPasswordlessClass" + | "kcSelectOTPListClass" + | "kcSelectOTPListItemClass" + | "kcAuthenticatorOtpCircleClass" + | "kcSelectOTPItemHeadingClass" + | "kcFormOptionsWrapperClass" + >; + + export type Account = KcPropsGeneric; +} + +export const defaultKcProps = { + "login": { + ...defaultKcTemplateProps.login, + "kcLogoLink": "http://www.keycloak.org", + "kcLogoClass": "login-pf-brand", + "kcContainerClass": "container-fluid", + "kcContentClass": ["col-sm-8", "col-sm-offset-2", "col-md-6", "col-md-offset-3", "col-lg-6", "col-lg-offset-3"], + "kcFeedbackAreaClass": ["col-md-12"], + "kcLocaleClass": ["col-xs-12", "col-sm-1"], + "kcAlertIconClasserror": ["pficon", "pficon-error-circle-o"], + + "kcFormAreaClass": ["col-sm-10", "col-sm-offset-1", "col-md-8", "col-md-offset-2", "col-lg-8", "col-lg-offset-2"], + "kcFormSocialAccountListClass": ["login-pf-social", "list-unstyled", "login-pf-social-all"], + "kcFormSocialAccountDoubleListClass": ["login-pf-social-double-col"], + "kcFormSocialAccountListLinkClass": ["login-pf-social-link"], + "kcWebAuthnKeyIcon": ["pficon", "pficon-key"], + "kcWebAuthnDefaultIcon": ["pficon", "pficon-key"], + + "kcFormClass": ["form-horizontal"], + "kcFormGroupErrorClass": ["has-error"], + "kcLabelClass": ["control-label"], + "kcInputClass": ["form-control"], + "kcInputErrorMessageClass": ["pf-c-form__helper-text", "pf-m-error", "required", "kc-feedback-text"], + "kcInputWrapperClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"], + "kcFormOptionsClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"], + "kcFormButtonsClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"], + "kcFormSettingClass": ["login-pf-settings"], + "kcTextareaClass": ["form-control"], + + "kcInfoAreaClass": ["col-xs-12", "col-sm-4", "col-md-4", "col-lg-5", "details"], + + // user-profile grouping + "kcFormGroupHeader": ["pf-c-form__group"], + + // css classes for form buttons main class used for all buttons + "kcButtonClass": ["btn"], + // classes defining priority of the button - primary or default (there is typically only one priority button for the form) + "kcButtonPrimaryClass": ["btn-primary"], + "kcButtonDefaultClass": ["btn-default"], + // classes defining size of the button + "kcButtonLargeClass": ["btn-lg"], + "kcButtonBlockClass": ["btn-block"], + + // css classes for input + "kcInputLargeClass": ["input-lg"], + + // css classes for form accessability + "kcSrOnlyClass": ["sr-only"], + + // css classes for select-authenticator form + "kcSelectAuthListClass": ["list-group", "list-view-pf"], + "kcSelectAuthListItemClass": ["list-group-item", "list-view-pf-stacked"], + "kcSelectAuthListItemFillClass": ["pf-l-split__item", "pf-m-fill"], + "kcSelectAuthListItemIconPropertyClass": ["fa-2x", "select-auth-box-icon-properties"], + "kcSelectAuthListItemIconClass": ["pf-l-split__item", "select-auth-box-icon"], + "kcSelectAuthListItemTitle": ["select-auth-box-paragraph"], + "kcSelectAuthListItemInfoClass": ["list-view-pf-main-info"], + "kcSelectAuthListItemLeftClass": ["list-view-pf-left"], + "kcSelectAuthListItemBodyClass": ["list-view-pf-body"], + "kcSelectAuthListItemDescriptionClass": ["list-view-pf-description"], + "kcSelectAuthListItemHeadingClass": ["list-group-item-heading"], + "kcSelectAuthListItemHelpTextClass": ["list-group-item-text"], + + // css classes for the authenticators + "kcAuthenticatorDefaultClass": ["fa", "list-view-pf-icon-lg"], + "kcAuthenticatorPasswordClass": ["fa", "fa-unlock list-view-pf-icon-lg"], + "kcAuthenticatorOTPClass": ["fa", "fa-mobile", "list-view-pf-icon-lg"], + "kcAuthenticatorWebAuthnClass": ["fa", "fa-key", "list-view-pf-icon-lg"], + "kcAuthenticatorWebAuthnPasswordlessClass": ["fa", "fa-key", "list-view-pf-icon-lg"], + + //css classes for the OTP Login Form + "kcSelectOTPListClass": ["card-pf", "card-pf-view", "card-pf-view-select", "card-pf-view-single-select"], + "kcSelectOTPListItemClass": ["card-pf-body", "card-pf-top-element"], + "kcAuthenticatorOtpCircleClass": ["fa", "fa-mobile", "card-pf-icon-circle"], + "kcSelectOTPItemHeadingClass": ["card-pf-title", "text-center"], + "kcFormOptionsWrapperClass": [] + }, + "account": { + ...defaultKcTemplateProps.account + } +}; + +assert>(); +assert(); +assert(); + +/** To use if you don't want any default */ +export const allClearKcProps = { + "login": allPropertiesValuesToUndefined(defaultKcProps.login), + "account": allPropertiesValuesToUndefined(defaultKcProps.account) +}; + +assert>(); + +export namespace PageProps { + export type Login = { + kcContext: KcContext; + i18n: I18n; + doFetchDefaultThemeResources?: boolean; + LoginThemeTemplate: (props: LoginThemeTemplateProps) => JSX.Element | null; + } & KcProps.Login; + + export type Account = { + kcContext: KcContext; + i18n: I18n; + doFetchDefaultThemeResources?: boolean; + AccountThemeTemplate: (props: LoginThemeTemplateProps) => JSX.Element | null; + } & KcProps.Account; +} diff --git a/src/tsconfig.json b/src/tsconfig.json index aa8ef65a..53e3fdda 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -7,7 +7,11 @@ "target": "ES2017", "lib": ["es2015", "DOM", "ES2019.Object"], "moduleResolution": "node", - "jsx": "react", + "baseUrl": ".", + "paths": { + "keycloakify/*": ["./*"] + }, + "jsx": "react-jsx", "allowSyntheticDefaultImports": true }, "exclude": ["./test", "./bin"],