From 195ea98fb29ffde1e88bc6d2006eba8660cbc0d5 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Fri, 11 Apr 2025 19:37:30 +0200 Subject: [PATCH] added save as dialog (#38820) fixes: #37717 Signed-off-by: Erik Jan de Wit --- .../admin/messages/messages_en.properties | 2 + .../realm-settings/themes/FileNameDialog.tsx | 39 ++++++ .../src/realm-settings/themes/ThemeColors.tsx | 111 ++++++++++-------- .../src/realm-settings/themes/ThemesTab.tsx | 5 +- 4 files changed, 107 insertions(+), 50 deletions(-) create mode 100644 js/apps/admin-ui/src/realm-settings/themes/FileNameDialog.tsx diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties index b5f7fa8d253..1fabe634f94 100644 --- a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties @@ -3473,3 +3473,5 @@ authTokenClientSecret=Auth Token Client Secret enableDebugSMTP=Enable Debug SMTP signatureMaxExp=Max expiration signatureMaxExpHelp=Maximum expiration allowed for the JWT. Tokens need to be generated right before authentication. After this period will be considered invalid because they are too old. If undefined the default value is 60 seconds. +fileNameDialogTitle=Save as +fileName=File name \ No newline at end of file diff --git a/js/apps/admin-ui/src/realm-settings/themes/FileNameDialog.tsx b/js/apps/admin-ui/src/realm-settings/themes/FileNameDialog.tsx new file mode 100644 index 00000000000..d42988c9fbd --- /dev/null +++ b/js/apps/admin-ui/src/realm-settings/themes/FileNameDialog.tsx @@ -0,0 +1,39 @@ +import { TextControl } from "@keycloak/keycloak-ui-shared"; +import { Form } from "@patternfly/react-core"; +import { ConfirmDialogModal } from "../../components/confirm-dialog/ConfirmDialog"; +import { useTranslation } from "react-i18next"; +import { FormProvider, useForm } from "react-hook-form"; + +type FileNameDialogProps = { + onSave: (fileName: string) => void; + onClose: () => void; +}; + +type FormValues = { + fileName: string; +}; +export const FileNameDialog = ({ onSave, onClose }: FileNameDialogProps) => { + const { t } = useTranslation(); + const form = useForm(); + const { handleSubmit } = form; + + const save = ({ fileName }: FormValues) => onSave(fileName); + return ( + handleSubmit(save)()} + > +
+ + + +
+
+ ); +}; diff --git a/js/apps/admin-ui/src/realm-settings/themes/ThemeColors.tsx b/js/apps/admin-ui/src/realm-settings/themes/ThemeColors.tsx index 3dca6970e98..3a37c4d1d19 100644 --- a/js/apps/admin-ui/src/realm-settings/themes/ThemeColors.tsx +++ b/js/apps/admin-ui/src/realm-settings/themes/ThemeColors.tsx @@ -23,6 +23,8 @@ import { import { useTranslation } from "react-i18next"; import { FixedButtonsGroup } from "../../components/form/FixedButtonGroup"; import { FormAccess } from "../../components/form/FormAccess"; +import useToggle from "../../utils/useToggle"; +import { FileNameDialog } from "./FileNameDialog"; import { ImageUpload } from "./ImageUpload"; import { usePreviewLogo } from "./LogoContext"; import { darkTheme, lightTheme } from "./PatternflyVars"; @@ -81,6 +83,7 @@ export const ThemeColors = ({ realm, save, theme }: ThemeColorsProps) => { const { handleSubmit, watch } = form; const style = watch(); const contextLogo = usePreviewLogo(); + const [open, toggle, setOpen] = useToggle(); const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); const mapping = useMemo( @@ -116,6 +119,7 @@ export const ThemeColors = ({ realm, save, theme }: ThemeColorsProps) => { favicon: values.favicon as File, logo: values.logo as File, bgimage: values.bgimage as File, + fileName: values.name as string, attributes: { ...realm.attributes, style: JSON.stringify({ @@ -135,54 +139,65 @@ export const ThemeColors = ({ realm, save, theme }: ThemeColorsProps) => { }, [realm]); return ( - - - {t("themeColorInfo")} - - {mediaQuery.matches && theme === "light" && ( - + <> + {open && ( + { + handleSubmit((data) => convert({ ...data, name }))(); + setOpen(false); + }} + onClose={toggle} + /> )} - - - - - - - - - contextLogo?.setLogo(logo)} - /> - - - - - {mapping.map((m) => ( - - ))} - - - - - - - - - - - + + + {t("themeColorInfo")} + + {mediaQuery.matches && theme === "light" && ( + + )} + + + + + + + + + contextLogo?.setLogo(logo)} + /> + + + + + {mapping.map((m) => ( + + ))} + + + + + + + + + + + + ); }; diff --git a/js/apps/admin-ui/src/realm-settings/themes/ThemesTab.tsx b/js/apps/admin-ui/src/realm-settings/themes/ThemesTab.tsx index 51bf99cb452..4dbcb2a49e6 100644 --- a/js/apps/admin-ui/src/realm-settings/themes/ThemesTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/themes/ThemesTab.tsx @@ -21,6 +21,7 @@ type ThemesTabProps = { }; export type ThemeRealmRepresentation = RealmRepresentation & { + fileName?: string; favicon?: File; logo?: File; bgimage?: File; @@ -37,7 +38,7 @@ export default function ThemesTab({ realm, save }: ThemesTabProps) { const styles = JSON.parse(realm.attributes?.style ?? "{}"); - const { favicon, logo, bgimage, ...rest } = realm; + const { favicon, logo, bgimage, fileName, ...rest } = realm; const logoName = "img/logo" + logo?.name?.substring(logo?.name?.lastIndexOf(".")); @@ -135,7 +136,7 @@ styles=css/login.css css/theme-styles.css const url = URL.createObjectURL(content); const a = document.createElement("a"); a.href = url; - a.download = "quick-theme.jar"; + a.download = fileName || "quick-theme.jar"; a.click(); URL.revokeObjectURL(url); });