Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
af87e41bb8 | |||
9ba884483d | |||
f5a300953a | |||
ab9a962f58 | |||
484adb607f | |||
e1f38d4196 | |||
5de629acf2 | |||
8b4b24a036 | |||
75ab130249 | |||
981ca7e9a4 | |||
acb4e260a7 | |||
ff20b0a844 | |||
1b77c69a01 | |||
158275f5c2 | |||
a085c8093e | |||
cb358bd745 | |||
e788c46601 | |||
d551b4bffb | |||
c168c7b156 | |||
7a46115042 | |||
249a7bde89 |
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "keycloakify",
|
||||
"version": "11.8.4",
|
||||
"version": "11.8.12",
|
||||
"description": "Framework to create custom Keycloak UIs",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -280,6 +280,24 @@ export async function downloadKeycloakDefaultTheme(params: {
|
||||
"fonts",
|
||||
"OpenSans-Semibold-webfont.woff2"
|
||||
),
|
||||
pathJoin(
|
||||
"patternfly",
|
||||
"dist",
|
||||
"fonts",
|
||||
"OpenSans-SemiboldItalic-webfont.woff2"
|
||||
),
|
||||
pathJoin(
|
||||
"patternfly",
|
||||
"dist",
|
||||
"fonts",
|
||||
"OpenSans-SemiboldItalic-webfont.woff"
|
||||
),
|
||||
pathJoin(
|
||||
"patternfly",
|
||||
"dist",
|
||||
"fonts",
|
||||
"OpenSans-SemiboldItalic-webfont.ttf"
|
||||
),
|
||||
pathJoin("patternfly", "dist", "img", "bg-login.jpg"),
|
||||
pathJoin("jquery", "dist", "jquery.min.js"),
|
||||
pathJoin("rfc4648", "lib", "rfc4648.js")
|
||||
|
@ -49,12 +49,15 @@ export async function command(params: { buildContext: BuildContext }) {
|
||||
}
|
||||
|
||||
const { value: emailThemeType } = await cliSelect({
|
||||
values: ["native (FreeMarker)" as const, "jsx-email (React)" as const]
|
||||
values: [
|
||||
"native (FreeMarker)" as const,
|
||||
"Another email templating solution" as const
|
||||
]
|
||||
}).catch(() => {
|
||||
process.exit(-1);
|
||||
});
|
||||
|
||||
if (emailThemeType === "jsx-email (React)") {
|
||||
if (emailThemeType === "Another email templating solution") {
|
||||
console.log(
|
||||
[
|
||||
"There is currently no automated support for keycloakify-email, it has to be done manually, see documentation:",
|
||||
@ -103,14 +106,21 @@ export async function command(params: { buildContext: BuildContext }) {
|
||||
|
||||
const moduleName = `@keycloakify/email-native`;
|
||||
|
||||
const [version] = (
|
||||
JSON.parse(
|
||||
child_process
|
||||
const [version] = ((): string[] => {
|
||||
const cmdOutput = child_process
|
||||
.execSync(`npm show ${moduleName} versions --json`)
|
||||
.toString("utf8")
|
||||
.trim()
|
||||
) as string[]
|
||||
)
|
||||
.trim();
|
||||
|
||||
const versions = JSON.parse(cmdOutput) as string | string[];
|
||||
|
||||
// NOTE: Bug in some older npm versions
|
||||
if (typeof versions === "string") {
|
||||
return [versions];
|
||||
}
|
||||
|
||||
return versions;
|
||||
})()
|
||||
.reverse()
|
||||
.filter(version => !version.includes("-"));
|
||||
|
||||
|
@ -105,14 +105,21 @@ export async function initializeSpa(params: {
|
||||
|
||||
const moduleName = `@keycloakify/keycloak-${themeType}-ui`;
|
||||
|
||||
const version = (
|
||||
JSON.parse(
|
||||
child_process
|
||||
const version = ((): string[] => {
|
||||
const cmdOutput = child_process
|
||||
.execSync(`npm show ${moduleName} versions --json`)
|
||||
.toString("utf8")
|
||||
.trim()
|
||||
) as string[]
|
||||
)
|
||||
.trim();
|
||||
|
||||
const versions = JSON.parse(cmdOutput) as string | string[];
|
||||
|
||||
// NOTE: Bug in some older npm versions
|
||||
if (typeof versions === "string") {
|
||||
return [versions];
|
||||
}
|
||||
|
||||
return versions;
|
||||
})()
|
||||
.reverse()
|
||||
.filter(version => !version.includes("-"))
|
||||
.find(version =>
|
||||
|
@ -101,7 +101,6 @@ function addOrEditTestUser(params: {
|
||||
);
|
||||
|
||||
newUser.username = defaultUser_default.username;
|
||||
newUser.email = defaultUser_default.email;
|
||||
|
||||
delete_existing_password_credential_if_any: {
|
||||
const i = newUser.credentials.findIndex(
|
||||
|
@ -53,12 +53,18 @@ export async function command(params: {
|
||||
.execSync("docker --version", {
|
||||
stdio: ["ignore", "pipe", "ignore"]
|
||||
})
|
||||
?.toString("utf8");
|
||||
} catch {}
|
||||
.toString("utf8");
|
||||
} catch {
|
||||
commandOutput = "";
|
||||
}
|
||||
|
||||
if (commandOutput?.includes("Docker") || commandOutput?.includes("podman")) {
|
||||
commandOutput = commandOutput.trim().toLowerCase();
|
||||
|
||||
for (const term of ["docker", "podman"]) {
|
||||
if (commandOutput.includes(term)) {
|
||||
break exit_if_docker_not_installed;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
[
|
||||
@ -781,6 +787,40 @@ export async function command(params: {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ignore_patternfly: {
|
||||
if (
|
||||
!isInside({
|
||||
dirPath: pathJoin(
|
||||
buildContext.themeSrcDirPath,
|
||||
"shared",
|
||||
"@patternfly"
|
||||
),
|
||||
filePath
|
||||
})
|
||||
) {
|
||||
break ignore_patternfly;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ignore_keycloak_ui_shared: {
|
||||
if (
|
||||
!isInside({
|
||||
dirPath: pathJoin(
|
||||
buildContext.themeSrcDirPath,
|
||||
"shared",
|
||||
"keycloak-ui-shared"
|
||||
),
|
||||
filePath
|
||||
})
|
||||
) {
|
||||
break ignore_keycloak_ui_shared;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Detected changes in ${filePath}`);
|
||||
|
@ -100,9 +100,19 @@ function addCommentToSourceCode(params: {
|
||||
}
|
||||
|
||||
if (fileRelativePath.endsWith(".ftl")) {
|
||||
return toResult(
|
||||
[`<#--`, ...commentLines.map(line => ` ${line}`), `-->`].join("\n")
|
||||
const comment = [`<#--`, ...commentLines.map(line => ` ${line}`), `-->`].join(
|
||||
"\n"
|
||||
);
|
||||
|
||||
if (sourceCode.trim().startsWith("<#ftl")) {
|
||||
const [first, ...rest] = sourceCode.split(">");
|
||||
|
||||
const last = rest.join(">");
|
||||
|
||||
return [`${first}>`, comment, last].join("\n");
|
||||
}
|
||||
|
||||
return toResult(comment);
|
||||
}
|
||||
|
||||
if (fileRelativePath.endsWith(".html") || fileRelativePath.endsWith(".svg")) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { JSX } from "keycloakify/tools/JSX";
|
||||
import { useEffect, useReducer, Fragment } from "react";
|
||||
import { useEffect, Fragment } from "react";
|
||||
import { assert } from "keycloakify/tools/assert";
|
||||
import { useIsPasswordRevealed } from "keycloakify/tools/useIsPasswordRevealed";
|
||||
import type { KcClsx } from "keycloakify/login/lib/kcClsx";
|
||||
import {
|
||||
useUserProfileForm,
|
||||
@ -249,15 +250,7 @@ function PasswordWrapper(props: { kcClsx: KcClsx; i18n: I18n; passwordInputId: s
|
||||
|
||||
const { msgStr } = i18n;
|
||||
|
||||
const [isPasswordRevealed, toggleIsPasswordRevealed] = useReducer((isPasswordRevealed: boolean) => !isPasswordRevealed, false);
|
||||
|
||||
useEffect(() => {
|
||||
const passwordInputElement = document.getElementById(passwordInputId);
|
||||
|
||||
assert(passwordInputElement instanceof HTMLInputElement);
|
||||
|
||||
passwordInputElement.type = isPasswordRevealed ? "text" : "password";
|
||||
}, [isPasswordRevealed]);
|
||||
const { isPasswordRevealed, toggleIsPasswordRevealed } = useIsPasswordRevealed({ passwordInputId });
|
||||
|
||||
return (
|
||||
<div className={kcClsx("kcInputGroup")}>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { JSX } from "keycloakify/tools/JSX";
|
||||
import { useState, useEffect, useReducer } from "react";
|
||||
import { useState } from "react";
|
||||
import { kcSanitize } from "keycloakify/lib/kcSanitize";
|
||||
import { assert } from "keycloakify/tools/assert";
|
||||
import { useIsPasswordRevealed } from "keycloakify/tools/useIsPasswordRevealed";
|
||||
import { clsx } from "keycloakify/tools/clsx";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
|
||||
@ -200,15 +200,7 @@ function PasswordWrapper(props: { kcClsx: KcClsx; i18n: I18n; passwordInputId: s
|
||||
|
||||
const { msgStr } = i18n;
|
||||
|
||||
const [isPasswordRevealed, toggleIsPasswordRevealed] = useReducer((isPasswordRevealed: boolean) => !isPasswordRevealed, false);
|
||||
|
||||
useEffect(() => {
|
||||
const passwordInputElement = document.getElementById(passwordInputId);
|
||||
|
||||
assert(passwordInputElement instanceof HTMLInputElement);
|
||||
|
||||
passwordInputElement.type = isPasswordRevealed ? "text" : "password";
|
||||
}, [isPasswordRevealed]);
|
||||
const { isPasswordRevealed, toggleIsPasswordRevealed } = useIsPasswordRevealed({ passwordInputId });
|
||||
|
||||
return (
|
||||
<div className={kcClsx("kcInputGroup")}>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import type { JSX } from "keycloakify/tools/JSX";
|
||||
import { useState, useEffect, useReducer } from "react";
|
||||
import { useState } from "react";
|
||||
import { kcSanitize } from "keycloakify/lib/kcSanitize";
|
||||
import { clsx } from "keycloakify/tools/clsx";
|
||||
import { assert } from "keycloakify/tools/assert";
|
||||
import { useIsPasswordRevealed } from "keycloakify/tools/useIsPasswordRevealed";
|
||||
import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
@ -107,15 +107,7 @@ function PasswordWrapper(props: { kcClsx: KcClsx; i18n: I18n; passwordInputId: s
|
||||
|
||||
const { msgStr } = i18n;
|
||||
|
||||
const [isPasswordRevealed, toggleIsPasswordRevealed] = useReducer((isPasswordRevealed: boolean) => !isPasswordRevealed, false);
|
||||
|
||||
useEffect(() => {
|
||||
const passwordInputElement = document.getElementById(passwordInputId);
|
||||
|
||||
assert(passwordInputElement instanceof HTMLInputElement);
|
||||
|
||||
passwordInputElement.type = isPasswordRevealed ? "text" : "password";
|
||||
}, [isPasswordRevealed]);
|
||||
const { isPasswordRevealed, toggleIsPasswordRevealed } = useIsPasswordRevealed({ passwordInputId });
|
||||
|
||||
return (
|
||||
<div className={kcClsx("kcInputGroup")}>
|
||||
|
@ -1,7 +1,6 @@
|
||||
import type { JSX } from "keycloakify/tools/JSX";
|
||||
import { useEffect, useReducer } from "react";
|
||||
import { useIsPasswordRevealed } from "keycloakify/tools/useIsPasswordRevealed";
|
||||
import { kcSanitize } from "keycloakify/lib/kcSanitize";
|
||||
import { assert } from "keycloakify/tools/assert";
|
||||
import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
@ -146,15 +145,7 @@ function PasswordWrapper(props: { kcClsx: KcClsx; i18n: I18n; passwordInputId: s
|
||||
|
||||
const { msgStr } = i18n;
|
||||
|
||||
const [isPasswordRevealed, toggleIsPasswordRevealed] = useReducer((isPasswordRevealed: boolean) => !isPasswordRevealed, false);
|
||||
|
||||
useEffect(() => {
|
||||
const passwordInputElement = document.getElementById(passwordInputId);
|
||||
|
||||
assert(passwordInputElement instanceof HTMLInputElement);
|
||||
|
||||
passwordInputElement.type = isPasswordRevealed ? "text" : "password";
|
||||
}, [isPasswordRevealed]);
|
||||
const { isPasswordRevealed, toggleIsPasswordRevealed } = useIsPasswordRevealed({ passwordInputId });
|
||||
|
||||
return (
|
||||
<div className={kcClsx("kcInputGroup")}>
|
||||
|
45
src/tools/useIsPasswordRevealed.ts
Normal file
45
src/tools/useIsPasswordRevealed.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { useEffect, useReducer } from "react";
|
||||
import { assert } from "keycloakify/tools/assert";
|
||||
|
||||
/**
|
||||
* Initially false, state that enables to dynamically control if
|
||||
* the type of a password input is "password" (false) or "text" (true).
|
||||
*/
|
||||
export function useIsPasswordRevealed(params: { passwordInputId: string }) {
|
||||
const { passwordInputId } = params;
|
||||
|
||||
const [isPasswordRevealed, toggleIsPasswordRevealed] = useReducer(
|
||||
(isPasswordRevealed: boolean) => !isPasswordRevealed,
|
||||
false
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const passwordInputElement = document.getElementById(passwordInputId);
|
||||
|
||||
assert(passwordInputElement instanceof HTMLInputElement);
|
||||
|
||||
const type = isPasswordRevealed ? "text" : "password";
|
||||
|
||||
passwordInputElement.type = type;
|
||||
|
||||
const observer = new MutationObserver(mutations => {
|
||||
mutations.forEach(mutation => {
|
||||
if (mutation.attributeName !== "type") {
|
||||
return;
|
||||
}
|
||||
if (passwordInputElement.type === type) {
|
||||
return;
|
||||
}
|
||||
passwordInputElement.type = type;
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(passwordInputElement, { attributes: true });
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [isPasswordRevealed]);
|
||||
|
||||
return { isPasswordRevealed, toggleIsPasswordRevealed };
|
||||
}
|
Reference in New Issue
Block a user