Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
85a6835748 | |||
259271bc0f | |||
b7bc0f178b | |||
688455d0aa | |||
3c96d2ea42 | |||
ab81481e5a | |||
a429ad5dcf | |||
5e1c5b510b | |||
9e63183f4b | |||
b1e740f026 | |||
ce4ea55438 | |||
18ab7cd22f | |||
8807743daf | |||
aad50377ff | |||
4b3ae58ea7 | |||
ce2c68ecc9 | |||
0c155a7a2e | |||
afddfe8b58 | |||
5fa0915271 | |||
6a0a170b17 | |||
4dde5b6e45 | |||
4b93a1cb9e | |||
e3a0639a0c | |||
4d3220820b | |||
a4ac9fb0f3 | |||
1ff79ecf07 | |||
1166b16420 | |||
213224942f | |||
ff16e66275 | |||
3c338e983f | |||
2c11ba6520 | |||
9a21656706 | |||
e96ee5ba53 | |||
b421633a8a | |||
e2e0d62560 | |||
c71fb06940 | |||
e2171af99c | |||
8cebf049d4 | |||
ef139ed1cc | |||
d717de006a | |||
a44f091878 | |||
1b37ba5339 | |||
bbaa90e997 | |||
86e6c4a419 | |||
4159883791 |
46
.github/workflows/ci.yaml
vendored
46
.github/workflows/ci.yaml
vendored
@ -13,10 +13,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ !github.event.created && github.repository != 'garronej/ts-ci' }}
|
if: ${{ !github.event.created && github.repository != 'garronej/ts-ci' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.3.4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v2.1.3
|
- uses: actions/setup-node@v3
|
||||||
- uses: bahmutov/npm-install@v1
|
- uses: bahmutov/npm-install@v1
|
||||||
- name: If this step fails run 'npm run lint' and 'npm run format' then commit again.
|
- name: If this step fails run 'yarn format' then commit again.
|
||||||
run: |
|
run: |
|
||||||
PACKAGE_MANAGER=npm
|
PACKAGE_MANAGER=npm
|
||||||
if [ -f "./yarn.lock" ]; then
|
if [ -f "./yarn.lock" ]; then
|
||||||
@ -24,22 +24,21 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
$PACKAGE_MANAGER run format:check
|
$PACKAGE_MANAGER run format:check
|
||||||
test:
|
test:
|
||||||
runs-on: macos-10.15
|
runs-on: ${{ matrix.os }}
|
||||||
needs: test_lint
|
needs: test_lint
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node: [ '15', '14' ]
|
node: [ '14', '15' ,'16', '17' ]
|
||||||
name: Test with Node v${{ matrix.node }}
|
os: [ windows-latest, ubuntu-latest ]
|
||||||
|
name: Test with Node v${{ matrix.node }} on ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Tell if project is using npm or yarn
|
- name: Tell if project is using npm or yarn
|
||||||
id: step1
|
id: step1
|
||||||
uses: garronej/ts-ci@v1.1.7
|
uses: garronej/ts-ci@v2.0.2
|
||||||
with:
|
with:
|
||||||
action_name: tell_if_project_uses_npm_or_yarn
|
action_name: tell_if_project_uses_npm_or_yarn
|
||||||
- uses: actions/checkout@v2.3.4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v2.1.3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- uses: bahmutov/npm-install@v1
|
- uses: bahmutov/npm-install@v1
|
||||||
@ -65,9 +64,9 @@ jobs:
|
|||||||
from_version: ${{ steps.step1.outputs.from_version }}
|
from_version: ${{ steps.step1.outputs.from_version }}
|
||||||
to_version: ${{ steps.step1.outputs.to_version }}
|
to_version: ${{ steps.step1.outputs.to_version }}
|
||||||
is_upgraded_version: ${{ steps.step1.outputs.is_upgraded_version }}
|
is_upgraded_version: ${{ steps.step1.outputs.is_upgraded_version }}
|
||||||
is_release_beta: ${{steps.step1.outputs.is_release_beta }}
|
is_pre_release: ${{steps.step1.outputs.is_pre_release }}
|
||||||
steps:
|
steps:
|
||||||
- uses: garronej/ts-ci@v1.1.7
|
- uses: garronej/ts-ci@v2.0.2
|
||||||
id: step1
|
id: step1
|
||||||
with:
|
with:
|
||||||
action_name: is_package_json_version_upgraded
|
action_name: is_package_json_version_upgraded
|
||||||
@ -75,13 +74,13 @@ jobs:
|
|||||||
|
|
||||||
create_github_release:
|
create_github_release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# We create a release only if the version have been upgraded and we are on a default branch
|
# We create a release only if the version have been upgraded and we are on the main branch
|
||||||
# PR on the default branch can release beta but not real release
|
# or if we are on a branch of the repo that has an PR open on main.
|
||||||
if: |
|
if: |
|
||||||
needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true' &&
|
needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true' &&
|
||||||
(
|
(
|
||||||
github.event_name == 'push' ||
|
github.event_name == 'push' ||
|
||||||
needs.check_if_version_upgraded.outputs.is_release_beta == 'true'
|
needs.check_if_version_upgraded.outputs.is_pre_release == 'true'
|
||||||
)
|
)
|
||||||
needs:
|
needs:
|
||||||
- check_if_version_upgraded
|
- check_if_version_upgraded
|
||||||
@ -93,7 +92,7 @@ jobs:
|
|||||||
target_commitish: ${{ github.head_ref || github.ref }}
|
target_commitish: ${{ github.head_ref || github.ref }}
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: ${{ needs.check_if_version_upgraded.outputs.is_release_beta == 'true' }}
|
prerelease: ${{ needs.check_if_version_upgraded.outputs.is_pre_release == 'true' }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@ -103,12 +102,11 @@ jobs:
|
|||||||
- create_github_release
|
- create_github_release
|
||||||
- check_if_version_upgraded
|
- check_if_version_upgraded
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.3.4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.ref }}
|
ref: ${{ github.ref }}
|
||||||
- uses: actions/setup-node@v2.1.3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '15'
|
|
||||||
registry-url: https://registry.npmjs.org/
|
registry-url: https://registry.npmjs.org/
|
||||||
- uses: bahmutov/npm-install@v1
|
- uses: bahmutov/npm-install@v1
|
||||||
- run: |
|
- run: |
|
||||||
@ -117,7 +115,7 @@ jobs:
|
|||||||
PACKAGE_MANAGER=yarn
|
PACKAGE_MANAGER=yarn
|
||||||
fi
|
fi
|
||||||
$PACKAGE_MANAGER run build
|
$PACKAGE_MANAGER run build
|
||||||
- run: npx -y -p denoify@1.0.2 enable_short_npm_import_path
|
- run: npx -y -p denoify@1.2.2 enable_short_npm_import_path
|
||||||
env:
|
env:
|
||||||
DRY_RUN: "0"
|
DRY_RUN: "0"
|
||||||
- name: Publishing on NPM
|
- name: Publishing on NPM
|
||||||
@ -131,11 +129,11 @@ jobs:
|
|||||||
false
|
false
|
||||||
fi
|
fi
|
||||||
EXTRA_ARGS=""
|
EXTRA_ARGS=""
|
||||||
if [ "$IS_BETA" = "true" ]; then
|
if [ "$IS_PRE_RELEASE" = "true" ]; then
|
||||||
EXTRA_ARGS="--tag beta"
|
EXTRA_ARGS="--tag next"
|
||||||
fi
|
fi
|
||||||
npm publish $EXTRA_ARGS
|
npm publish $EXTRA_ARGS
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||||
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
|
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
|
||||||
IS_BETA: ${{ needs.check_if_version_upgraded.outputs.is_release_beta }}
|
IS_PRE_RELEASE: ${{ needs.check_if_version_upgraded.outputs.is_pre_release }}
|
24
README.md
24
README.md
@ -49,6 +49,30 @@
|
|||||||
|
|
||||||
# Changelog highlights
|
# Changelog highlights
|
||||||
|
|
||||||
|
## 6.8.0
|
||||||
|
|
||||||
|
- It is now possible to pass a custom `<Template />` component as a prop to `<KcApp />` and every
|
||||||
|
individual page (`<Login />`, `<RegisterUserProfile />`, ...) it enables to customize only the header and footer for
|
||||||
|
example without having to switch to a full-component level customization. [See issue](https://github.com/InseeFrLab/keycloakify/issues/191).
|
||||||
|
|
||||||
|
## 6.7.0
|
||||||
|
|
||||||
|
- Add support for `webauthn-authenticate.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/185).
|
||||||
|
|
||||||
|
## 6.6.0
|
||||||
|
|
||||||
|
- Add support for `login-password.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/184).
|
||||||
|
|
||||||
|
## 6.5.0
|
||||||
|
|
||||||
|
- Add support for `login-username.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/183).
|
||||||
|
|
||||||
|
## 6.4.0
|
||||||
|
|
||||||
|
- You can now optionally pass a `doFetchDefaultThemeResources: boolean` prop to every page component and the default `<KcApp />`
|
||||||
|
This enables you to prevent the default CSS and JS that comes with the builtin Keycloak theme to be downloaded.
|
||||||
|
You'll get [a black slate](https://user-images.githubusercontent.com/6702424/192619083-4baa5df4-4a21-4ec7-8e28-d200d1208299.png).
|
||||||
|
|
||||||
## 6.0.0
|
## 6.0.0
|
||||||
|
|
||||||
- Bundle size drastically reduced, locals and component dynamically loaded.
|
- Bundle size drastically reduced, locals and component dynamically loaded.
|
||||||
|
15
package.json
15
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloakify",
|
"name": "keycloakify",
|
||||||
"version": "6.1.0",
|
"version": "6.8.0",
|
||||||
"description": "Keycloak theme generator for Reacts app",
|
"description": "Keycloak theme generator for Reacts app",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -13,7 +13,8 @@
|
|||||||
"build:test": "rimraf dist_test/ && tsc -p src/test && yarn copy-files dist_test/",
|
"build:test": "rimraf dist_test/ && tsc -p src/test && yarn copy-files dist_test/",
|
||||||
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
|
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
|
||||||
"copy-files": "copyfiles -u 1 src/**/*.ftl",
|
"copy-files": "copyfiles -u 1 src/**/*.ftl",
|
||||||
"test": "yarn build:test && node dist_test/test/bin && node dist_test/test/lib",
|
"pretest": "yarn build:test",
|
||||||
|
"test": "node dist_test/test/bin && node dist_test/test/lib",
|
||||||
"generate-messages": "node dist/bin/generate-i18n-messages.js",
|
"generate-messages": "node dist/bin/generate-i18n-messages.js",
|
||||||
"link_in_test_app": "node dist/bin/link_in_test_app.js",
|
"link_in_test_app": "node dist/bin/link_in_test_app.js",
|
||||||
"_format": "prettier '**/*.{ts,tsx,json,md}'",
|
"_format": "prettier '**/*.{ts,tsx,json,md}'",
|
||||||
@ -58,6 +59,7 @@
|
|||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.0.0",
|
||||||
"@emotion/react": "^11.4.1",
|
"@emotion/react": "^11.4.1",
|
||||||
"@types/memoizee": "^0.4.7",
|
"@types/memoizee": "^0.4.7",
|
||||||
"@types/minimist": "^1.2.2",
|
"@types/minimist": "^1.2.2",
|
||||||
@ -76,16 +78,17 @@
|
|||||||
"@octokit/rest": "^18.12.0",
|
"@octokit/rest": "^18.12.0",
|
||||||
"cheerio": "^1.0.0-rc.5",
|
"cheerio": "^1.0.0-rc.5",
|
||||||
"cli-select": "^1.1.2",
|
"cli-select": "^1.1.2",
|
||||||
"evt": "^2.4.0",
|
"evt": "^2.4.5",
|
||||||
"memoizee": "^0.4.15",
|
"memoizee": "^0.4.15",
|
||||||
"minimal-polyfills": "^2.2.2",
|
"minimal-polyfills": "^2.2.2",
|
||||||
"minimist": "^1.2.6",
|
"minimist": "^1.2.6",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"powerhooks": "^0.20.15",
|
"powerhooks": "^0.20.22",
|
||||||
"react-markdown": "^5.0.3",
|
"react-markdown": "^5.0.3",
|
||||||
|
"rfc4648": "^1.5.2",
|
||||||
"scripting-tools": "^0.19.13",
|
"scripting-tools": "^0.19.13",
|
||||||
"tsafe": "^1.0.1",
|
"tsafe": "^1.1.3",
|
||||||
"tss-react": "^4.1.3",
|
"tss-react": "^4.3.4",
|
||||||
"zod": "^3.17.10"
|
"zod": "^3.17.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,6 +272,11 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
|
|||||||
|
|
||||||
<#list object as array_item>
|
<#list object as array_item>
|
||||||
|
|
||||||
|
<#if !array_item??>
|
||||||
|
<#local out_seq += ["null,"]>
|
||||||
|
<#continue>
|
||||||
|
</#if>
|
||||||
|
|
||||||
<#local rec_out = ftl_object_to_js_code_declaring_an_object(array_item, path + [ i ])>
|
<#local rec_out = ftl_object_to_js_code_declaring_an_object(array_item, path + [ i ])>
|
||||||
|
|
||||||
<#local i = i + 1>
|
<#local i = i + 1>
|
||||||
|
@ -13,6 +13,9 @@ import { Reflect } from "tsafe/Reflect";
|
|||||||
// https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java
|
// https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java
|
||||||
export const pageIds = [
|
export const pageIds = [
|
||||||
"login.ftl",
|
"login.ftl",
|
||||||
|
"login-username.ftl",
|
||||||
|
"login-password.ftl",
|
||||||
|
"webauthn-authenticate.ftl",
|
||||||
"register.ftl",
|
"register.ftl",
|
||||||
"register-user-profile.ftl",
|
"register-user-profile.ftl",
|
||||||
"info.ftl",
|
"info.ftl",
|
||||||
@ -27,7 +30,9 @@ export const pageIds = [
|
|||||||
"login-idp-link-email.ftl",
|
"login-idp-link-email.ftl",
|
||||||
"login-page-expired.ftl",
|
"login-page-expired.ftl",
|
||||||
"login-config-totp.ftl",
|
"login-config-totp.ftl",
|
||||||
"logout-confirm.ftl"
|
"logout-confirm.ftl",
|
||||||
|
"update-user-profile.ftl",
|
||||||
|
"idp-review-user-profile.ftl"
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
||||||
|
@ -56,7 +56,7 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//We want, however, to test in a container running the latest Keycloak version
|
//We want, however, to test in a container running the latest Keycloak version
|
||||||
const containerKeycloakVersion = "18.0.2";
|
const containerKeycloakVersion = "19.0.1";
|
||||||
|
|
||||||
generateStartKeycloakTestingContainer({
|
generateStartKeycloakTestingContainer({
|
||||||
keycloakThemeBuildingDirPath,
|
keycloakThemeBuildingDirPath,
|
||||||
|
@ -1,18 +1,27 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Error = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Error; i18n: I18n } & KcProps) => {
|
export type ErrorProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.Error;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Error = memo((props: ErrorProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { message, client } = kcContext;
|
const { message, client } = kcContext;
|
||||||
|
|
||||||
const { msg } = i18n;
|
const { msg } = i18n;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("errorTitle")}
|
headerNode={msg("errorTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
|
60
src/lib/components/IdpReviewUserProfile.tsx
Normal file
60
src/lib/components/IdpReviewUserProfile.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import React, { useState, memo } from "react";
|
||||||
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
|
import type { KcProps } from "./KcProps";
|
||||||
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
import { UserProfileFormFields } from "./shared/UserProfileCommons";
|
||||||
|
|
||||||
|
export type IdpReviewUserProfileProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.IdpReviewUserProfile;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const IdpReviewUserProfile = memo((props: IdpReviewUserProfileProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
|
const { url } = kcContext;
|
||||||
|
|
||||||
|
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Template
|
||||||
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
|
headerNode={msg("loginIdpReviewProfileTitle")}
|
||||||
|
formNode={
|
||||||
|
<form id="kc-idp-review-profile-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
|
||||||
|
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...kcProps} />
|
||||||
|
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)} />
|
||||||
|
</div>
|
||||||
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
|
<input
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
type="submit"
|
||||||
|
value={msgStr("doSubmit")}
|
||||||
|
disabled={!isFomSubmittable}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default IdpReviewUserProfile;
|
@ -1,11 +1,21 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import { assert } from "../tools/assert";
|
import { assert } from "../tools/assert";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Info = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Info; i18n: I18n } & KcProps) => {
|
export type InfoProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.Info;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Info = memo((props: InfoProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { msgStr, msg } = i18n;
|
const { msgStr, msg } = i18n;
|
||||||
|
|
||||||
assert(kcContext.message !== undefined);
|
assert(kcContext.message !== undefined);
|
||||||
@ -14,8 +24,7 @@ const Info = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Inf
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={messageHeader !== undefined ? <>{messageHeader}</> : <>{message.summary}</>}
|
headerNode={messageHeader !== undefined ? <>{messageHeader}</> : <>{message.summary}</>}
|
||||||
formNode={
|
formNode={
|
||||||
|
@ -3,6 +3,8 @@ import type { KcContextBase } from "../getKcContext/KcContextBase";
|
|||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import { __unsafe_useI18n as useI18n } from "../i18n";
|
import { __unsafe_useI18n as useI18n } from "../i18n";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
|
|
||||||
const Login = lazy(() => import("./Login"));
|
const Login = lazy(() => import("./Login"));
|
||||||
const Register = lazy(() => import("./Register"));
|
const Register = lazy(() => import("./Register"));
|
||||||
@ -13,6 +15,9 @@ const LoginResetPassword = lazy(() => import("./LoginResetPassword"));
|
|||||||
const LoginVerifyEmail = lazy(() => import("./LoginVerifyEmail"));
|
const LoginVerifyEmail = lazy(() => import("./LoginVerifyEmail"));
|
||||||
const Terms = lazy(() => import("./Terms"));
|
const Terms = lazy(() => import("./Terms"));
|
||||||
const LoginOtp = lazy(() => import("./LoginOtp"));
|
const LoginOtp = lazy(() => import("./LoginOtp"));
|
||||||
|
const LoginPassword = lazy(() => import("./LoginPassword"));
|
||||||
|
const LoginUsername = lazy(() => import("./LoginUsername"));
|
||||||
|
const WebauthnAuthenticate = lazy(() => import("./WebauthnAuthenticate"));
|
||||||
const LoginUpdatePassword = lazy(() => import("./LoginUpdatePassword"));
|
const LoginUpdatePassword = lazy(() => import("./LoginUpdatePassword"));
|
||||||
const LoginUpdateProfile = lazy(() => import("./LoginUpdateProfile"));
|
const LoginUpdateProfile = lazy(() => import("./LoginUpdateProfile"));
|
||||||
const LoginIdpLinkConfirm = lazy(() => import("./LoginIdpLinkConfirm"));
|
const LoginIdpLinkConfirm = lazy(() => import("./LoginIdpLinkConfirm"));
|
||||||
@ -20,8 +25,19 @@ const LoginPageExpired = lazy(() => import("./LoginPageExpired"));
|
|||||||
const LoginIdpLinkEmail = lazy(() => import("./LoginIdpLinkEmail"));
|
const LoginIdpLinkEmail = lazy(() => import("./LoginIdpLinkEmail"));
|
||||||
const LoginConfigTotp = lazy(() => import("./LoginConfigTotp"));
|
const LoginConfigTotp = lazy(() => import("./LoginConfigTotp"));
|
||||||
const LogoutConfirm = lazy(() => import("./LogoutConfirm"));
|
const LogoutConfirm = lazy(() => import("./LogoutConfirm"));
|
||||||
|
const UpdateUserProfile = lazy(() => import("./UpdateUserProfile"));
|
||||||
|
const IdpReviewUserProfile = lazy(() => import("./IdpReviewUserProfile"));
|
||||||
|
|
||||||
|
export type KcAppProps = KcProps & {
|
||||||
|
kcContext: KcContextBase;
|
||||||
|
i18n?: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const KcApp = memo((props_: KcAppProps) => {
|
||||||
|
const { kcContext, i18n: userProvidedI18n, Template = DefaultTemplate, ...kcProps } = props_;
|
||||||
|
|
||||||
const KcApp = memo(({ kcContext, i18n: userProvidedI18n, ...kcProps }: { kcContext: KcContextBase; i18n?: I18n } & KcProps) => {
|
|
||||||
const i18n = (function useClosure() {
|
const i18n = (function useClosure() {
|
||||||
const i18n = useI18n({
|
const i18n = useI18n({
|
||||||
kcContext,
|
kcContext,
|
||||||
@ -36,44 +52,54 @@ const KcApp = memo(({ kcContext, i18n: userProvidedI18n, ...kcProps }: { kcConte
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = { i18n, ...kcProps };
|
const commonProps = { i18n, Template, ...kcProps };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense>
|
<Suspense>
|
||||||
{(() => {
|
{(() => {
|
||||||
switch (kcContext.pageId) {
|
switch (kcContext.pageId) {
|
||||||
case "login.ftl":
|
case "login.ftl":
|
||||||
return <Login {...{ kcContext, ...props }} />;
|
return <Login {...{ kcContext, ...commonProps }} />;
|
||||||
case "register.ftl":
|
case "register.ftl":
|
||||||
return <Register {...{ kcContext, ...props }} />;
|
return <Register {...{ kcContext, ...commonProps }} />;
|
||||||
case "register-user-profile.ftl":
|
case "register-user-profile.ftl":
|
||||||
return <RegisterUserProfile {...{ kcContext, ...props }} />;
|
return <RegisterUserProfile {...{ kcContext, ...commonProps }} />;
|
||||||
case "info.ftl":
|
case "info.ftl":
|
||||||
return <Info {...{ kcContext, ...props }} />;
|
return <Info {...{ kcContext, ...commonProps }} />;
|
||||||
case "error.ftl":
|
case "error.ftl":
|
||||||
return <Error {...{ kcContext, ...props }} />;
|
return <Error {...{ kcContext, ...commonProps }} />;
|
||||||
case "login-reset-password.ftl":
|
case "login-reset-password.ftl":
|
||||||
return <LoginResetPassword {...{ kcContext, ...props }} />;
|
return <LoginResetPassword {...{ kcContext, ...commonProps }} />;
|
||||||
case "login-verify-email.ftl":
|
case "login-verify-email.ftl":
|
||||||
return <LoginVerifyEmail {...{ kcContext, ...props }} />;
|
return <LoginVerifyEmail {...{ kcContext, ...commonProps }} />;
|
||||||
case "terms.ftl":
|
case "terms.ftl":
|
||||||
return <Terms {...{ kcContext, ...props }} />;
|
return <Terms {...{ kcContext, ...commonProps }} />;
|
||||||
case "login-otp.ftl":
|
case "login-otp.ftl":
|
||||||
return <LoginOtp {...{ kcContext, ...props }} />;
|
return <LoginOtp {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-username.ftl":
|
||||||
|
return <LoginUsername {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "login-password.ftl":
|
||||||
|
return <LoginPassword {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "webauthn-authenticate.ftl":
|
||||||
|
return <WebauthnAuthenticate {...{ kcContext, ...commonProps }} />;
|
||||||
case "login-update-password.ftl":
|
case "login-update-password.ftl":
|
||||||
return <LoginUpdatePassword {...{ kcContext, ...props }} />;
|
return <LoginUpdatePassword {...{ kcContext, ...commonProps }} />;
|
||||||
case "login-update-profile.ftl":
|
case "login-update-profile.ftl":
|
||||||
return <LoginUpdateProfile {...{ kcContext, ...props }} />;
|
return <LoginUpdateProfile {...{ kcContext, ...commonProps }} />;
|
||||||
case "login-idp-link-confirm.ftl":
|
case "login-idp-link-confirm.ftl":
|
||||||
return <LoginIdpLinkConfirm {...{ kcContext, ...props }} />;
|
return <LoginIdpLinkConfirm {...{ kcContext, ...commonProps }} />;
|
||||||
case "login-idp-link-email.ftl":
|
case "login-idp-link-email.ftl":
|
||||||
return <LoginIdpLinkEmail {...{ kcContext, ...props }} />;
|
return <LoginIdpLinkEmail {...{ kcContext, ...commonProps }} />;
|
||||||
case "login-page-expired.ftl":
|
case "login-page-expired.ftl":
|
||||||
return <LoginPageExpired {...{ kcContext, ...props }} />;
|
return <LoginPageExpired {...{ kcContext, ...commonProps }} />;
|
||||||
case "login-config-totp.ftl":
|
case "login-config-totp.ftl":
|
||||||
return <LoginConfigTotp {...{ kcContext, ...props }} />;
|
return <LoginConfigTotp {...{ kcContext, ...commonProps }} />;
|
||||||
case "logout-confirm.ftl":
|
case "logout-confirm.ftl":
|
||||||
return <LogoutConfirm {...{ kcContext, ...props }} />;
|
return <LogoutConfirm {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "update-user-profile.ftl":
|
||||||
|
return <UpdateUserProfile {...{ kcContext, ...commonProps }} />;
|
||||||
|
case "idp-review-user-profile.ftl":
|
||||||
|
return <IdpReviewUserProfile {...{ kcContext, ...commonProps }} />;
|
||||||
}
|
}
|
||||||
})()}
|
})()}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
@ -84,6 +84,7 @@ export type KcProps = KcPropsGeneric<
|
|||||||
| "kcFormSocialAccountDoubleListClass"
|
| "kcFormSocialAccountDoubleListClass"
|
||||||
| "kcFormSocialAccountListLinkClass"
|
| "kcFormSocialAccountListLinkClass"
|
||||||
| "kcWebAuthnKeyIcon"
|
| "kcWebAuthnKeyIcon"
|
||||||
|
| "kcWebAuthnDefaultIcon"
|
||||||
| "kcFormClass"
|
| "kcFormClass"
|
||||||
| "kcFormGroupErrorClass"
|
| "kcFormGroupErrorClass"
|
||||||
| "kcLabelClass"
|
| "kcLabelClass"
|
||||||
@ -105,12 +106,16 @@ export type KcProps = KcPropsGeneric<
|
|||||||
| "kcSrOnlyClass"
|
| "kcSrOnlyClass"
|
||||||
| "kcSelectAuthListClass"
|
| "kcSelectAuthListClass"
|
||||||
| "kcSelectAuthListItemClass"
|
| "kcSelectAuthListItemClass"
|
||||||
|
| "kcSelectAuthListItemFillClass"
|
||||||
| "kcSelectAuthListItemInfoClass"
|
| "kcSelectAuthListItemInfoClass"
|
||||||
| "kcSelectAuthListItemLeftClass"
|
| "kcSelectAuthListItemLeftClass"
|
||||||
| "kcSelectAuthListItemBodyClass"
|
| "kcSelectAuthListItemBodyClass"
|
||||||
| "kcSelectAuthListItemDescriptionClass"
|
| "kcSelectAuthListItemDescriptionClass"
|
||||||
| "kcSelectAuthListItemHeadingClass"
|
| "kcSelectAuthListItemHeadingClass"
|
||||||
| "kcSelectAuthListItemHelpTextClass"
|
| "kcSelectAuthListItemHelpTextClass"
|
||||||
|
| "kcSelectAuthListItemIconPropertyClass"
|
||||||
|
| "kcSelectAuthListItemIconClass"
|
||||||
|
| "kcSelectAuthListItemTitle"
|
||||||
| "kcAuthenticatorDefaultClass"
|
| "kcAuthenticatorDefaultClass"
|
||||||
| "kcAuthenticatorPasswordClass"
|
| "kcAuthenticatorPasswordClass"
|
||||||
| "kcAuthenticatorOTPClass"
|
| "kcAuthenticatorOTPClass"
|
||||||
@ -138,6 +143,7 @@ export const defaultKcProps = {
|
|||||||
"kcFormSocialAccountDoubleListClass": ["login-pf-social-double-col"],
|
"kcFormSocialAccountDoubleListClass": ["login-pf-social-double-col"],
|
||||||
"kcFormSocialAccountListLinkClass": ["login-pf-social-link"],
|
"kcFormSocialAccountListLinkClass": ["login-pf-social-link"],
|
||||||
"kcWebAuthnKeyIcon": ["pficon", "pficon-key"],
|
"kcWebAuthnKeyIcon": ["pficon", "pficon-key"],
|
||||||
|
"kcWebAuthnDefaultIcon": ["pficon", "pficon-key"],
|
||||||
|
|
||||||
"kcFormClass": ["form-horizontal"],
|
"kcFormClass": ["form-horizontal"],
|
||||||
"kcFormGroupErrorClass": ["has-error"],
|
"kcFormGroupErrorClass": ["has-error"],
|
||||||
@ -173,6 +179,10 @@ export const defaultKcProps = {
|
|||||||
// css classes for select-authenticator form
|
// css classes for select-authenticator form
|
||||||
"kcSelectAuthListClass": ["list-group", "list-view-pf"],
|
"kcSelectAuthListClass": ["list-group", "list-view-pf"],
|
||||||
"kcSelectAuthListItemClass": ["list-group-item", "list-view-pf-stacked"],
|
"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"],
|
"kcSelectAuthListItemInfoClass": ["list-view-pf-main-info"],
|
||||||
"kcSelectAuthListItemLeftClass": ["list-view-pf-left"],
|
"kcSelectAuthListItemLeftClass": ["list-view-pf-left"],
|
||||||
"kcSelectAuthListItemBodyClass": ["list-view-pf-body"],
|
"kcSelectAuthListItemBodyClass": ["list-view-pf-body"],
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState, memo } from "react";
|
import React, { useState, memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
@ -7,7 +8,16 @@ import { useConstCallback } from "powerhooks/useConstCallback";
|
|||||||
import type { FormEventHandler } from "react";
|
import type { FormEventHandler } from "react";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Login = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Login; i18n: I18n } & KcProps) => {
|
export type LoginProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.Login;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Login = memo((props: LoginProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
|
const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
@ -32,20 +42,21 @@ const Login = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Lo
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
displayInfo={social.displayInfo}
|
displayInfo={social.displayInfo}
|
||||||
displayWide={realm.password && social.providers !== undefined}
|
displayWide={realm.password && social.providers !== undefined}
|
||||||
headerNode={msg("doLogIn")}
|
headerNode={msg("doLogIn")}
|
||||||
formNode={
|
formNode={
|
||||||
<div id="kc-form" className={cx(realm.password && social.providers !== undefined && props.kcContentWrapperClass)}>
|
<div id="kc-form" className={cx(realm.password && social.providers !== undefined && kcProps.kcContentWrapperClass)}>
|
||||||
<div
|
<div
|
||||||
id="kc-form-wrapper"
|
id="kc-form-wrapper"
|
||||||
className={cx(realm.password && social.providers && [props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass])}
|
className={cx(
|
||||||
|
realm.password && social.providers && [kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass]
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{realm.password && (
|
{realm.password && (
|
||||||
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
|
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
{(() => {
|
{(() => {
|
||||||
const label = !realm.loginWithEmailAllowed
|
const label = !realm.loginWithEmailAllowed
|
||||||
? "username"
|
? "username"
|
||||||
@ -57,13 +68,13 @@ const Login = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Lo
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<label htmlFor={autoCompleteHelper} className={cx(props.kcLabelClass)}>
|
<label htmlFor={autoCompleteHelper} className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg(label)}
|
{msg(label)}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
tabIndex={1}
|
tabIndex={1}
|
||||||
id={autoCompleteHelper}
|
id={autoCompleteHelper}
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
//NOTE: This is used by Google Chrome auto fill so we use it to tell
|
//NOTE: This is used by Google Chrome auto fill so we use it to tell
|
||||||
//the browser how to pre fill the form but before submit we put it back
|
//the browser how to pre fill the form but before submit we put it back
|
||||||
//to username because it is what keycloak expects.
|
//to username because it is what keycloak expects.
|
||||||
@ -81,20 +92,20 @@ const Login = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Lo
|
|||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<label htmlFor="password" className={cx(props.kcLabelClass)}>
|
<label htmlFor="password" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("password")}
|
{msg("password")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
tabIndex={2}
|
tabIndex={2}
|
||||||
id="password"
|
id="password"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
name="password"
|
name="password"
|
||||||
type="password"
|
type="password"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcFormGroupClass, props.kcFormSettingClass)}>
|
<div className={cx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
|
||||||
<div id="kc-form-options">
|
<div id="kc-form-options">
|
||||||
{realm.rememberMe && !usernameEditDisabled && (
|
{realm.rememberMe && !usernameEditDisabled && (
|
||||||
<div className="checkbox">
|
<div className="checkbox">
|
||||||
@ -115,7 +126,7 @@ const Login = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Lo
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
|
||||||
{realm.resetPasswordAllowed && (
|
{realm.resetPasswordAllowed && (
|
||||||
<span>
|
<span>
|
||||||
<a tabIndex={5} href={url.loginResetCredentialsUrl}>
|
<a tabIndex={5} href={url.loginResetCredentialsUrl}>
|
||||||
@ -125,7 +136,7 @@ const Login = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Lo
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormGroupClass)}>
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
id="id-hidden-input"
|
id="id-hidden-input"
|
||||||
@ -139,10 +150,10 @@ const Login = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Lo
|
|||||||
<input
|
<input
|
||||||
tabIndex={4}
|
tabIndex={4}
|
||||||
className={cx(
|
className={cx(
|
||||||
props.kcButtonClass,
|
kcProps.kcButtonClass,
|
||||||
props.kcButtonPrimaryClass,
|
kcProps.kcButtonPrimaryClass,
|
||||||
props.kcButtonBlockClass,
|
kcProps.kcButtonBlockClass,
|
||||||
props.kcButtonLargeClass
|
kcProps.kcButtonLargeClass
|
||||||
)}
|
)}
|
||||||
name="login"
|
name="login"
|
||||||
id="kc-login"
|
id="kc-login"
|
||||||
@ -155,15 +166,15 @@ const Login = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Lo
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{realm.password && social.providers !== undefined && (
|
{realm.password && social.providers !== undefined && (
|
||||||
<div id="kc-social-providers" className={cx(props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass)}>
|
<div id="kc-social-providers" className={cx(kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass)}>
|
||||||
<ul
|
<ul
|
||||||
className={cx(
|
className={cx(
|
||||||
props.kcFormSocialAccountListClass,
|
kcProps.kcFormSocialAccountListClass,
|
||||||
social.providers.length > 4 && props.kcFormSocialAccountDoubleListClass
|
social.providers.length > 4 && kcProps.kcFormSocialAccountDoubleListClass
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{social.providers.map(p => (
|
{social.providers.map(p => (
|
||||||
<li key={p.providerId} className={cx(props.kcFormSocialAccountListLinkClass)}>
|
<li key={p.providerId} className={cx(kcProps.kcFormSocialAccountListLinkClass)}>
|
||||||
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
|
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
|
||||||
<span>{p.displayName}</span>
|
<span>{p.displayName}</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginConfigTotp = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginConfigTotp; i18n: I18n } & KcProps) => {
|
export type LoginConfigTotpProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.LoginConfigTotp;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginConfigTotp = memo((props: LoginConfigTotpProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { url, isAppInitiatedAction, totp, mode, messagesPerField } = kcContext;
|
const { url, isAppInitiatedAction, totp, mode, messagesPerField } = kcContext;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
@ -13,15 +23,14 @@ const LoginConfigTotp = memo(({ kcContext, i18n, ...props }: { kcContext: KcCont
|
|||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const algToKeyUriAlg: Record<KcContextBase.LoginConfigTotp["totp"]["policy"]["algorithm"], string> = {
|
const algToKeyUriAlg: Record<KcContextBase.LoginConfigTotp["totp"]["policy"]["algorithm"], string> = {
|
||||||
HmacSHA1: "SHA1",
|
"HmacSHA1": "SHA1",
|
||||||
HmacSHA256: "SHA256",
|
"HmacSHA256": "SHA256",
|
||||||
HmacSHA512: "SHA512"
|
"HmacSHA512": "SHA512"
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
headerNode={msg("loginTotpTitle")}
|
headerNode={msg("loginTotpTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
<>
|
<>
|
||||||
@ -93,26 +102,26 @@ const LoginConfigTotp = memo(({ kcContext, i18n, ...props }: { kcContext: KcCont
|
|||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<form action={url.loginAction} className={cx(props.kcFormClass)} id="kc-totp-settings-form" method="post">
|
<form action={url.loginAction} className={cx(kcProps.kcFormClass)} id="kc-totp-settings-form" method="post">
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<label htmlFor="totp" className={cx(props.kcLabelClass)}>
|
<label htmlFor="totp" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("authenticatorCode")}
|
{msg("authenticatorCode")}
|
||||||
</label>{" "}
|
</label>{" "}
|
||||||
<span className="required">*</span>
|
<span className="required">*</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="totp"
|
id="totp"
|
||||||
name="totp"
|
name="totp"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
aria-invalid={messagesPerField.existsError("totp")}
|
aria-invalid={messagesPerField.existsError("totp")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{messagesPerField.existsError("totp") && (
|
{messagesPerField.existsError("totp") && (
|
||||||
<span id="input-error-otp-code" className={cx(props.kcInputErrorMessageClass)} aria-live="polite">
|
<span id="input-error-otp-code" className={cx(kcProps.kcInputErrorMessageClass)} aria-live="polite">
|
||||||
{messagesPerField.get("totp")}
|
{messagesPerField.get("totp")}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@ -121,24 +130,24 @@ const LoginConfigTotp = memo(({ kcContext, i18n, ...props }: { kcContext: KcCont
|
|||||||
{mode && <input type="hidden" id="mode" value={mode} />}
|
{mode && <input type="hidden" id="mode" value={mode} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<label htmlFor="userLabel" className={cx(props.kcLabelClass)}>
|
<label htmlFor="userLabel" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("loginTotpDeviceName")}
|
{msg("loginTotpDeviceName")}
|
||||||
</label>{" "}
|
</label>{" "}
|
||||||
{totp.otpCredentials.length >= 1 && <span className="required">*</span>}
|
{totp.otpCredentials.length >= 1 && <span className="required">*</span>}
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="userLabel"
|
id="userLabel"
|
||||||
name="userLabel"
|
name="userLabel"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
aria-invalid={messagesPerField.existsError("userLabel")}
|
aria-invalid={messagesPerField.existsError("userLabel")}
|
||||||
/>
|
/>
|
||||||
{messagesPerField.existsError("userLabel") && (
|
{messagesPerField.existsError("userLabel") && (
|
||||||
<span id="input-error-otp-label" className={cx(props.kcInputErrorMessageClass)} aria-live="polite">
|
<span id="input-error-otp-label" className={cx(kcProps.kcInputErrorMessageClass)} aria-live="polite">
|
||||||
{messagesPerField.get("userLabel")}
|
{messagesPerField.get("userLabel")}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@ -149,17 +158,17 @@ const LoginConfigTotp = memo(({ kcContext, i18n, ...props }: { kcContext: KcCont
|
|||||||
<>
|
<>
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
|
||||||
id="saveTOTPBtn"
|
id="saveTOTPBtn"
|
||||||
value={msgStr("doSubmit")}
|
value={msgStr("doSubmit")}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className={cx(
|
className={cx(
|
||||||
props.kcButtonClass,
|
kcProps.kcButtonClass,
|
||||||
props.kcButtonDefaultClass,
|
kcProps.kcButtonDefaultClass,
|
||||||
props.kcButtonLargeClass,
|
kcProps.kcButtonLargeClass,
|
||||||
props.kcButtonLargeClass
|
kcProps.kcButtonLargeClass
|
||||||
)}
|
)}
|
||||||
id="cancelTOTPBtn"
|
id="cancelTOTPBtn"
|
||||||
name="cancel-aia"
|
name="cancel-aia"
|
||||||
@ -171,7 +180,7 @@ const LoginConfigTotp = memo(({ kcContext, i18n, ...props }: { kcContext: KcCont
|
|||||||
) : (
|
) : (
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
|
||||||
id="saveTOTPBtn"
|
id="saveTOTPBtn"
|
||||||
value={msgStr("doSubmit")}
|
value={msgStr("doSubmit")}
|
||||||
/>
|
/>
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginIdpLinkConfirm = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginIdpLinkConfirm; i18n: I18n } & KcProps) => {
|
export type LoginIdpLinkConfirmProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.LoginIdpLinkConfirm;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginIdpLinkConfirm = memo((props: LoginIdpLinkConfirmProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { url, idpAlias } = kcContext;
|
const { url, idpAlias } = kcContext;
|
||||||
|
|
||||||
const { msg } = i18n;
|
const { msg } = i18n;
|
||||||
@ -14,15 +24,19 @@ const LoginIdpLinkConfirm = memo(({ kcContext, i18n, ...props }: { kcContext: Kc
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
headerNode={msg("confirmLinkIdpTitle")}
|
headerNode={msg("confirmLinkIdpTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
<form id="kc-register-form" action={url.loginAction} method="post">
|
<form id="kc-register-form" action={url.loginAction} method="post">
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonDefaultClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
name="submitAction"
|
name="submitAction"
|
||||||
id="updateProfile"
|
id="updateProfile"
|
||||||
value="updateProfile"
|
value="updateProfile"
|
||||||
@ -31,7 +45,12 @@ const LoginIdpLinkConfirm = memo(({ kcContext, i18n, ...props }: { kcContext: Kc
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonDefaultClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
name="submitAction"
|
name="submitAction"
|
||||||
id="linkAccount"
|
id="linkAccount"
|
||||||
value="linkAccount"
|
value="linkAccount"
|
||||||
|
@ -1,18 +1,27 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginIdpLinkEmail = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginIdpLinkEmail; i18n: I18n } & KcProps) => {
|
export type LoginIdpLinkEmailProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.LoginIdpLinkEmail;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginIdpLinkEmail = memo((props: LoginIdpLinkEmailProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { url, realm, brokerContext, idpAlias } = kcContext;
|
const { url, realm, brokerContext, idpAlias } = kcContext;
|
||||||
|
|
||||||
const { msg } = i18n;
|
const { msg } = i18n;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
headerNode={msg("emailLinkIdpTitle", idpAlias)}
|
headerNode={msg("emailLinkIdpTitle", idpAlias)}
|
||||||
formNode={
|
formNode={
|
||||||
<>
|
<>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, memo } from "react";
|
import React, { useEffect, memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { headInsert } from "../tools/headInsert";
|
import { headInsert } from "../tools/headInsert";
|
||||||
@ -7,7 +8,16 @@ import { pathJoin } from "../../bin/tools/pathJoin";
|
|||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginOtp = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginOtp; i18n: I18n } & KcProps) => {
|
export type LoginOtpProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.LoginOtp;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginOtp = memo((props: LoginOtpProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { otpLogin, url } = kcContext;
|
const { otpLogin, url } = kcContext;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
@ -33,46 +43,50 @@ const LoginOtp = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
headerNode={msg("doLogIn")}
|
headerNode={msg("doLogIn")}
|
||||||
formNode={
|
formNode={
|
||||||
<form id="kc-otp-login-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
<form id="kc-otp-login-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
|
||||||
{otpLogin.userOtpCredentials.length > 1 && (
|
{otpLogin.userOtpCredentials.length > 1 && (
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
{otpLogin.userOtpCredentials.map(otpCredential => (
|
{otpLogin.userOtpCredentials.map(otpCredential => (
|
||||||
<div key={otpCredential.id} className={cx(props.kcSelectOTPListClass)}>
|
<div key={otpCredential.id} className={cx(kcProps.kcSelectOTPListClass)}>
|
||||||
<input type="hidden" value="${otpCredential.id}" />
|
<input type="hidden" value="${otpCredential.id}" />
|
||||||
<div className={cx(props.kcSelectOTPListItemClass)}>
|
<div className={cx(kcProps.kcSelectOTPListItemClass)}>
|
||||||
<span className={cx(props.kcAuthenticatorOtpCircleClass)} />
|
<span className={cx(kcProps.kcAuthenticatorOtpCircleClass)} />
|
||||||
<h2 className={cx(props.kcSelectOTPItemHeadingClass)}>{otpCredential.userLabel}</h2>
|
<h2 className={cx(kcProps.kcSelectOTPItemHeadingClass)}>{otpCredential.userLabel}</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="otp" className={cx(props.kcLabelClass)}>
|
<label htmlFor="otp" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("loginOtpOneTime")}
|
{msg("loginOtpOneTime")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input id="otp" name="otp" autoComplete="off" type="text" className={cx(props.kcInputClass)} autoFocus />
|
<input id="otp" name="otp" autoComplete="off" type="text" className={cx(kcProps.kcInputClass)} autoFocus />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)} />
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
<input
|
<input
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
name="login"
|
name="login"
|
||||||
id="kc-login"
|
id="kc-login"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@ -1,18 +1,27 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginPageExpired = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginPageExpired; i18n: I18n } & KcProps) => {
|
export type LoginPageExpired = KcProps & {
|
||||||
|
kcContext: KcContextBase.LoginPageExpired;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginPageExpired = memo((props: LoginPageExpired) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { url } = kcContext;
|
const { url } = kcContext;
|
||||||
|
|
||||||
const { msg } = i18n;
|
const { msg } = i18n;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("pageExpiredTitle")}
|
headerNode={msg("pageExpiredTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
|
99
src/lib/components/LoginPassword.tsx
Normal file
99
src/lib/components/LoginPassword.tsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import React, { useState, memo } from "react";
|
||||||
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
|
import type { KcProps } from "./KcProps";
|
||||||
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
|
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||||
|
import type { FormEventHandler } from "react";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
|
export type LoginPasswordProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.LoginPassword;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginPassword = memo((props: LoginPasswordProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
|
const { realm, url, login } = kcContext;
|
||||||
|
|
||||||
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
|
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
|
||||||
|
|
||||||
|
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
setIsLoginButtonDisabled(true);
|
||||||
|
|
||||||
|
const formElement = e.target as HTMLFormElement;
|
||||||
|
|
||||||
|
formElement.submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Template
|
||||||
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
|
headerNode={msg("doLogIn")}
|
||||||
|
formNode={
|
||||||
|
<div id="kc-form">
|
||||||
|
<div id="kc-form-wrapper">
|
||||||
|
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
|
<hr />
|
||||||
|
<label htmlFor="password" className={cx(kcProps.kcLabelClass)}>
|
||||||
|
{msg("password")}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
tabIndex={2}
|
||||||
|
id="password"
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
autoFocus={true}
|
||||||
|
autoComplete="on"
|
||||||
|
defaultValue={login.password ?? ""}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
|
||||||
|
<div id="kc-form-options" />
|
||||||
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
|
||||||
|
{realm.resetPasswordAllowed && (
|
||||||
|
<span>
|
||||||
|
<a tabIndex={5} href={url.loginResetCredentialsUrl}>
|
||||||
|
{msg("doForgotPassword")}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormGroupClass)}>
|
||||||
|
<input
|
||||||
|
tabIndex={4}
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
name="login"
|
||||||
|
id="kc-login"
|
||||||
|
type="submit"
|
||||||
|
value={msgStr("doLogIn")}
|
||||||
|
disabled={isLoginButtonDisabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default LoginPassword;
|
@ -1,11 +1,21 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginResetPassword = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginResetPassword; i18n: I18n } & KcProps) => {
|
export type LoginResetPasswordProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.LoginResetPassword;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginResetPassword = memo((props: LoginResetPasswordProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { url, realm, auth } = kcContext;
|
const { url, realm, auth } = kcContext;
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
@ -14,15 +24,14 @@ const LoginResetPassword = memo(({ kcContext, i18n, ...props }: { kcContext: KcC
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("emailForgotTitle")}
|
headerNode={msg("emailForgotTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
<form id="kc-reset-password-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
<form id="kc-reset-password-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="username" className={cx(props.kcLabelClass)}>
|
<label htmlFor="username" className={cx(kcProps.kcLabelClass)}>
|
||||||
{!realm.loginWithEmailAllowed
|
{!realm.loginWithEmailAllowed
|
||||||
? msg("username")
|
? msg("username")
|
||||||
: !realm.registrationEmailAsUsername
|
: !realm.registrationEmailAsUsername
|
||||||
@ -30,29 +39,34 @@ const LoginResetPassword = memo(({ kcContext, i18n, ...props }: { kcContext: KcC
|
|||||||
: msg("email")}
|
: msg("email")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="username"
|
id="username"
|
||||||
name="username"
|
name="username"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
autoFocus
|
autoFocus
|
||||||
defaultValue={auth !== undefined && auth.showUsername ? auth.attemptedUsername : undefined}
|
defaultValue={auth !== undefined && auth.showUsername ? auth.attemptedUsername : undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcFormGroupClass, props.kcFormSettingClass)}>
|
<div className={cx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
|
||||||
<span>
|
<span>
|
||||||
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
<input
|
<input
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
type="submit"
|
type="submit"
|
||||||
value={msgStr("doSubmit")}
|
value={msgStr("doSubmit")}
|
||||||
/>
|
/>
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginUpdatePassword = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginUpdatePassword; i18n: I18n } & KcProps) => {
|
export type LoginUpdatePasswordProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.LoginUpdatePassword;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginUpdatePassword = memo((props: LoginUpdatePasswordProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
@ -14,11 +24,10 @@ const LoginUpdatePassword = memo(({ kcContext, i18n, ...props }: { kcContext: Kc
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
headerNode={msg("updatePasswordTitle")}
|
headerNode={msg("updatePasswordTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
<form id="kc-passwd-update-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
<form id="kc-passwd-update-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="username"
|
id="username"
|
||||||
@ -30,44 +39,44 @@ const LoginUpdatePassword = memo(({ kcContext, i18n, ...props }: { kcContext: Kc
|
|||||||
/>
|
/>
|
||||||
<input type="password" id="password" name="password" autoComplete="current-password" style={{ display: "none" }} />
|
<input type="password" id="password" name="password" autoComplete="current-password" style={{ display: "none" }} />
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("password", props.kcFormGroupErrorClass))}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("password", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="password-new" className={cx(props.kcLabelClass)}>
|
<label htmlFor="password-new" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("passwordNew")}
|
{msg("passwordNew")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="password-new"
|
id="password-new"
|
||||||
name="password-new"
|
name="password-new"
|
||||||
autoFocus
|
autoFocus
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("password-confirm", props.kcFormGroupErrorClass))}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("password-confirm", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="password-confirm" className={cx(props.kcLabelClass)}>
|
<label htmlFor="password-confirm" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("passwordConfirm")}
|
{msg("passwordConfirm")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="password-confirm"
|
id="password-confirm"
|
||||||
name="password-confirm"
|
name="password-confirm"
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
|
||||||
{isAppInitiatedAction && (
|
{isAppInitiatedAction && (
|
||||||
<div className="checkbox">
|
<div className="checkbox">
|
||||||
<label>
|
<label>
|
||||||
@ -79,16 +88,16 @@ const LoginUpdatePassword = memo(({ kcContext, i18n, ...props }: { kcContext: Kc
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
{isAppInitiatedAction ? (
|
{isAppInitiatedAction ? (
|
||||||
<>
|
<>
|
||||||
<input
|
<input
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
|
||||||
type="submit"
|
type="submit"
|
||||||
defaultValue={msgStr("doSubmit")}
|
defaultValue={msgStr("doSubmit")}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
|
||||||
type="submit"
|
type="submit"
|
||||||
name="cancel-aia"
|
name="cancel-aia"
|
||||||
value="true"
|
value="true"
|
||||||
@ -99,10 +108,10 @@ const LoginUpdatePassword = memo(({ kcContext, i18n, ...props }: { kcContext: Kc
|
|||||||
) : (
|
) : (
|
||||||
<input
|
<input
|
||||||
className={cx(
|
className={cx(
|
||||||
props.kcButtonClass,
|
kcProps.kcButtonClass,
|
||||||
props.kcButtonPrimaryClass,
|
kcProps.kcButtonPrimaryClass,
|
||||||
props.kcButtonBlockClass,
|
kcProps.kcButtonBlockClass,
|
||||||
props.kcButtonLargeClass
|
kcProps.kcButtonLargeClass
|
||||||
)}
|
)}
|
||||||
type="submit"
|
type="submit"
|
||||||
defaultValue={msgStr("doSubmit")}
|
defaultValue={msgStr("doSubmit")}
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginUpdateProfile = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginUpdateProfile; i18n: I18n } & KcProps) => {
|
export type LoginUpdateProfile = KcProps & {
|
||||||
|
kcContext: KcContextBase.LoginUpdateProfile;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginUpdateProfile = memo((props: LoginUpdateProfile) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
@ -14,84 +24,89 @@ const LoginUpdateProfile = memo(({ kcContext, i18n, ...props }: { kcContext: KcC
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
headerNode={msg("loginProfileTitle")}
|
headerNode={msg("loginProfileTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
<form id="kc-update-profile-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
<form id="kc-update-profile-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
|
||||||
{user.editUsernameAllowed && (
|
{user.editUsernameAllowed && (
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("username", props.kcFormGroupErrorClass))}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("username", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="username" className={cx(props.kcLabelClass)}>
|
<label htmlFor="username" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("username")}
|
{msg("username")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="username"
|
id="username"
|
||||||
name="username"
|
name="username"
|
||||||
defaultValue={user.username ?? ""}
|
defaultValue={user.username ?? ""}
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("email", props.kcFormGroupErrorClass))}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("email", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="email" className={cx(props.kcLabelClass)}>
|
<label htmlFor="email" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("email")}
|
{msg("email")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input type="text" id="email" name="email" defaultValue={user.email ?? ""} className={cx(props.kcInputClass)} />
|
<input type="text" id="email" name="email" defaultValue={user.email ?? ""} className={cx(kcProps.kcInputClass)} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("firstName", props.kcFormGroupErrorClass))}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("firstName", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="firstName" className={cx(props.kcLabelClass)}>
|
<label htmlFor="firstName" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("firstName")}
|
{msg("firstName")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="firstName"
|
id="firstName"
|
||||||
name="firstName"
|
name="firstName"
|
||||||
defaultValue={user.firstName ?? ""}
|
defaultValue={user.firstName ?? ""}
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("lastName", props.kcFormGroupErrorClass))}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("lastName", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="lastName" className={cx(props.kcLabelClass)}>
|
<label htmlFor="lastName" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("lastName")}
|
{msg("lastName")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input type="text" id="lastName" name="lastName" defaultValue={user.lastName ?? ""} className={cx(props.kcInputClass)} />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="lastName"
|
||||||
|
name="lastName"
|
||||||
|
defaultValue={user.lastName ?? ""}
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)} />
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
{isAppInitiatedAction ? (
|
{isAppInitiatedAction ? (
|
||||||
<>
|
<>
|
||||||
<input
|
<input
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
|
||||||
type="submit"
|
type="submit"
|
||||||
defaultValue={msgStr("doSubmit")}
|
defaultValue={msgStr("doSubmit")}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
|
||||||
type="submit"
|
type="submit"
|
||||||
name="cancel-aia"
|
name="cancel-aia"
|
||||||
value="true"
|
value="true"
|
||||||
@ -102,10 +117,10 @@ const LoginUpdateProfile = memo(({ kcContext, i18n, ...props }: { kcContext: KcC
|
|||||||
) : (
|
) : (
|
||||||
<input
|
<input
|
||||||
className={cx(
|
className={cx(
|
||||||
props.kcButtonClass,
|
kcProps.kcButtonClass,
|
||||||
props.kcButtonPrimaryClass,
|
kcProps.kcButtonPrimaryClass,
|
||||||
props.kcButtonBlockClass,
|
kcProps.kcButtonBlockClass,
|
||||||
props.kcButtonLargeClass
|
kcProps.kcButtonLargeClass
|
||||||
)}
|
)}
|
||||||
type="submit"
|
type="submit"
|
||||||
defaultValue={msgStr("doSubmit")}
|
defaultValue={msgStr("doSubmit")}
|
||||||
|
171
src/lib/components/LoginUsername.tsx
Normal file
171
src/lib/components/LoginUsername.tsx
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
import React, { useState, memo } from "react";
|
||||||
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
|
import type { KcProps } from "./KcProps";
|
||||||
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
|
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||||
|
import type { FormEventHandler } from "react";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
|
export type LoginUsernameProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.LoginUsername;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginUsername = memo((props: LoginUsernameProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
|
const { social, realm, url, usernameHidden, login, registrationDisabled } = kcContext;
|
||||||
|
|
||||||
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
|
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
|
||||||
|
|
||||||
|
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
setIsLoginButtonDisabled(true);
|
||||||
|
|
||||||
|
const formElement = e.target as HTMLFormElement;
|
||||||
|
|
||||||
|
//NOTE: Even if we login with email Keycloak expect username and password in
|
||||||
|
//the POST request.
|
||||||
|
formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
|
||||||
|
|
||||||
|
formElement.submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Template
|
||||||
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
|
displayInfo={social.displayInfo}
|
||||||
|
displayWide={realm.password && social.providers !== undefined}
|
||||||
|
headerNode={msg("doLogIn")}
|
||||||
|
formNode={
|
||||||
|
<div id="kc-form" className={cx(realm.password && social.providers !== undefined && kcProps.kcContentWrapperClass)}>
|
||||||
|
<div
|
||||||
|
id="kc-form-wrapper"
|
||||||
|
className={cx(
|
||||||
|
realm.password && social.providers && [kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass]
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{realm.password && (
|
||||||
|
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
|
{!usernameHidden &&
|
||||||
|
(() => {
|
||||||
|
const label = !realm.loginWithEmailAllowed
|
||||||
|
? "username"
|
||||||
|
: realm.registrationEmailAsUsername
|
||||||
|
? "email"
|
||||||
|
: "usernameOrEmail";
|
||||||
|
|
||||||
|
const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<label htmlFor={autoCompleteHelper} className={cx(kcProps.kcLabelClass)}>
|
||||||
|
{msg(label)}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
tabIndex={1}
|
||||||
|
id={autoCompleteHelper}
|
||||||
|
className={cx(kcProps.kcInputClass)}
|
||||||
|
//NOTE: This is used by Google Chrome auto fill so we use it to tell
|
||||||
|
//the browser how to pre fill the form but before submit we put it back
|
||||||
|
//to username because it is what keycloak expects.
|
||||||
|
name={autoCompleteHelper}
|
||||||
|
defaultValue={login.username ?? ""}
|
||||||
|
type="text"
|
||||||
|
autoFocus={true}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
|
||||||
|
<div id="kc-form-options">
|
||||||
|
{realm.rememberMe && !usernameHidden && (
|
||||||
|
<div className="checkbox">
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
tabIndex={3}
|
||||||
|
id="rememberMe"
|
||||||
|
name="rememberMe"
|
||||||
|
type="checkbox"
|
||||||
|
{...(login.rememberMe
|
||||||
|
? {
|
||||||
|
"checked": true
|
||||||
|
}
|
||||||
|
: {})}
|
||||||
|
/>
|
||||||
|
{msg("rememberMe")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormGroupClass)}>
|
||||||
|
<input
|
||||||
|
tabIndex={4}
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
name="login"
|
||||||
|
id="kc-login"
|
||||||
|
type="submit"
|
||||||
|
value={msgStr("doLogIn")}
|
||||||
|
disabled={isLoginButtonDisabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{realm.password && social.providers !== undefined && (
|
||||||
|
<div id="kc-social-providers" className={cx(kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass)}>
|
||||||
|
<ul
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcFormSocialAccountListClass,
|
||||||
|
social.providers.length > 4 && kcProps.kcFormSocialAccountDoubleListClass
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{social.providers.map(p => (
|
||||||
|
<li key={p.providerId} className={cx(kcProps.kcFormSocialAccountListLinkClass)}>
|
||||||
|
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
|
||||||
|
<span>{p.displayName}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
infoNode={
|
||||||
|
realm.password &&
|
||||||
|
realm.registrationAllowed &&
|
||||||
|
!registrationDisabled && (
|
||||||
|
<div id="kc-registration">
|
||||||
|
<span>
|
||||||
|
{msg("noAccount")}
|
||||||
|
<a tabIndex={6} href={url.registrationUrl}>
|
||||||
|
{msg("doRegister")}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default LoginUsername;
|
@ -1,18 +1,27 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginVerifyEmail = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginVerifyEmail; i18n: I18n } & KcProps) => {
|
export type LoginVerifyEmailProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.LoginVerifyEmail;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginVerifyEmail = memo((props: LoginVerifyEmailProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { msg } = i18n;
|
const { msg } = i18n;
|
||||||
|
|
||||||
const { url, user } = kcContext;
|
const { url, user } = kcContext;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("emailVerifyTitle")}
|
headerNode={msg("emailVerifyTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LogoutConfirm = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LogoutConfirm; i18n: I18n } & KcProps) => {
|
export type LogoutConfirmProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.LogoutConfirm;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LogoutConfirm = memo((props: LogoutConfirmProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { url, client, logoutConfirm } = kcContext;
|
const { url, client, logoutConfirm } = kcContext;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
@ -14,8 +24,7 @@ const LogoutConfirm = memo(({ kcContext, i18n, ...props }: { kcContext: KcContex
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("logoutConfirmTitle")}
|
headerNode={msg("logoutConfirmTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
@ -24,18 +33,18 @@ const LogoutConfirm = memo(({ kcContext, i18n, ...props }: { kcContext: KcContex
|
|||||||
<p className="instruction">{msg("logoutConfirmHeader")}</p>
|
<p className="instruction">{msg("logoutConfirmHeader")}</p>
|
||||||
<form className="form-actions" action={url.logoutConfirmAction} method="POST">
|
<form className="form-actions" action={url.logoutConfirmAction} method="POST">
|
||||||
<input type="hidden" name="session_code" value={logoutConfirm.code} />
|
<input type="hidden" name="session_code" value={logoutConfirm.code} />
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div id="kc-form-options">
|
<div id="kc-form-options">
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}></div>
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormGroupClass)}>
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<input
|
<input
|
||||||
tabIndex={4}
|
tabIndex={4}
|
||||||
className={cx(
|
className={cx(
|
||||||
props.kcButtonClass,
|
kcProps.kcButtonClass,
|
||||||
props.kcButtonPrimaryClass,
|
kcProps.kcButtonPrimaryClass,
|
||||||
props.kcButtonBlockClass,
|
kcProps.kcButtonBlockClass,
|
||||||
props.kcButtonLargeClass
|
kcProps.kcButtonLargeClass
|
||||||
)}
|
)}
|
||||||
name="confirmLogout"
|
name="confirmLogout"
|
||||||
id="kc-logout"
|
id="kc-logout"
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Register = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Register; i18n: I18n } & KcProps) => {
|
export type RegisterProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.Register;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Register = memo((props: RegisterProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
@ -14,56 +24,55 @@ const Register = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
headerNode={msg("registerTitle")}
|
headerNode={msg("registerTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
|
<form id="kc-register-form" className={cx(kcProps.kcFormClass)} action={url.registrationAction} method="post">
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("firstName", props.kcFormGroupErrorClass))}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("firstName", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="firstName" className={cx(props.kcLabelClass)}>
|
<label htmlFor="firstName" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("firstName")}
|
{msg("firstName")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="firstName"
|
id="firstName"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
name="firstName"
|
name="firstName"
|
||||||
defaultValue={register.formData.firstName ?? ""}
|
defaultValue={register.formData.firstName ?? ""}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("lastName", props.kcFormGroupErrorClass))}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("lastName", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="lastName" className={cx(props.kcLabelClass)}>
|
<label htmlFor="lastName" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("lastName")}
|
{msg("lastName")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="lastName"
|
id="lastName"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
name="lastName"
|
name="lastName"
|
||||||
defaultValue={register.formData.lastName ?? ""}
|
defaultValue={register.formData.lastName ?? ""}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("email", props.kcFormGroupErrorClass))}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("email", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="email" className={cx(props.kcLabelClass)}>
|
<label htmlFor="email" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("email")}
|
{msg("email")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="email"
|
id="email"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
name="email"
|
name="email"
|
||||||
defaultValue={register.formData.email ?? ""}
|
defaultValue={register.formData.email ?? ""}
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
@ -71,17 +80,17 @@ const Register = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!realm.registrationEmailAsUsername && (
|
{!realm.registrationEmailAsUsername && (
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("username", props.kcFormGroupErrorClass))}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("username", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="username" className={cx(props.kcLabelClass)}>
|
<label htmlFor="username" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("username")}
|
{msg("username")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="username"
|
id="username"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
name="username"
|
name="username"
|
||||||
defaultValue={register.formData.username ?? ""}
|
defaultValue={register.formData.username ?? ""}
|
||||||
autoComplete="username"
|
autoComplete="username"
|
||||||
@ -91,17 +100,17 @@ const Register = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase
|
|||||||
)}
|
)}
|
||||||
{passwordRequired && (
|
{passwordRequired && (
|
||||||
<>
|
<>
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("password", props.kcFormGroupErrorClass))}>
|
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("password", kcProps.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="password" className={cx(props.kcLabelClass)}>
|
<label htmlFor="password" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("password")}
|
{msg("password")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="password"
|
id="password"
|
||||||
className={cx(props.kcInputClass)}
|
className={cx(kcProps.kcInputClass)}
|
||||||
name="password"
|
name="password"
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
/>
|
/>
|
||||||
@ -110,40 +119,45 @@ const Register = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx(
|
||||||
props.kcFormGroupClass,
|
kcProps.kcFormGroupClass,
|
||||||
messagesPerField.printIfExists("password-confirm", props.kcFormGroupErrorClass)
|
messagesPerField.printIfExists("password-confirm", kcProps.kcFormGroupErrorClass)
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(kcProps.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="password-confirm" className={cx(props.kcLabelClass)}>
|
<label htmlFor="password-confirm" className={cx(kcProps.kcLabelClass)}>
|
||||||
{msg("passwordConfirm")}
|
{msg("passwordConfirm")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<input type="password" id="password-confirm" className={cx(props.kcInputClass)} name="password-confirm" />
|
<input type="password" id="password-confirm" className={cx(kcProps.kcInputClass)} name="password-confirm" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{recaptchaRequired && (
|
{recaptchaRequired && (
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey}></div>
|
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey}></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
|
||||||
<span>
|
<span>
|
||||||
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
<input
|
<input
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
type="submit"
|
type="submit"
|
||||||
value={msgStr("doRegister")}
|
value={msgStr("doRegister")}
|
||||||
/>
|
/>
|
||||||
|
@ -1,24 +1,32 @@
|
|||||||
import React, { useMemo, memo, useEffect, useState, Fragment } from "react";
|
import React, { useMemo, memo, useState } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase, Attribute } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
import type { ReactComponent } from "../tools/ReactComponent";
|
|
||||||
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
|
|
||||||
import { useFormValidationSlice } from "../useFormValidationSlice";
|
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
import { UserProfileFormFields } from "./shared/UserProfileCommons";
|
||||||
|
|
||||||
|
export type RegisterUserProfileProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.RegisterUserProfile;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RegisterUserProfile = memo((props: RegisterUserProfileProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps_ } = props;
|
||||||
|
|
||||||
const RegisterUserProfile = memo(({ kcContext, i18n, ...props_ }: { kcContext: KcContextBase.RegisterUserProfile; i18n: I18n } & KcProps) => {
|
|
||||||
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const { cx, css } = useCssAndCx();
|
const { cx, css } = useCssAndCx();
|
||||||
|
|
||||||
const props = useMemo(
|
const kcProps = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...props_,
|
...kcProps_,
|
||||||
"kcFormGroupClass": cx(props_.kcFormGroupClass, css({ "marginBottom": 20 }))
|
"kcFormGroupClass": cx(kcProps_.kcFormGroupClass, css({ "marginBottom": 20 }))
|
||||||
}),
|
}),
|
||||||
[cx, css]
|
[cx, css]
|
||||||
);
|
);
|
||||||
@ -27,33 +35,37 @@ const RegisterUserProfile = memo(({ kcContext, i18n, ...props_ }: { kcContext: K
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
displayMessage={messagesPerField.exists("global")}
|
displayMessage={messagesPerField.exists("global")}
|
||||||
displayRequiredFields={true}
|
displayRequiredFields={true}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
headerNode={msg("registerTitle")}
|
headerNode={msg("registerTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
|
<form id="kc-register-form" className={cx(kcProps.kcFormClass)} action={url.registrationAction} method="post">
|
||||||
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...props} />
|
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...kcProps} />
|
||||||
{recaptchaRequired && (
|
{recaptchaRequired && (
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(kcProps.kcInputWrapperClass)}>
|
||||||
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} />
|
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={cx(props.kcFormGroupClass)}>
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
|
||||||
<span>
|
<span>
|
||||||
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
<input
|
<input
|
||||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
type="submit"
|
type="submit"
|
||||||
value={msgStr("doRegister")}
|
value={msgStr("doRegister")}
|
||||||
disabled={!isFomSubmittable}
|
disabled={!isFomSubmittable}
|
||||||
@ -66,155 +78,4 @@ const RegisterUserProfile = memo(({ kcContext, i18n, ...props_ }: { kcContext: K
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
type UserProfileFormFieldsProps = { kcContext: KcContextBase.RegisterUserProfile; i18n: I18n } & KcProps &
|
|
||||||
Partial<Record<"BeforeField" | "AfterField", ReactComponent<{ attribute: Attribute }>>> & {
|
|
||||||
onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange, i18n, ...props }: UserProfileFormFieldsProps) => {
|
|
||||||
const { cx, css } = useCssAndCx();
|
|
||||||
|
|
||||||
const { advancedMsg } = i18n;
|
|
||||||
|
|
||||||
const {
|
|
||||||
formValidationState: { fieldStateByAttributeName, isFormSubmittable },
|
|
||||||
formValidationReducer,
|
|
||||||
attributesWithPassword
|
|
||||||
} = useFormValidationSlice({
|
|
||||||
kcContext,
|
|
||||||
i18n
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onIsFormSubmittableValueChange(isFormSubmittable);
|
|
||||||
}, [isFormSubmittable]);
|
|
||||||
|
|
||||||
const onChangeFactory = useCallbackFactory(
|
|
||||||
(
|
|
||||||
[name]: [string],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
target: { value }
|
|
||||||
}
|
|
||||||
]: [React.ChangeEvent<HTMLInputElement | HTMLSelectElement>]
|
|
||||||
) =>
|
|
||||||
formValidationReducer({
|
|
||||||
"action": "update value",
|
|
||||||
name,
|
|
||||||
"newValue": value
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const onBlurFactory = useCallbackFactory(([name]: [string]) =>
|
|
||||||
formValidationReducer({
|
|
||||||
"action": "focus lost",
|
|
||||||
name
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
let currentGroup = "";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{attributesWithPassword.map((attribute, i) => {
|
|
||||||
const { group = "", groupDisplayHeader = "", groupDisplayDescription = "" } = attribute;
|
|
||||||
|
|
||||||
const { value, displayableErrors } = fieldStateByAttributeName[attribute.name];
|
|
||||||
|
|
||||||
const formGroupClassName = cx(props.kcFormGroupClass, displayableErrors.length !== 0 && props.kcFormGroupErrorClass);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Fragment key={i}>
|
|
||||||
{group !== currentGroup && (currentGroup = group) !== "" && (
|
|
||||||
<div className={formGroupClassName}>
|
|
||||||
<div className={cx(props.kcContentWrapperClass)}>
|
|
||||||
<label id={`header-${group}`} className={cx(props.kcFormGroupHeader)}>
|
|
||||||
{advancedMsg(groupDisplayHeader) || currentGroup}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{groupDisplayDescription !== "" && (
|
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
|
||||||
<label id={`description-${group}`} className={`${cx(props.kcLabelClass)}`}>
|
|
||||||
{advancedMsg(groupDisplayDescription)}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className={formGroupClassName}>
|
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
|
||||||
<label htmlFor={attribute.name} className={cx(props.kcLabelClass)}>
|
|
||||||
{advancedMsg(attribute.displayName ?? "")}
|
|
||||||
</label>
|
|
||||||
{attribute.required && <>*</>}
|
|
||||||
</div>
|
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
|
||||||
{(() => {
|
|
||||||
const { options } = attribute.validators;
|
|
||||||
|
|
||||||
if (options !== undefined) {
|
|
||||||
return (
|
|
||||||
<select
|
|
||||||
id={attribute.name}
|
|
||||||
name={attribute.name}
|
|
||||||
onChange={onChangeFactory(attribute.name)}
|
|
||||||
onBlur={onBlurFactory(attribute.name)}
|
|
||||||
value={value}
|
|
||||||
>
|
|
||||||
{options.options.map(option => (
|
|
||||||
<option key={option} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<input
|
|
||||||
type={(() => {
|
|
||||||
switch (attribute.name) {
|
|
||||||
case "password-confirm":
|
|
||||||
case "password":
|
|
||||||
return "password";
|
|
||||||
default:
|
|
||||||
return "text";
|
|
||||||
}
|
|
||||||
})()}
|
|
||||||
id={attribute.name}
|
|
||||||
name={attribute.name}
|
|
||||||
value={value}
|
|
||||||
onChange={onChangeFactory(attribute.name)}
|
|
||||||
className={cx(props.kcInputClass)}
|
|
||||||
aria-invalid={displayableErrors.length !== 0}
|
|
||||||
disabled={attribute.readOnly}
|
|
||||||
autoComplete={attribute.autocomplete}
|
|
||||||
onBlur={onBlurFactory(attribute.name)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
{displayableErrors.length !== 0 && (
|
|
||||||
<span
|
|
||||||
id={`input-error-${attribute.name}`}
|
|
||||||
className={cx(
|
|
||||||
props.kcInputErrorMessageClass,
|
|
||||||
css({
|
|
||||||
"position": displayableErrors.length === 1 ? "absolute" : undefined,
|
|
||||||
"& > span": { "display": "block" }
|
|
||||||
})
|
|
||||||
)}
|
|
||||||
aria-live="polite"
|
|
||||||
>
|
|
||||||
{displayableErrors.map(({ errorMessage }) => errorMessage)}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default RegisterUserProfile;
|
export default RegisterUserProfile;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, memo } from "react";
|
import React, { useEffect, memo } from "react";
|
||||||
import Template from "./Template";
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
@ -11,6 +12,8 @@ import type { I18n } from "../i18n";
|
|||||||
import memoize from "memoizee";
|
import memoize from "memoizee";
|
||||||
import { useConst } from "powerhooks/useConst";
|
import { useConst } from "powerhooks/useConst";
|
||||||
import { useConstCallback } from "powerhooks/useConstCallback";
|
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||||
|
import { Markdown } from "../tools/Markdown";
|
||||||
|
import type { Extends } from "tsafe";
|
||||||
|
|
||||||
export const evtTermMarkdown = Evt.create<string | undefined>(undefined);
|
export const evtTermMarkdown = Evt.create<string | undefined>(undefined);
|
||||||
|
|
||||||
@ -21,7 +24,7 @@ export type KcContextLike = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
assert<KcContextBase extends KcContextLike ? true : false>();
|
assert<Extends<KcContextBase, KcContextLike>>();
|
||||||
|
|
||||||
/** Allow to avoid bundling the terms and download it on demand*/
|
/** Allow to avoid bundling the terms and download it on demand*/
|
||||||
export function useDownloadTerms(params: {
|
export function useDownloadTerms(params: {
|
||||||
@ -53,7 +56,16 @@ export function useDownloadTerms(params: {
|
|||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Terms = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Terms; i18n: I18n } & KcProps) => {
|
export type TermsProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.Terms;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Terms = memo((props: TermsProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
useRerenderOnStateChange(evtTermMarkdown);
|
useRerenderOnStateChange(evtTermMarkdown);
|
||||||
@ -68,21 +80,20 @@ const Terms = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Te
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, i18n, ...props }}
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
doFetchDefaultThemeResources={true}
|
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("termsTitle")}
|
headerNode={msg("termsTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
<>
|
<>
|
||||||
<div id="kc-terms-text">{evtTermMarkdown.state}</div>
|
<div id="kc-terms-text">{evtTermMarkdown.state && <Markdown>{evtTermMarkdown.state}</Markdown>}</div>
|
||||||
<form className="form-actions" action={url.loginAction} method="POST">
|
<form className="form-actions" action={url.loginAction} method="POST">
|
||||||
<input
|
<input
|
||||||
className={cx(
|
className={cx(
|
||||||
props.kcButtonClass,
|
kcProps.kcButtonClass,
|
||||||
props.kcButtonClass,
|
kcProps.kcButtonClass,
|
||||||
props.kcButtonClass,
|
kcProps.kcButtonClass,
|
||||||
props.kcButtonPrimaryClass,
|
kcProps.kcButtonPrimaryClass,
|
||||||
props.kcButtonLargeClass
|
kcProps.kcButtonLargeClass
|
||||||
)}
|
)}
|
||||||
name="accept"
|
name="accept"
|
||||||
id="kc-accept"
|
id="kc-accept"
|
||||||
@ -90,7 +101,7 @@ const Terms = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Te
|
|||||||
value={msgStr("doAccept")}
|
value={msgStr("doAccept")}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
|
||||||
name="cancel"
|
name="cancel"
|
||||||
id="kc-decline"
|
id="kc-decline"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
80
src/lib/components/UpdateUserProfile.tsx
Normal file
80
src/lib/components/UpdateUserProfile.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import React, { useState, memo } from "react";
|
||||||
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
|
import type { KcProps } from "./KcProps";
|
||||||
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
import { UserProfileFormFields } from "./shared/UserProfileCommons";
|
||||||
|
|
||||||
|
export type UpdateUserProfileProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.UpdateUserProfile;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const UpdateUserProfile = memo((props: UpdateUserProfileProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
|
const { url, isAppInitiatedAction } = kcContext;
|
||||||
|
|
||||||
|
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Template
|
||||||
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
|
headerNode={msg("loginProfileTitle")}
|
||||||
|
formNode={
|
||||||
|
<form id="kc-update-profile-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
|
||||||
|
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...kcProps} />
|
||||||
|
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
|
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
|
||||||
|
<div className={cx(kcProps.kcFormOptionsWrapperClass)}></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
|
{isAppInitiatedAction ? (
|
||||||
|
<>
|
||||||
|
<input
|
||||||
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
|
||||||
|
type="submit"
|
||||||
|
value={msgStr("doSubmit")}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className={cx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
|
||||||
|
type="submit"
|
||||||
|
name="cancel-aia"
|
||||||
|
value="true"
|
||||||
|
formNoValidate
|
||||||
|
>
|
||||||
|
{msg("doCancel")}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
type="submit"
|
||||||
|
defaultValue={msgStr("doSubmit")}
|
||||||
|
disabled={!isFomSubmittable}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default UpdateUserProfile;
|
205
src/lib/components/WebauthnAuthenticate.tsx
Normal file
205
src/lib/components/WebauthnAuthenticate.tsx
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
import React, { useRef, useState, memo } from "react";
|
||||||
|
import DefaultTemplate from "./Template";
|
||||||
|
import type { TemplateProps } from "./Template";
|
||||||
|
import type { KcProps } from "./KcProps";
|
||||||
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||||
|
import type { I18n, MessageKeyBase } from "../i18n";
|
||||||
|
import { base64url } from "rfc4648";
|
||||||
|
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||||
|
|
||||||
|
export type WebauthnAuthenticateProps = KcProps & {
|
||||||
|
kcContext: KcContextBase.WebauthnAuthenticate;
|
||||||
|
i18n: I18n;
|
||||||
|
doFetchDefaultThemeResources?: boolean;
|
||||||
|
Template?: (props: TemplateProps) => JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const WebauthnAuthenticate = memo((props: WebauthnAuthenticateProps) => {
|
||||||
|
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
|
||||||
|
|
||||||
|
const { url } = kcContext;
|
||||||
|
|
||||||
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
|
const { authenticators, challenge, shouldDisplayAuthenticators, userVerification, rpId } = kcContext;
|
||||||
|
const createTimeout = Number(kcContext.createTimeout);
|
||||||
|
const isUserIdentified = kcContext.isUserIdentified == "true";
|
||||||
|
|
||||||
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
|
const webAuthnAuthenticate = useConstCallback(async () => {
|
||||||
|
if (!isUserIdentified) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const allowCredentials = authenticators.authenticators.map(
|
||||||
|
authenticator =>
|
||||||
|
({
|
||||||
|
id: base64url.parse(authenticator.credentialId, { loose: true }),
|
||||||
|
type: "public-key"
|
||||||
|
} as PublicKeyCredentialDescriptor)
|
||||||
|
);
|
||||||
|
// Check if WebAuthn is supported by this browser
|
||||||
|
if (!window.PublicKeyCredential) {
|
||||||
|
setError(msgStr("webauthn-unsupported-browser-text"));
|
||||||
|
submitForm();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const publicKey: PublicKeyCredentialRequestOptions = {
|
||||||
|
rpId,
|
||||||
|
challenge: base64url.parse(challenge, { loose: true })
|
||||||
|
};
|
||||||
|
|
||||||
|
if (createTimeout !== 0) {
|
||||||
|
publicKey.timeout = createTimeout * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowCredentials.length) {
|
||||||
|
publicKey.allowCredentials = allowCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userVerification !== "not specified") {
|
||||||
|
publicKey.userVerification = userVerification;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resultRaw = await navigator.credentials.get({ publicKey });
|
||||||
|
if (!resultRaw || resultRaw.type != "public-key") return;
|
||||||
|
const result = resultRaw as PublicKeyCredential;
|
||||||
|
if (!("authenticatorData" in result.response)) return;
|
||||||
|
const response = result.response as AuthenticatorAssertionResponse;
|
||||||
|
const clientDataJSON = response.clientDataJSON;
|
||||||
|
const authenticatorData = response.authenticatorData;
|
||||||
|
const signature = response.signature;
|
||||||
|
|
||||||
|
setClientDataJSON(base64url.stringify(new Uint8Array(clientDataJSON), { pad: false }));
|
||||||
|
setAuthenticatorData(base64url.stringify(new Uint8Array(authenticatorData), { pad: false }));
|
||||||
|
setSignature(base64url.stringify(new Uint8Array(signature), { pad: false }));
|
||||||
|
setCredentialId(result.id);
|
||||||
|
setUserHandle(base64url.stringify(new Uint8Array(response.userHandle!), { pad: false }));
|
||||||
|
submitForm();
|
||||||
|
} catch (err) {
|
||||||
|
setError(String(err));
|
||||||
|
submitForm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const webAuthForm = useRef<HTMLFormElement>(null);
|
||||||
|
const submitForm = useConstCallback(() => {
|
||||||
|
webAuthForm.current!.submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
const [clientDataJSON, setClientDataJSON] = useState("");
|
||||||
|
const [authenticatorData, setAuthenticatorData] = useState("");
|
||||||
|
const [signature, setSignature] = useState("");
|
||||||
|
const [credentialId, setCredentialId] = useState("");
|
||||||
|
const [userHandle, setUserHandle] = useState("");
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Template
|
||||||
|
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
|
||||||
|
headerNode={msg("webauthn-login-title")}
|
||||||
|
formNode={
|
||||||
|
<div id="kc-form-webauthn" className={cx(kcProps.kcFormClass)}>
|
||||||
|
<form id="webauth" action={url.loginAction} ref={webAuthForm} method="post">
|
||||||
|
<input type="hidden" id="clientDataJSON" name="clientDataJSON" value={clientDataJSON} />
|
||||||
|
<input type="hidden" id="authenticatorData" name="authenticatorData" value={authenticatorData} />
|
||||||
|
<input type="hidden" id="signature" name="signature" value={signature} />
|
||||||
|
<input type="hidden" id="credentialId" name="credentialId" value={credentialId} />
|
||||||
|
<input type="hidden" id="userHandle" name="userHandle" value={userHandle} />
|
||||||
|
<input type="hidden" id="error" name="error" value={error} />
|
||||||
|
</form>
|
||||||
|
<div className={cx(kcProps.kcFormGroupClass)}>
|
||||||
|
{authenticators &&
|
||||||
|
(() => (
|
||||||
|
<form id="authn_select" className={cx(kcProps.kcFormClass)}>
|
||||||
|
{authenticators.authenticators.map(authenticator => (
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="authn_use_chk"
|
||||||
|
value={authenticator.credentialId}
|
||||||
|
key={authenticator.credentialId}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</form>
|
||||||
|
))()}
|
||||||
|
{authenticators &&
|
||||||
|
shouldDisplayAuthenticators &&
|
||||||
|
(() => (
|
||||||
|
<>
|
||||||
|
{authenticators.authenticators.length > 1 && (
|
||||||
|
<p className={cx(kcProps.kcSelectAuthListItemTitle)}>{msg("webauthn-available-authenticators")}</p>
|
||||||
|
)}
|
||||||
|
<div className={cx(kcProps.kcFormClass)}>
|
||||||
|
{authenticators.authenticators.map(authenticator => (
|
||||||
|
<div id="kc-webauthn-authenticator" className={cx(kcProps.kcSelectAuthListItemClass)}>
|
||||||
|
<div className={cx(kcProps.kcSelectAuthListItemIconClass)}>
|
||||||
|
<i
|
||||||
|
className={cx(
|
||||||
|
kcProps[authenticator.transports.iconClass] ?? kcProps.kcWebAuthnDefaultIcon,
|
||||||
|
kcProps.kcSelectAuthListItemIconPropertyClass
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcSelectAuthListItemBodyClass)}>
|
||||||
|
<div
|
||||||
|
id="kc-webauthn-authenticator-label"
|
||||||
|
className={cx(kcProps.kcSelectAuthListItemHeadingClass)}
|
||||||
|
>
|
||||||
|
{authenticator.label}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{authenticator.transports && authenticator.transports.displayNameProperties.length && (
|
||||||
|
<div
|
||||||
|
id="kc-webauthn-authenticator-transport"
|
||||||
|
className={cx(kcProps.kcSelectAuthListItemDescriptionClass)}
|
||||||
|
>
|
||||||
|
{authenticator.transports.displayNameProperties.map(
|
||||||
|
(transport: MessageKeyBase, index: number) => (
|
||||||
|
<>
|
||||||
|
<span>{msg(transport)}</span>
|
||||||
|
{index < authenticator.transports.displayNameProperties.length - 1 && (
|
||||||
|
<span>{", "}</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className={cx(kcProps.kcSelectAuthListItemDescriptionClass)}>
|
||||||
|
<span id="kc-webauthn-authenticator-created-label">{msg("webauthn-createdAt-label")}</span>
|
||||||
|
<span id="kc-webauthn-authenticator-created">{authenticator.createdAt}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={cx(kcProps.kcSelectAuthListItemFillClass)} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
))()}
|
||||||
|
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
|
||||||
|
<input
|
||||||
|
id="authenticateWebAuthnButton"
|
||||||
|
type="button"
|
||||||
|
onClick={webAuthnAuthenticate}
|
||||||
|
autoFocus={true}
|
||||||
|
value={msgStr("webauthn-doAuthenticate")}
|
||||||
|
className={cx(
|
||||||
|
kcProps.kcButtonClass,
|
||||||
|
kcProps.kcButtonPrimaryClass,
|
||||||
|
kcProps.kcButtonBlockClass,
|
||||||
|
kcProps.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default WebauthnAuthenticate;
|
170
src/lib/components/shared/UserProfileCommons.tsx
Normal file
170
src/lib/components/shared/UserProfileCommons.tsx
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
import React, { memo, useEffect, Fragment } from "react";
|
||||||
|
import type { KcProps } from "../KcProps";
|
||||||
|
import type { Attribute } from "../../getKcContext/KcContextBase";
|
||||||
|
import { useCssAndCx } from "../../tools/useCssAndCx";
|
||||||
|
import type { ReactComponent } from "../../tools/ReactComponent";
|
||||||
|
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
|
||||||
|
import { useFormValidationSlice } from "../../useFormValidationSlice";
|
||||||
|
import type { I18n } from "../../i18n";
|
||||||
|
import type { Param0 } from "tsafe/Param0";
|
||||||
|
|
||||||
|
export type UserProfileFormFieldsProps = {
|
||||||
|
kcContext: Param0<typeof useFormValidationSlice>["kcContext"];
|
||||||
|
i18n: I18n;
|
||||||
|
} & KcProps &
|
||||||
|
Partial<Record<"BeforeField" | "AfterField", ReactComponent<{ attribute: Attribute }>>> & {
|
||||||
|
onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UserProfileFormFields = memo(
|
||||||
|
({ kcContext, onIsFormSubmittableValueChange, i18n, BeforeField, AfterField, ...props }: UserProfileFormFieldsProps) => {
|
||||||
|
const { cx, css } = useCssAndCx();
|
||||||
|
|
||||||
|
const { advancedMsg } = i18n;
|
||||||
|
|
||||||
|
const {
|
||||||
|
formValidationState: { fieldStateByAttributeName, isFormSubmittable },
|
||||||
|
formValidationReducer,
|
||||||
|
attributesWithPassword
|
||||||
|
} = useFormValidationSlice({
|
||||||
|
kcContext,
|
||||||
|
i18n
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onIsFormSubmittableValueChange(isFormSubmittable);
|
||||||
|
}, [isFormSubmittable]);
|
||||||
|
|
||||||
|
const onChangeFactory = useCallbackFactory(
|
||||||
|
(
|
||||||
|
[name]: [string],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
target: { value }
|
||||||
|
}
|
||||||
|
]: [React.ChangeEvent<HTMLInputElement | HTMLSelectElement>]
|
||||||
|
) =>
|
||||||
|
formValidationReducer({
|
||||||
|
"action": "update value",
|
||||||
|
name,
|
||||||
|
"newValue": value
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const onBlurFactory = useCallbackFactory(([name]: [string]) =>
|
||||||
|
formValidationReducer({
|
||||||
|
"action": "focus lost",
|
||||||
|
name
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let currentGroup = "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{attributesWithPassword.map((attribute, i) => {
|
||||||
|
const { group = "", groupDisplayHeader = "", groupDisplayDescription = "" } = attribute;
|
||||||
|
|
||||||
|
const { value, displayableErrors } = fieldStateByAttributeName[attribute.name];
|
||||||
|
|
||||||
|
const formGroupClassName = cx(props.kcFormGroupClass, displayableErrors.length !== 0 && props.kcFormGroupErrorClass);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment key={i}>
|
||||||
|
{group !== currentGroup && (currentGroup = group) !== "" && (
|
||||||
|
<div className={formGroupClassName}>
|
||||||
|
<div className={cx(props.kcContentWrapperClass)}>
|
||||||
|
<label id={`header-${group}`} className={cx(props.kcFormGroupHeader)}>
|
||||||
|
{advancedMsg(groupDisplayHeader) || currentGroup}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{groupDisplayDescription !== "" && (
|
||||||
|
<div className={cx(props.kcLabelWrapperClass)}>
|
||||||
|
<label id={`description-${group}`} className={`${cx(props.kcLabelClass)}`}>
|
||||||
|
{advancedMsg(groupDisplayDescription)}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{BeforeField && <BeforeField attribute={attribute} />}
|
||||||
|
|
||||||
|
<div className={formGroupClassName}>
|
||||||
|
<div className={cx(props.kcLabelWrapperClass)}>
|
||||||
|
<label htmlFor={attribute.name} className={cx(props.kcLabelClass)}>
|
||||||
|
{advancedMsg(attribute.displayName ?? "")}
|
||||||
|
</label>
|
||||||
|
{attribute.required && <>*</>}
|
||||||
|
</div>
|
||||||
|
<div className={cx(props.kcInputWrapperClass)}>
|
||||||
|
{(() => {
|
||||||
|
const { options } = attribute.validators;
|
||||||
|
|
||||||
|
if (options !== undefined) {
|
||||||
|
return (
|
||||||
|
<select
|
||||||
|
id={attribute.name}
|
||||||
|
name={attribute.name}
|
||||||
|
onChange={onChangeFactory(attribute.name)}
|
||||||
|
onBlur={onBlurFactory(attribute.name)}
|
||||||
|
value={value}
|
||||||
|
>
|
||||||
|
{options.options.map(option => (
|
||||||
|
<option key={option} value={option}>
|
||||||
|
{option}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={(() => {
|
||||||
|
switch (attribute.name) {
|
||||||
|
case "password-confirm":
|
||||||
|
case "password":
|
||||||
|
return "password";
|
||||||
|
default:
|
||||||
|
return "text";
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
id={attribute.name}
|
||||||
|
name={attribute.name}
|
||||||
|
value={value}
|
||||||
|
onChange={onChangeFactory(attribute.name)}
|
||||||
|
className={cx(props.kcInputClass)}
|
||||||
|
aria-invalid={displayableErrors.length !== 0}
|
||||||
|
disabled={attribute.readOnly}
|
||||||
|
autoComplete={attribute.autocomplete}
|
||||||
|
onBlur={onBlurFactory(attribute.name)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
{displayableErrors.length !== 0 && (
|
||||||
|
<span
|
||||||
|
id={`input-error-${attribute.name}`}
|
||||||
|
className={cx(
|
||||||
|
props.kcInputErrorMessageClass,
|
||||||
|
css({
|
||||||
|
"position": displayableErrors.length === 1 ? "absolute" : undefined,
|
||||||
|
"& > span": { "display": "block" }
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
|
{displayableErrors.map(({ errorMessage }) => errorMessage)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{AfterField && <AfterField attribute={attribute} />}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
@ -2,6 +2,7 @@ import type { PageId } from "../../bin/keycloakify/generateFtl";
|
|||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { Equals } from "tsafe";
|
import type { Equals } from "tsafe";
|
||||||
import type { MessageKeyBase } from "../i18n";
|
import type { MessageKeyBase } from "../i18n";
|
||||||
|
import type { KcTemplateClassKey } from "../components/KcProps";
|
||||||
|
|
||||||
type ExtractAfterStartingWith<Prefix extends string, StrEnum> = StrEnum extends `${Prefix}${infer U}` ? U : never;
|
type ExtractAfterStartingWith<Prefix extends string, StrEnum> = StrEnum extends `${Prefix}${infer U}` ? U : never;
|
||||||
|
|
||||||
@ -19,13 +20,28 @@ export type KcContextBase =
|
|||||||
| KcContextBase.LoginVerifyEmail
|
| KcContextBase.LoginVerifyEmail
|
||||||
| KcContextBase.Terms
|
| KcContextBase.Terms
|
||||||
| KcContextBase.LoginOtp
|
| KcContextBase.LoginOtp
|
||||||
|
| KcContextBase.LoginUsername
|
||||||
|
| KcContextBase.WebauthnAuthenticate
|
||||||
|
| KcContextBase.LoginPassword
|
||||||
| KcContextBase.LoginUpdatePassword
|
| KcContextBase.LoginUpdatePassword
|
||||||
| KcContextBase.LoginUpdateProfile
|
| KcContextBase.LoginUpdateProfile
|
||||||
| KcContextBase.LoginIdpLinkConfirm
|
| KcContextBase.LoginIdpLinkConfirm
|
||||||
| KcContextBase.LoginIdpLinkEmail
|
| KcContextBase.LoginIdpLinkEmail
|
||||||
| KcContextBase.LoginPageExpired
|
| KcContextBase.LoginPageExpired
|
||||||
| KcContextBase.LoginConfigTotp
|
| KcContextBase.LoginConfigTotp
|
||||||
| KcContextBase.LogoutConfirm;
|
| KcContextBase.LogoutConfirm
|
||||||
|
| KcContextBase.UpdateUserProfile
|
||||||
|
| KcContextBase.IdpReviewUserProfile;
|
||||||
|
|
||||||
|
export type WebauthnAuthenticator = {
|
||||||
|
credentialId: string;
|
||||||
|
transports: {
|
||||||
|
iconClass: KcTemplateClassKey;
|
||||||
|
displayNameProperties: MessageKeyBase[];
|
||||||
|
};
|
||||||
|
label: string;
|
||||||
|
createdAt: string;
|
||||||
|
};
|
||||||
|
|
||||||
export declare namespace KcContextBase {
|
export declare namespace KcContextBase {
|
||||||
export type Common = {
|
export type Common = {
|
||||||
@ -196,6 +212,77 @@ export declare namespace KcContextBase {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type LoginUsername = Common & {
|
||||||
|
pageId: "login-username.ftl";
|
||||||
|
url: {
|
||||||
|
loginResetCredentialsUrl: string;
|
||||||
|
registrationUrl: string;
|
||||||
|
};
|
||||||
|
realm: {
|
||||||
|
loginWithEmailAllowed: boolean;
|
||||||
|
rememberMe: boolean;
|
||||||
|
password: boolean;
|
||||||
|
resetPasswordAllowed: boolean;
|
||||||
|
registrationAllowed: boolean;
|
||||||
|
};
|
||||||
|
registrationDisabled: boolean;
|
||||||
|
login: {
|
||||||
|
username?: string;
|
||||||
|
rememberMe?: boolean;
|
||||||
|
};
|
||||||
|
usernameHidden?: boolean;
|
||||||
|
social: {
|
||||||
|
displayInfo: boolean;
|
||||||
|
providers?: {
|
||||||
|
loginUrl: string;
|
||||||
|
alias: string;
|
||||||
|
providerId: string;
|
||||||
|
displayName: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LoginPassword = Common & {
|
||||||
|
pageId: "login-password.ftl";
|
||||||
|
url: {
|
||||||
|
loginResetCredentialsUrl: string;
|
||||||
|
registrationUrl: string;
|
||||||
|
};
|
||||||
|
realm: {
|
||||||
|
resetPasswordAllowed: boolean;
|
||||||
|
};
|
||||||
|
auth?: {
|
||||||
|
showUsername?: boolean;
|
||||||
|
showResetCredentials?: boolean;
|
||||||
|
showTryAnotherWayLink?: boolean;
|
||||||
|
attemptedUsername?: string;
|
||||||
|
};
|
||||||
|
social: {
|
||||||
|
displayInfo: boolean;
|
||||||
|
};
|
||||||
|
login: {
|
||||||
|
password?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WebauthnAuthenticate = Common & {
|
||||||
|
pageId: "webauthn-authenticate.ftl";
|
||||||
|
authenticators: {
|
||||||
|
authenticators: WebauthnAuthenticator[];
|
||||||
|
};
|
||||||
|
challenge: string;
|
||||||
|
// I hate this:
|
||||||
|
userVerification: UserVerificationRequirement | "not specified";
|
||||||
|
rpId: string;
|
||||||
|
createTimeout: string;
|
||||||
|
isUserIdentified: "true" | "false";
|
||||||
|
shouldDisplayAuthenticators: boolean;
|
||||||
|
social: {
|
||||||
|
displayInfo: boolean;
|
||||||
|
};
|
||||||
|
login: {};
|
||||||
|
};
|
||||||
|
|
||||||
export type LoginUpdatePassword = Common & {
|
export type LoginUpdatePassword = Common & {
|
||||||
pageId: "login-update-password.ftl";
|
pageId: "login-update-password.ftl";
|
||||||
username: string;
|
username: string;
|
||||||
@ -270,6 +357,23 @@ export declare namespace KcContextBase {
|
|||||||
skipLink?: boolean;
|
skipLink?: boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UpdateUserProfile = Common & {
|
||||||
|
pageId: "update-user-profile.ftl";
|
||||||
|
profile: {
|
||||||
|
attributes: Attribute[];
|
||||||
|
attributesByName: Record<string, Attribute>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IdpReviewUserProfile = Common & {
|
||||||
|
pageId: "idp-review-user-profile.ftl";
|
||||||
|
profile: {
|
||||||
|
context: "IDP_REVIEW";
|
||||||
|
attributes: Attribute[];
|
||||||
|
attributesByName: Record<string, Attribute>;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Attribute = {
|
export type Attribute = {
|
||||||
|
@ -10,6 +10,7 @@ import { getKcContextFromWindow } from "./getKcContextFromWindow";
|
|||||||
import { pathJoin } from "../../bin/tools/pathJoin";
|
import { pathJoin } from "../../bin/tools/pathJoin";
|
||||||
import { pathBasename } from "../tools/pathBasename";
|
import { pathBasename } from "../tools/pathBasename";
|
||||||
import { mockTestingResourcesCommonPath } from "../../bin/mockTestingResourcesPath";
|
import { mockTestingResourcesCommonPath } from "../../bin/mockTestingResourcesPath";
|
||||||
|
import { symToStr } from "tsafe/symToStr";
|
||||||
|
|
||||||
export function getKcContext<KcContextExtended extends { pageId: string } = never>(params?: {
|
export function getKcContext<KcContextExtended extends { pageId: string } = never>(params?: {
|
||||||
mockPageId?: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
mockPageId?: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
||||||
@ -17,9 +18,19 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
|
|||||||
}): { kcContext: ExtendsKcContextBase<KcContextExtended> | undefined } {
|
}): { kcContext: ExtendsKcContextBase<KcContextExtended> | undefined } {
|
||||||
const { mockPageId, mockData } = params ?? {};
|
const { mockPageId, mockData } = params ?? {};
|
||||||
|
|
||||||
if (mockPageId !== undefined) {
|
const realKcContext = getKcContextFromWindow<KcContextExtended>();
|
||||||
|
|
||||||
|
if (mockPageId !== undefined && realKcContext === undefined) {
|
||||||
//TODO maybe trow if no mock fo custom page
|
//TODO maybe trow if no mock fo custom page
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
[
|
||||||
|
`%cKeycloakify: ${symToStr({ mockPageId })} set to ${mockPageId}.`,
|
||||||
|
`If assets are missing make sure you have built your Keycloak theme at least once.`
|
||||||
|
].join(" "),
|
||||||
|
"background: red; color: yellow; font-size: medium"
|
||||||
|
);
|
||||||
|
|
||||||
const kcContextDefaultMock = kcContextMocks.find(({ pageId }) => pageId === mockPageId);
|
const kcContextDefaultMock = kcContextMocks.find(({ pageId }) => pageId === mockPageId);
|
||||||
|
|
||||||
const partialKcContextCustomMock = mockData?.find(({ pageId }) => pageId === mockPageId);
|
const partialKcContextCustomMock = mockData?.find(({ pageId }) => pageId === mockPageId);
|
||||||
@ -47,8 +58,16 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
|
|||||||
"source": partialKcContextCustomMock
|
"source": partialKcContextCustomMock
|
||||||
});
|
});
|
||||||
|
|
||||||
if (partialKcContextCustomMock.pageId === "register-user-profile.ftl") {
|
if (
|
||||||
assert(kcContextDefaultMock?.pageId === "register-user-profile.ftl");
|
partialKcContextCustomMock.pageId === "register-user-profile.ftl" ||
|
||||||
|
partialKcContextCustomMock.pageId === "update-user-profile.ftl" ||
|
||||||
|
partialKcContextCustomMock.pageId === "idp-review-user-profile.ftl"
|
||||||
|
) {
|
||||||
|
assert(
|
||||||
|
kcContextDefaultMock?.pageId === "register-user-profile.ftl" ||
|
||||||
|
kcContextDefaultMock?.pageId === "update-user-profile.ftl" ||
|
||||||
|
kcContextDefaultMock?.pageId === "idp-review-user-profile.ftl"
|
||||||
|
);
|
||||||
|
|
||||||
const { attributes } = kcContextDefaultMock.profile;
|
const { attributes } = kcContextDefaultMock.profile;
|
||||||
|
|
||||||
@ -60,8 +79,6 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
|
|||||||
].filter(exclude(undefined));
|
].filter(exclude(undefined));
|
||||||
|
|
||||||
attributes.forEach(attribute => {
|
attributes.forEach(attribute => {
|
||||||
console.log("====>", attribute);
|
|
||||||
|
|
||||||
const partialAttribute = partialAttributes.find(({ name }) => name === attribute.name);
|
const partialAttribute = partialAttributes.find(({ name }) => name === attribute.name);
|
||||||
|
|
||||||
const augmentedAttribute: Attribute = {} as any;
|
const augmentedAttribute: Attribute = {} as any;
|
||||||
@ -100,13 +117,15 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
|
|||||||
return { kcContext };
|
return { kcContext };
|
||||||
}
|
}
|
||||||
|
|
||||||
const kcContext = getKcContextFromWindow<KcContextExtended>();
|
if (realKcContext === undefined) {
|
||||||
|
return { "kcContext": undefined };
|
||||||
|
}
|
||||||
|
|
||||||
if (kcContext !== undefined) {
|
{
|
||||||
const { url } = kcContext;
|
const { url } = realKcContext;
|
||||||
|
|
||||||
url.resourcesCommonPath = pathJoin(url.resourcesPath, pathBasename(mockTestingResourcesCommonPath));
|
url.resourcesCommonPath = pathJoin(url.resourcesPath, pathBasename(mockTestingResourcesCommonPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
return { kcContext };
|
return { "kcContext": realKcContext };
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,100 @@ import { pathJoin } from "../../../bin/tools/pathJoin";
|
|||||||
|
|
||||||
const PUBLIC_URL = process.env["PUBLIC_URL"] ?? "/";
|
const PUBLIC_URL = process.env["PUBLIC_URL"] ?? "/";
|
||||||
|
|
||||||
|
const attributes: Attribute[] = [
|
||||||
|
{
|
||||||
|
"validators": {
|
||||||
|
"username-prohibited-characters": {
|
||||||
|
"ignore.empty.value": true
|
||||||
|
},
|
||||||
|
"up-username-has-value": {},
|
||||||
|
"length": {
|
||||||
|
"ignore.empty.value": true,
|
||||||
|
"min": "3",
|
||||||
|
"max": "255"
|
||||||
|
},
|
||||||
|
"up-duplicate-username": {},
|
||||||
|
"up-username-mutation": {}
|
||||||
|
},
|
||||||
|
"displayName": "${username}",
|
||||||
|
"annotations": {},
|
||||||
|
"required": true,
|
||||||
|
"groupAnnotations": {},
|
||||||
|
"autocomplete": "username",
|
||||||
|
"readOnly": false,
|
||||||
|
"name": "username",
|
||||||
|
"value": "xxxx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"validators": {
|
||||||
|
"up-email-exists-as-username": {},
|
||||||
|
"length": {
|
||||||
|
"max": "255",
|
||||||
|
"ignore.empty.value": true
|
||||||
|
},
|
||||||
|
"up-blank-attribute-value": {
|
||||||
|
"error-message": "missingEmailMessage",
|
||||||
|
"fail-on-null": false
|
||||||
|
},
|
||||||
|
"up-duplicate-email": {},
|
||||||
|
"email": {
|
||||||
|
"ignore.empty.value": true
|
||||||
|
},
|
||||||
|
"pattern": {
|
||||||
|
"ignore.empty.value": true,
|
||||||
|
"pattern": "gmail\\.com$"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"displayName": "${email}",
|
||||||
|
"annotations": {},
|
||||||
|
"required": true,
|
||||||
|
"groupAnnotations": {},
|
||||||
|
"autocomplete": "email",
|
||||||
|
"readOnly": false,
|
||||||
|
"name": "email"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"validators": {
|
||||||
|
"length": {
|
||||||
|
"max": "255",
|
||||||
|
"ignore.empty.value": true
|
||||||
|
},
|
||||||
|
"person-name-prohibited-characters": {
|
||||||
|
"ignore.empty.value": true
|
||||||
|
},
|
||||||
|
"up-immutable-attribute": {},
|
||||||
|
"up-attribute-required-by-metadata-value": {}
|
||||||
|
},
|
||||||
|
"displayName": "${firstName}",
|
||||||
|
"annotations": {},
|
||||||
|
"required": true,
|
||||||
|
"groupAnnotations": {},
|
||||||
|
"readOnly": false,
|
||||||
|
"name": "firstName"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"validators": {
|
||||||
|
"length": {
|
||||||
|
"max": "255",
|
||||||
|
"ignore.empty.value": true
|
||||||
|
},
|
||||||
|
"person-name-prohibited-characters": {
|
||||||
|
"ignore.empty.value": true
|
||||||
|
},
|
||||||
|
"up-immutable-attribute": {},
|
||||||
|
"up-attribute-required-by-metadata-value": {}
|
||||||
|
},
|
||||||
|
"displayName": "${lastName}",
|
||||||
|
"annotations": {},
|
||||||
|
"required": true,
|
||||||
|
"groupAnnotations": {},
|
||||||
|
"readOnly": false,
|
||||||
|
"name": "lastName"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const attributesByName = Object.fromEntries(attributes.map(attribute => [attribute.name, attribute])) as any;
|
||||||
|
|
||||||
export const kcContextCommonMock: KcContextBase.Common = {
|
export const kcContextCommonMock: KcContextBase.Common = {
|
||||||
"url": {
|
"url": {
|
||||||
"loginAction": "#",
|
"loginAction": "#",
|
||||||
@ -200,104 +294,8 @@ export const kcContextMocks: KcContextBase[] = [
|
|||||||
...registerCommon,
|
...registerCommon,
|
||||||
"profile": {
|
"profile": {
|
||||||
"context": "REGISTRATION_PROFILE" as const,
|
"context": "REGISTRATION_PROFILE" as const,
|
||||||
...(() => {
|
attributes,
|
||||||
const attributes: Attribute[] = [
|
attributesByName
|
||||||
{
|
|
||||||
"validators": {
|
|
||||||
"username-prohibited-characters": {
|
|
||||||
"ignore.empty.value": true
|
|
||||||
},
|
|
||||||
"up-username-has-value": {},
|
|
||||||
"length": {
|
|
||||||
"ignore.empty.value": true,
|
|
||||||
"min": "3",
|
|
||||||
"max": "255"
|
|
||||||
},
|
|
||||||
"up-duplicate-username": {},
|
|
||||||
"up-username-mutation": {}
|
|
||||||
},
|
|
||||||
"displayName": "${username}",
|
|
||||||
"annotations": {},
|
|
||||||
"required": true,
|
|
||||||
"groupAnnotations": {},
|
|
||||||
"autocomplete": "username",
|
|
||||||
"readOnly": false,
|
|
||||||
"name": "username",
|
|
||||||
"value": "xxxx"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"validators": {
|
|
||||||
"up-email-exists-as-username": {},
|
|
||||||
"length": {
|
|
||||||
"max": "255",
|
|
||||||
"ignore.empty.value": true
|
|
||||||
},
|
|
||||||
"up-blank-attribute-value": {
|
|
||||||
"error-message": "missingEmailMessage",
|
|
||||||
"fail-on-null": false
|
|
||||||
},
|
|
||||||
"up-duplicate-email": {},
|
|
||||||
"email": {
|
|
||||||
"ignore.empty.value": true
|
|
||||||
},
|
|
||||||
"pattern": {
|
|
||||||
"ignore.empty.value": true,
|
|
||||||
"pattern": "gmail\\.com$"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"displayName": "${email}",
|
|
||||||
"annotations": {},
|
|
||||||
"required": true,
|
|
||||||
"groupAnnotations": {},
|
|
||||||
"autocomplete": "email",
|
|
||||||
"readOnly": false,
|
|
||||||
"name": "email"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"validators": {
|
|
||||||
"length": {
|
|
||||||
"max": "255",
|
|
||||||
"ignore.empty.value": true
|
|
||||||
},
|
|
||||||
"person-name-prohibited-characters": {
|
|
||||||
"ignore.empty.value": true
|
|
||||||
},
|
|
||||||
"up-immutable-attribute": {},
|
|
||||||
"up-attribute-required-by-metadata-value": {}
|
|
||||||
},
|
|
||||||
"displayName": "${firstName}",
|
|
||||||
"annotations": {},
|
|
||||||
"required": true,
|
|
||||||
"groupAnnotations": {},
|
|
||||||
"readOnly": false,
|
|
||||||
"name": "firstName"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"validators": {
|
|
||||||
"length": {
|
|
||||||
"max": "255",
|
|
||||||
"ignore.empty.value": true
|
|
||||||
},
|
|
||||||
"person-name-prohibited-characters": {
|
|
||||||
"ignore.empty.value": true
|
|
||||||
},
|
|
||||||
"up-immutable-attribute": {},
|
|
||||||
"up-attribute-required-by-metadata-value": {}
|
|
||||||
},
|
|
||||||
"displayName": "${lastName}",
|
|
||||||
"annotations": {},
|
|
||||||
"required": true,
|
|
||||||
"groupAnnotations": {},
|
|
||||||
"readOnly": false,
|
|
||||||
"name": "lastName"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
|
||||||
attributes,
|
|
||||||
"attributesByName": Object.fromEntries(attributes.map(attribute => [attribute.name, attribute])) as any
|
|
||||||
} as any;
|
|
||||||
})()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@ -361,6 +359,61 @@ export const kcContextMocks: KcContextBase[] = [
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
id<KcContextBase.LoginUsername>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "login-username.ftl",
|
||||||
|
"url": loginUrl,
|
||||||
|
"realm": {
|
||||||
|
...kcContextCommonMock.realm,
|
||||||
|
"loginWithEmailAllowed": true,
|
||||||
|
"rememberMe": true,
|
||||||
|
"password": true,
|
||||||
|
"resetPasswordAllowed": true,
|
||||||
|
"registrationAllowed": true
|
||||||
|
},
|
||||||
|
"social": {
|
||||||
|
"displayInfo": true
|
||||||
|
},
|
||||||
|
"usernameHidden": false,
|
||||||
|
"login": {
|
||||||
|
"rememberMe": false
|
||||||
|
},
|
||||||
|
"registrationDisabled": false
|
||||||
|
}),
|
||||||
|
id<KcContextBase.LoginPassword>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "login-password.ftl",
|
||||||
|
"url": loginUrl,
|
||||||
|
"realm": {
|
||||||
|
...kcContextCommonMock.realm,
|
||||||
|
"resetPasswordAllowed": true
|
||||||
|
},
|
||||||
|
"social": {
|
||||||
|
"displayInfo": false
|
||||||
|
},
|
||||||
|
"login": {}
|
||||||
|
}),
|
||||||
|
id<KcContextBase.WebauthnAuthenticate>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "webauthn-authenticate.ftl",
|
||||||
|
"url": loginUrl,
|
||||||
|
"authenticators": {
|
||||||
|
"authenticators": []
|
||||||
|
},
|
||||||
|
"realm": {
|
||||||
|
...kcContextCommonMock.realm
|
||||||
|
},
|
||||||
|
"challenge": "",
|
||||||
|
"userVerification": "not specified",
|
||||||
|
"rpId": "",
|
||||||
|
"createTimeout": "0",
|
||||||
|
"isUserIdentified": "false",
|
||||||
|
"shouldDisplayAuthenticators": false,
|
||||||
|
"social": {
|
||||||
|
"displayInfo": false
|
||||||
|
},
|
||||||
|
"login": {}
|
||||||
|
}),
|
||||||
id<KcContextBase.LoginUpdatePassword>({
|
id<KcContextBase.LoginUpdatePassword>({
|
||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "login-update-password.ftl",
|
"pageId": "login-update-password.ftl",
|
||||||
@ -423,5 +476,22 @@ export const kcContextMocks: KcContextBase[] = [
|
|||||||
"baseUrl": "#"
|
"baseUrl": "#"
|
||||||
},
|
},
|
||||||
"logoutConfirm": { "code": "123", skipLink: false }
|
"logoutConfirm": { "code": "123", skipLink: false }
|
||||||
|
}),
|
||||||
|
id<KcContextBase.UpdateUserProfile>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "update-user-profile.ftl",
|
||||||
|
"profile": {
|
||||||
|
attributes,
|
||||||
|
attributesByName
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
id<KcContextBase.IdpReviewUserProfile>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "idp-review-user-profile.ftl",
|
||||||
|
"profile": {
|
||||||
|
context: "IDP_REVIEW",
|
||||||
|
attributes,
|
||||||
|
attributesByName
|
||||||
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import "minimal-polyfills/Object.fromEntries";
|
import "minimal-polyfills/Object.fromEntries";
|
||||||
//NOTE for later: https://github.com/remarkjs/react-markdown/blob/236182ecf30bd89c1e5a7652acaf8d0bf81e6170/src/renderers.js#L7-L35
|
//NOTE for later: https://github.com/remarkjs/react-markdown/blob/236182ecf30bd89c1e5a7652acaf8d0bf81e6170/src/renderers.js#L7-L35
|
||||||
import React, { useEffect, useState, useRef } from "react";
|
import React, { useEffect, useState, useRef } from "react";
|
||||||
import ReactMarkdown from "react-markdown";
|
|
||||||
import type baseMessages from "./generated_messages/18.0.1/login/en";
|
import type baseMessages from "./generated_messages/18.0.1/login/en";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
|
import { Markdown } from "../tools/Markdown";
|
||||||
|
|
||||||
export const fallbackLanguageTag = "en";
|
export const fallbackLanguageTag = "en";
|
||||||
|
|
||||||
@ -234,9 +234,9 @@ function createI18nTranslationFunctions<MessageKey extends string>(params: {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
return doRenderMarkdown ? (
|
return doRenderMarkdown ? (
|
||||||
<ReactMarkdown allowDangerousHtml renderers={key === "termsText" ? undefined : { "paragraph": "span" }}>
|
<Markdown allowDangerousHtml renderers={{ "paragraph": "span" }}>
|
||||||
{messageWithArgsInjectedIfAny}
|
{messageWithArgsInjectedIfAny}
|
||||||
</ReactMarkdown>
|
</Markdown>
|
||||||
) : (
|
) : (
|
||||||
messageWithArgsInjectedIfAny
|
messageWithArgsInjectedIfAny
|
||||||
);
|
);
|
||||||
|
3
src/lib/tools/Markdown.ts
Normal file
3
src/lib/tools/Markdown.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import Markdown from "react-markdown";
|
||||||
|
|
||||||
|
export { Markdown };
|
@ -304,13 +304,17 @@ export function useGetErrors(params: {
|
|||||||
return { getErrors };
|
return { getErrors };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: The attributesWithPassword returned is actually augmented with
|
||||||
|
* artificial password related attributes only if kcContext.passwordRequired === true
|
||||||
|
*/
|
||||||
export function useFormValidationSlice(params: {
|
export function useFormValidationSlice(params: {
|
||||||
kcContext: {
|
kcContext: {
|
||||||
messagesPerField: Pick<KcContextBase.Common["messagesPerField"], "existsError" | "get">;
|
messagesPerField: Pick<KcContextBase.Common["messagesPerField"], "existsError" | "get">;
|
||||||
profile: {
|
profile: {
|
||||||
attributes: Attribute[];
|
attributes: Attribute[];
|
||||||
};
|
};
|
||||||
passwordRequired: boolean;
|
passwordRequired?: boolean;
|
||||||
realm: { registrationEmailAsUsername: boolean };
|
realm: { registrationEmailAsUsername: boolean };
|
||||||
};
|
};
|
||||||
/** NOTE: Try to avoid passing a new ref every render for better performances. */
|
/** NOTE: Try to avoid passing a new ref every render for better performances. */
|
||||||
|
318
yarn.lock
318
yarn.lock
@ -2,25 +2,126 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
"@babel/code-frame@^7.0.0":
|
"@ampproject/remapping@^2.1.0":
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
|
||||||
|
integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/gen-mapping" "^0.1.0"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.9"
|
||||||
|
|
||||||
|
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6":
|
||||||
version "7.18.6"
|
version "7.18.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
|
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
|
||||||
integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
|
integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/highlight" "^7.18.6"
|
"@babel/highlight" "^7.18.6"
|
||||||
|
|
||||||
"@babel/helper-module-imports@^7.16.7":
|
"@babel/compat-data@^7.19.3":
|
||||||
|
version "7.19.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.3.tgz#707b939793f867f5a73b2666e6d9a3396eb03151"
|
||||||
|
integrity sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw==
|
||||||
|
|
||||||
|
"@babel/core@^7.0.0":
|
||||||
|
version "7.19.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.3.tgz#2519f62a51458f43b682d61583c3810e7dcee64c"
|
||||||
|
integrity sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==
|
||||||
|
dependencies:
|
||||||
|
"@ampproject/remapping" "^2.1.0"
|
||||||
|
"@babel/code-frame" "^7.18.6"
|
||||||
|
"@babel/generator" "^7.19.3"
|
||||||
|
"@babel/helper-compilation-targets" "^7.19.3"
|
||||||
|
"@babel/helper-module-transforms" "^7.19.0"
|
||||||
|
"@babel/helpers" "^7.19.0"
|
||||||
|
"@babel/parser" "^7.19.3"
|
||||||
|
"@babel/template" "^7.18.10"
|
||||||
|
"@babel/traverse" "^7.19.3"
|
||||||
|
"@babel/types" "^7.19.3"
|
||||||
|
convert-source-map "^1.7.0"
|
||||||
|
debug "^4.1.0"
|
||||||
|
gensync "^1.0.0-beta.2"
|
||||||
|
json5 "^2.2.1"
|
||||||
|
semver "^6.3.0"
|
||||||
|
|
||||||
|
"@babel/generator@^7.19.3":
|
||||||
|
version "7.19.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.3.tgz#d7f4d1300485b4547cb6f94b27d10d237b42bf59"
|
||||||
|
integrity sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.19.3"
|
||||||
|
"@jridgewell/gen-mapping" "^0.3.2"
|
||||||
|
jsesc "^2.5.1"
|
||||||
|
|
||||||
|
"@babel/helper-compilation-targets@^7.19.3":
|
||||||
|
version "7.19.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz#a10a04588125675d7c7ae299af86fa1b2ee038ca"
|
||||||
|
integrity sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/compat-data" "^7.19.3"
|
||||||
|
"@babel/helper-validator-option" "^7.18.6"
|
||||||
|
browserslist "^4.21.3"
|
||||||
|
semver "^6.3.0"
|
||||||
|
|
||||||
|
"@babel/helper-environment-visitor@^7.18.9":
|
||||||
|
version "7.18.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
|
||||||
|
integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
|
||||||
|
|
||||||
|
"@babel/helper-function-name@^7.19.0":
|
||||||
|
version "7.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c"
|
||||||
|
integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==
|
||||||
|
dependencies:
|
||||||
|
"@babel/template" "^7.18.10"
|
||||||
|
"@babel/types" "^7.19.0"
|
||||||
|
|
||||||
|
"@babel/helper-hoist-variables@^7.18.6":
|
||||||
|
version "7.18.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
|
||||||
|
integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.18.6"
|
||||||
|
|
||||||
|
"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6":
|
||||||
version "7.18.6"
|
version "7.18.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
|
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
|
||||||
integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
|
integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.18.6"
|
"@babel/types" "^7.18.6"
|
||||||
|
|
||||||
|
"@babel/helper-module-transforms@^7.19.0":
|
||||||
|
version "7.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30"
|
||||||
|
integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-environment-visitor" "^7.18.9"
|
||||||
|
"@babel/helper-module-imports" "^7.18.6"
|
||||||
|
"@babel/helper-simple-access" "^7.18.6"
|
||||||
|
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||||
|
"@babel/helper-validator-identifier" "^7.18.6"
|
||||||
|
"@babel/template" "^7.18.10"
|
||||||
|
"@babel/traverse" "^7.19.0"
|
||||||
|
"@babel/types" "^7.19.0"
|
||||||
|
|
||||||
"@babel/helper-plugin-utils@^7.18.6":
|
"@babel/helper-plugin-utils@^7.18.6":
|
||||||
version "7.18.9"
|
version "7.18.9"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f"
|
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f"
|
||||||
integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==
|
integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==
|
||||||
|
|
||||||
|
"@babel/helper-simple-access@^7.18.6":
|
||||||
|
version "7.18.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea"
|
||||||
|
integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.18.6"
|
||||||
|
|
||||||
|
"@babel/helper-split-export-declaration@^7.18.6":
|
||||||
|
version "7.18.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075"
|
||||||
|
integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.18.6"
|
||||||
|
|
||||||
"@babel/helper-string-parser@^7.18.10":
|
"@babel/helper-string-parser@^7.18.10":
|
||||||
version "7.18.10"
|
version "7.18.10"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56"
|
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56"
|
||||||
@ -31,6 +132,25 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
|
||||||
integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
|
integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
|
||||||
|
|
||||||
|
"@babel/helper-validator-identifier@^7.19.1":
|
||||||
|
version "7.19.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
|
||||||
|
integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
|
||||||
|
|
||||||
|
"@babel/helper-validator-option@^7.18.6":
|
||||||
|
version "7.18.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
|
||||||
|
integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==
|
||||||
|
|
||||||
|
"@babel/helpers@^7.19.0":
|
||||||
|
version "7.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.0.tgz#f30534657faf246ae96551d88dd31e9d1fa1fc18"
|
||||||
|
integrity sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/template" "^7.18.10"
|
||||||
|
"@babel/traverse" "^7.19.0"
|
||||||
|
"@babel/types" "^7.19.0"
|
||||||
|
|
||||||
"@babel/highlight@^7.18.6":
|
"@babel/highlight@^7.18.6":
|
||||||
version "7.18.6"
|
version "7.18.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
|
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
|
||||||
@ -40,6 +160,11 @@
|
|||||||
chalk "^2.0.0"
|
chalk "^2.0.0"
|
||||||
js-tokens "^4.0.0"
|
js-tokens "^4.0.0"
|
||||||
|
|
||||||
|
"@babel/parser@^7.18.10", "@babel/parser@^7.19.3":
|
||||||
|
version "7.19.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a"
|
||||||
|
integrity sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==
|
||||||
|
|
||||||
"@babel/plugin-syntax-jsx@^7.17.12":
|
"@babel/plugin-syntax-jsx@^7.17.12":
|
||||||
version "7.18.6"
|
version "7.18.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0"
|
||||||
@ -54,6 +179,40 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
|
"@babel/template@^7.18.10":
|
||||||
|
version "7.18.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71"
|
||||||
|
integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/code-frame" "^7.18.6"
|
||||||
|
"@babel/parser" "^7.18.10"
|
||||||
|
"@babel/types" "^7.18.10"
|
||||||
|
|
||||||
|
"@babel/traverse@^7.19.0", "@babel/traverse@^7.19.3":
|
||||||
|
version "7.19.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.3.tgz#3a3c5348d4988ba60884e8494b0592b2f15a04b4"
|
||||||
|
integrity sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/code-frame" "^7.18.6"
|
||||||
|
"@babel/generator" "^7.19.3"
|
||||||
|
"@babel/helper-environment-visitor" "^7.18.9"
|
||||||
|
"@babel/helper-function-name" "^7.19.0"
|
||||||
|
"@babel/helper-hoist-variables" "^7.18.6"
|
||||||
|
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||||
|
"@babel/parser" "^7.19.3"
|
||||||
|
"@babel/types" "^7.19.3"
|
||||||
|
debug "^4.1.0"
|
||||||
|
globals "^11.1.0"
|
||||||
|
|
||||||
|
"@babel/types@^7.18.10", "@babel/types@^7.19.0", "@babel/types@^7.19.3":
|
||||||
|
version "7.19.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.3.tgz#fc420e6bbe54880bce6779ffaf315f5e43ec9624"
|
||||||
|
integrity sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-string-parser" "^7.18.10"
|
||||||
|
"@babel/helper-validator-identifier" "^7.19.1"
|
||||||
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@babel/types@^7.18.6":
|
"@babel/types@^7.18.6":
|
||||||
version "7.18.13"
|
version "7.18.13"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.13.tgz#30aeb9e514f4100f7c1cb6e5ba472b30e48f519a"
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.13.tgz#30aeb9e514f4100f7c1cb6e5ba472b30e48f519a"
|
||||||
@ -152,6 +311,46 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb"
|
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb"
|
||||||
integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
|
integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
|
||||||
|
|
||||||
|
"@jridgewell/gen-mapping@^0.1.0":
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
|
||||||
|
integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/set-array" "^1.0.0"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
|
||||||
|
"@jridgewell/gen-mapping@^0.3.2":
|
||||||
|
version "0.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
|
||||||
|
integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/set-array" "^1.0.1"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.9"
|
||||||
|
|
||||||
|
"@jridgewell/resolve-uri@^3.0.3":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
|
||||||
|
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
|
||||||
|
|
||||||
|
"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
|
||||||
|
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec@^1.4.10":
|
||||||
|
version "1.4.14"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
||||||
|
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping@^0.3.9":
|
||||||
|
version "0.3.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774"
|
||||||
|
integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/resolve-uri" "^3.0.3"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
|
||||||
"@octokit/auth-token@^2.4.4":
|
"@octokit/auth-token@^2.4.4":
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36"
|
resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36"
|
||||||
@ -397,11 +596,26 @@ braces@^3.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fill-range "^7.0.1"
|
fill-range "^7.0.1"
|
||||||
|
|
||||||
|
browserslist@^4.21.3:
|
||||||
|
version "4.21.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987"
|
||||||
|
integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
|
||||||
|
dependencies:
|
||||||
|
caniuse-lite "^1.0.30001400"
|
||||||
|
electron-to-chromium "^1.4.251"
|
||||||
|
node-releases "^2.0.6"
|
||||||
|
update-browserslist-db "^1.0.9"
|
||||||
|
|
||||||
callsites@^3.0.0:
|
callsites@^3.0.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||||
|
|
||||||
|
caniuse-lite@^1.0.30001400:
|
||||||
|
version "1.0.30001415"
|
||||||
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001415.tgz#fd7ea96e9e94c181a7f56e7571efb43d92b860cc"
|
||||||
|
integrity sha512-ER+PfgCJUe8BqunLGWd/1EY4g8AzQcsDAVzdtMGKVtQEmKAwaFfU6vb7EAVIqTMYsqxBorYZi2+22Iouj/y7GQ==
|
||||||
|
|
||||||
chalk@^2.0.0:
|
chalk@^2.0.0:
|
||||||
version "2.4.2"
|
version "2.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||||
@ -549,7 +763,7 @@ concat-map@0.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||||
|
|
||||||
convert-source-map@^1.5.0:
|
convert-source-map@^1.5.0, convert-source-map@^1.7.0:
|
||||||
version "1.8.0"
|
version "1.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
|
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
|
||||||
integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
|
integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
|
||||||
@ -623,7 +837,7 @@ d@1, d@^1.0.1:
|
|||||||
es5-ext "^0.10.50"
|
es5-ext "^0.10.50"
|
||||||
type "^1.0.1"
|
type "^1.0.1"
|
||||||
|
|
||||||
debug@^4.0.0, debug@^4.3.2:
|
debug@^4.0.0, debug@^4.1.0, debug@^4.3.2:
|
||||||
version "4.3.4"
|
version "4.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||||
@ -665,6 +879,11 @@ domutils@^3.0.1:
|
|||||||
domelementtype "^2.3.0"
|
domelementtype "^2.3.0"
|
||||||
domhandler "^5.0.1"
|
domhandler "^5.0.1"
|
||||||
|
|
||||||
|
electron-to-chromium@^1.4.251:
|
||||||
|
version "1.4.271"
|
||||||
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.271.tgz#2d9f04f6a53c70e1bb1acfaae9c39f07ca40d290"
|
||||||
|
integrity sha512-BCPBtK07xR1/uY2HFDtl3wK2De66AW4MSiPlLrnPNxKC/Qhccxd59W73654S3y6Rb/k3hmuGJOBnhjfoutetXA==
|
||||||
|
|
||||||
emoji-regex@^8.0.0:
|
emoji-regex@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||||
@ -748,14 +967,14 @@ event-emitter@^0.3.5:
|
|||||||
d "1"
|
d "1"
|
||||||
es5-ext "~0.10.14"
|
es5-ext "~0.10.14"
|
||||||
|
|
||||||
evt@^2.4.0:
|
evt@^2.4.5:
|
||||||
version "2.4.0"
|
version "2.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/evt/-/evt-2.4.0.tgz#5052d3a685bba8f7e7be699b358c1781bd4037bf"
|
resolved "https://registry.yarnpkg.com/evt/-/evt-2.4.5.tgz#9e383c20a7917977e26b9fbad178585916ea8142"
|
||||||
integrity sha512-1V8FnExwqzX2fufT793mHfCnQay078kRdAEDHzJz863pgJK9aeDiWXZFs5aKmzJfIgi+svevtApVnleZXz4Jeg==
|
integrity sha512-shiXMrEhTHqTl5PacT3vdA1U9i3usnInuxt77Lj0Ph9igNM982b1Uf/3L24OSzI0SqLx/XIRVIJcgzUZ2Ij8AA==
|
||||||
dependencies:
|
dependencies:
|
||||||
minimal-polyfills "^2.2.2"
|
minimal-polyfills "^2.2.2"
|
||||||
run-exclusive "^2.2.16"
|
run-exclusive "^2.2.16"
|
||||||
tsafe "^1.0.1"
|
tsafe "^1.1.2"
|
||||||
|
|
||||||
execa@^5.1.1:
|
execa@^5.1.1:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
@ -821,6 +1040,11 @@ function-bind@^1.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||||
|
|
||||||
|
gensync@^1.0.0-beta.2:
|
||||||
|
version "1.0.0-beta.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||||
|
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
||||||
|
|
||||||
get-caller-file@^2.0.5:
|
get-caller-file@^2.0.5:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||||
@ -848,6 +1072,11 @@ glob@^7.0.5, glob@^7.1.3:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
path-is-absolute "^1.0.0"
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
|
globals@^11.1.0:
|
||||||
|
version "11.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||||
|
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
||||||
|
|
||||||
has-flag@^3.0.0:
|
has-flag@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||||
@ -1038,11 +1267,21 @@ isexe@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||||
|
|
||||||
|
jsesc@^2.5.1:
|
||||||
|
version "2.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
|
||||||
|
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
|
||||||
|
|
||||||
json-parse-even-better-errors@^2.3.0:
|
json-parse-even-better-errors@^2.3.0:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
|
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
|
||||||
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
|
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
|
||||||
|
|
||||||
|
json5@^2.2.1:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
|
||||||
|
integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
|
||||||
|
|
||||||
lines-and-columns@^1.1.6:
|
lines-and-columns@^1.1.6:
|
||||||
version "1.2.4"
|
version "1.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
||||||
@ -1220,6 +1459,11 @@ node-fetch@^2.6.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
whatwg-url "^5.0.0"
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
|
node-releases@^2.0.6:
|
||||||
|
version "2.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
|
||||||
|
integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
|
||||||
|
|
||||||
noms@0.0.0:
|
noms@0.0.0:
|
||||||
version "0.0.0"
|
version "0.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859"
|
resolved "https://registry.yarnpkg.com/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859"
|
||||||
@ -1366,6 +1610,11 @@ path-type@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||||
|
|
||||||
|
picocolors@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
||||||
|
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
||||||
|
|
||||||
picomatch@^2.3.1:
|
picomatch@^2.3.1:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||||
@ -1385,15 +1634,15 @@ please-upgrade-node@^3.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
semver-compare "^1.0.0"
|
semver-compare "^1.0.0"
|
||||||
|
|
||||||
powerhooks@^0.20.15:
|
powerhooks@^0.20.22:
|
||||||
version "0.20.15"
|
version "0.20.22"
|
||||||
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.20.15.tgz#e5093a11f3ed1badbc09944acf5a3fb15ac3f1d7"
|
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.20.22.tgz#469ba048dfa9c1d549325c73a091f8f3b96895b3"
|
||||||
integrity sha512-28xNjPTtjPPP8vyi9SWXfn0U9hjxm/UzuarD2uofIXa/CPWmkCeTtikyLoR6yuiE4pOfvdDXVfU5im40GuzJaQ==
|
integrity sha512-xFv5s7JTkwQh+lDVR1yLgGXPYfpbmdmdg6qT4VTF4EArieaImhCXFp7arSul55FvYoCuD5+gR9ooctDG53LeLg==
|
||||||
dependencies:
|
dependencies:
|
||||||
evt "^2.4.0"
|
evt "^2.4.5"
|
||||||
memoizee "^0.4.15"
|
memoizee "^0.4.15"
|
||||||
resize-observer-polyfill "^1.5.1"
|
resize-observer-polyfill "^1.5.1"
|
||||||
tsafe "^1.0.1"
|
tsafe "^1.1.3"
|
||||||
|
|
||||||
prettier@^2.3.0:
|
prettier@^2.3.0:
|
||||||
version "2.7.1"
|
version "2.7.1"
|
||||||
@ -1516,6 +1765,11 @@ restore-cursor@^3.1.0:
|
|||||||
onetime "^5.1.0"
|
onetime "^5.1.0"
|
||||||
signal-exit "^3.0.2"
|
signal-exit "^3.0.2"
|
||||||
|
|
||||||
|
rfc4648@^1.5.2:
|
||||||
|
version "1.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.5.2.tgz#cf5dac417dd83e7f4debf52e3797a723c1373383"
|
||||||
|
integrity sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg==
|
||||||
|
|
||||||
rfdc@^1.3.0:
|
rfdc@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
|
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
|
||||||
@ -1562,6 +1816,11 @@ semver-regex@^3.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.4.tgz#13053c0d4aa11d070a2f2872b6b1e3ae1e1971b4"
|
resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.4.tgz#13053c0d4aa11d070a2f2872b6b1e3ae1e1971b4"
|
||||||
integrity sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==
|
integrity sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==
|
||||||
|
|
||||||
|
semver@^6.3.0:
|
||||||
|
version "6.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||||
|
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||||
|
|
||||||
shebang-command@^2.0.0:
|
shebang-command@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
||||||
@ -1733,20 +1992,25 @@ trough@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
|
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
|
||||||
integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
|
integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
|
||||||
|
|
||||||
tsafe@^1.0.1:
|
tsafe@^1.1.2:
|
||||||
version "1.0.1"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.0.1.tgz#c8c4eb2d75d1478418a4941307c5dd667fd76d23"
|
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.1.2.tgz#cece1900bca89e25a84a65e7087f00dff3664b2e"
|
||||||
integrity sha512-FgJ1a4rE7YbmW5QIzpsfFl4tsAp0x74FH2bVE6qODb2U8jSrwTr5/ckIazeylme5zXndVbtgKm4BZdqmoGhiPw==
|
integrity sha512-jom5KsB9vpvOE9dLx2yTrPAJzzwU9CRPgoatoD7m2Zb7FCqo6ueEdZ+AZk+OysM4N+m8EUnIa1s9Pq3IMRDYLA==
|
||||||
|
|
||||||
|
tsafe@^1.1.3:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.1.3.tgz#fa7c2ae7da689884292b70ee30febbca5c665d03"
|
||||||
|
integrity sha512-QQQoed5Acb7Qe/sjMwE/qg57mxD5MXqY5HcGN4i3QyhJiplpo79ABEZiOBvL1cRTOYIzeryWvWQ3xsotTA49MQ==
|
||||||
|
|
||||||
tslib@^2.1.0:
|
tslib@^2.1.0:
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
||||||
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
||||||
|
|
||||||
tss-react@^4.1.3:
|
tss-react@^4.3.4:
|
||||||
version "4.1.3"
|
version "4.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-4.1.3.tgz#170a2edd85e32ba3b752416eeb45438c35e90243"
|
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-4.3.4.tgz#1e6d67f963562f4c232ec28ca4d90c8aac3a728b"
|
||||||
integrity sha512-Ul99aVoVngHY2nwFC87jZlGP+K3LfG9ZRakimOzRwgJz77f/zXWuY2FVzwaSc+OW804twNaxZZRtLlpagex8sg==
|
integrity sha512-PAEB9NxGtW2nEuPDm90WISi4nb9O+bmY/XyDKp17/CoPsoaW3UqaEi+gTjBmA/P65GCRuZhpZniTOkhLcTDQqg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@emotion/cache" "*"
|
"@emotion/cache" "*"
|
||||||
"@emotion/serialize" "*"
|
"@emotion/serialize" "*"
|
||||||
@ -1828,6 +2092,14 @@ untildify@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
|
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
|
||||||
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
|
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
|
||||||
|
|
||||||
|
update-browserslist-db@^1.0.9:
|
||||||
|
version "1.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18"
|
||||||
|
integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==
|
||||||
|
dependencies:
|
||||||
|
escalade "^3.1.1"
|
||||||
|
picocolors "^1.0.0"
|
||||||
|
|
||||||
util-deprecate@~1.0.1:
|
util-deprecate@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
|
Reference in New Issue
Block a user