From e15d0a167372b1784c1706751af0e66b0a881299 Mon Sep 17 00:00:00 2001 From: Kanika Bansal Date: Tue, 24 Dec 2024 16:57:30 +0530 Subject: [PATCH] feat: New Theme Structure and Custom Style Integration (#1958) Co-authored-by: Jeeva Ramachandran --- config/config.toml | 1 + public/hyperswitch/module.js | 74 ++++++++-- src/UIConifg/UIConfig.res | 46 ++++-- src/components/Button.res | 17 +-- src/context/ThemeProvider.res | 133 +++++++++++++++--- src/entryPoints/AuthModule/AuthWrapper.res | 2 +- .../AuthModule/PreLoginModule/AuthSelect.res | 2 +- .../PreLoginModule/ResetPassword.res | 2 +- src/entryPoints/FeatureFlagUtils.res | 2 + src/entryPoints/HyperSwitchApp.res | 2 +- src/entryPoints/HyperSwitchEntry.res | 62 ++++++-- .../configs/HyperSwitchConfigTypes.res | 59 +++++++- .../configs/HyperSwitchDefaultConfig.res | 22 +-- src/libraries/Window.res | 2 +- src/screens/Sidebar/Sidebar.res | 4 +- tailwind.config.js | 43 +++++- 16 files changed, 384 insertions(+), 89 deletions(-) diff --git a/config/config.toml b/config/config.toml index f541028d9..61e94b68f 100644 --- a/config/config.toml +++ b/config/config.toml @@ -48,6 +48,7 @@ new_analytics=false new_analytics_smart_retries=false new_analytics_refunds=false down_time=false +dev_theme_feature=false tax_processor=true x_feature_route=false tenant_user=false diff --git a/public/hyperswitch/module.js b/public/hyperswitch/module.js index 6a9ad6d36..5007decaa 100644 --- a/public/hyperswitch/module.js +++ b/public/hyperswitch/module.js @@ -213,26 +213,72 @@ const getRGBColor = (hex, type) => { return `--color-${type}: ${r}, ${g}, ${b};`; }; -function appendStyle(customStyle) { - let { primaryColor, primaryHover, sidebar } = customStyle; - let cssVariables = ` -:root { - ${getRGBColor(primaryColor, "primary")} - ${getRGBColor(primaryHover, "hover")} - ${getRGBColor(sidebar, "sidebar")} +const getRGBvalue = (hex) => { + let color = hex.replace(/#/g, ""); + if (color.length !== 6) { + color = `${color}${color}`; + } + var r = parseInt(color.substr(0, 2), 16); + var g = parseInt(color.substr(2, 2), 16); + var b = parseInt(color.substr(4, 2), 16); -} -`; - let style; + return `${r}, ${g}, ${b};`; +}; + +const toSnakeCase = (str) => { + return str + .replace(/([A-Z])/g, "-$1") + .replace(/\s+/g, "-") + .replace(/^-/, "") + .toLowerCase(); +}; + +const generateVariablesForSection = (sectionData, sectionName) => { + return ( + Object.entries(sectionData) + .map(([key, value]) => { + const processedValue = + typeof value === "string" && value.startsWith("#") + ? getRGBvalue(value) + : value; + return ` --${sectionName}-${toSnakeCase(key)}: ${processedValue};`; + }) + .join("\n") + "\n" + ); +}; + +const generateButtonVariables = (buttons) => { + return ` + /* Primary Button */ + ${generateVariablesForSection(buttons.primary, "btn-primary")} + + /* Secondary Button */ + ${generateVariablesForSection(buttons.secondary, "btn-secondary")} + `; +}; + +function appendStyle(themesConfig) { + const settings = themesConfig.settings; + let cssVariables = `:root{ + /* Colors */ + ${generateVariablesForSection(settings.colors, "colors")} + /* Typography */ + ${generateVariablesForSection(settings.typography, "base")} + /* Buttons */ + ${generateButtonVariables(settings.buttons)} + /* Borders */ + ${generateVariablesForSection(settings.borders, "borders")} + /* Spacing */ + ${generateVariablesForSection(settings.spacing, "spacing")} - if (document.getElementById("custom-style")) { - style = document.getElementById("custom-style"); +}`; + if (document.getElementById("custom-themes-style")) { + style = document.getElementById("custom-themes-style"); } else { style = document.createElement("style"); } - // let style = document.createElement("style") let text = document.createTextNode(cssVariables); - style.setAttribute("id", "custom-style"); + style.setAttribute("id", "custom-themes-style"); style.appendChild(text); document.head.appendChild(style); } diff --git a/src/UIConifg/UIConfig.res b/src/UIConifg/UIConfig.res index b26bbb2dc..546837c83 100644 --- a/src/UIConifg/UIConfig.res +++ b/src/UIConifg/UIConfig.res @@ -3,7 +3,15 @@ module ButtonConfig = { default: string, defaultPagination: string, } - type textColor = {primaryOutline: string, primaryNormal: string, primaryDisabled: string} + type textColor = { + primaryOutline: string, + primaryNormal: string, + primaryDisabled: string, + secondaryNormal: string, + secondaryLoading: string, + secondaryDisabled: string, + secondaryNoBorder: string, + } type bGcolor = { primaryNormal: string, primaryDisabled: string, @@ -18,6 +26,7 @@ module ButtonConfig = { secondaryNormal: string, secondaryLoading: string, secondaryNoHover: string, + secondaryNoBorder: string, } type border = { @@ -71,7 +80,7 @@ module BorderConfig = { } module SidebarConfig = { - type backgroundColor = {primaryNormal: string} + type backgroundColor = {sidebarNormal: string} type t = {backgroundColor: backgroundColor} } @@ -80,13 +89,14 @@ type t = { font: FontConfig.t, backgroundColor: string, primaryColor: string, + secondaryColor: string, shadow: ShadowConfig.t, border: BorderConfig.t, sidebarColor: SidebarConfig.t, } let defaultUIConfig: t = { - backgroundColor: "bg-primary", + backgroundColor: "bg-background", button: { height: { medium: "h-fit", @@ -114,19 +124,20 @@ let defaultUIConfig: t = { borderSecondaryBorderStyleClass: "border-border_gray border-opacity-20 dark:border-jp-gray-960 dark:border-opacity-100", }, backgroundColor: { - primaryNormal: "bg-primary hover:bg-primary-hover focus:outline-none", - primaryDisabled: "bg-primary opacity-60 dark:bg-jp-gray-950 dark:bg-opacity-50 border dark:border-jp-gray-disabled_border dark:border-opacity-50", - primaryNoHover: "bg-primary hover:bg-primary-hover focus:outline-none dark:text-opacity-50 text-opacity-50", - primaryLoading: "bg-primary", + primaryNormal: "bg-button-primary-bg hover:bg-button-primary-hoverbg focus:outline-none", + primaryDisabled: "bg-button-primary-bg opacity-60 dark:bg-jp-gray-950 dark:bg-opacity-50 border dark:border-jp-gray-disabled_border dark:border-opacity-50", + primaryNoHover: "bg-button-primary-bg hover:bg-button-primary-hoverbg focus:outline-none dark:text-opacity-50 text-opacity-50", + primaryLoading: "bg-button-primary-bg ", primaryOutline: "mix-blend-normal", paginationNormal: "border-left-1 opacity-80 border-right-1 font-normal border-left-1 text-jp-gray-900 text-opacity-50 hover:text-jp-gray-900 focus:outline-none", paginationLoading: "border-left-1 border-right-1 font-normal border-left-1 bg-jp-gray-200 dark:bg-jp-gray-800 dark:bg-opacity-10", paginationDisabled: "border-left-1 border-right-1 font-normal border-left-1 bg-jp-gray-300 dark:bg-jp-gray-950 dark:bg-opacity-50 border dark:border-jp-gray-disabled_border dark:border-opacity-50", paginationNoHover: "bg-white border-left-1 border-right-1 font-normal text-jp-gray-900 text-opacity-75 hover:text-jp-gray-900 dark:text-jp-gray-text_darktheme dark:text-opacity-75", dropdownDisabled: "bg-gray-200 dark:bg-jp-gray-950 dark:bg-opacity-50 border dark:border-jp-gray-disabled_border dark:border-opacity-50", - secondaryNormal: "bg-jp-gray-button_gray text-jp-gray-900 text-opacity-75 hover:bg-jp-gray-secondary_hover hover:text-jp-gray-890 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme dark:text-opacity-50 focus:outline-none", - secondaryLoading: "bg-jp-gray-button_gray dark:bg-jp-gray-darkgray_background", - secondaryNoHover: "bg-jp-gray-button_gray text-jp-gray-900 text-opacity-50 hover:bg-jp-gray-secondary_hover hover:text-jp-gray-890 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme focus:outline-none dark:text-opacity-50 ", + secondaryNormal: "bg-button-secondary-bg text-jp-gray-900 text-opacity-75 hover:bg-button-secondary-hoverbg hover:text-jp-gray-890 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme dark:text-opacity-50 focus:outline-none", + secondaryNoBorder: "hover:bg-jp-gray-lightmode_steelgray hover:bg-opacity-40 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme dark:text-opacity-50 dark:hover:bg-jp-gray-950 focus:outline-none", + secondaryLoading: "bg-button-secondary-bg dark:bg-jp-gray-darkgray_background", + secondaryNoHover: "bg-button-secondary-bg text-jp-gray-900 text-opacity-50 hover:bg-button-secondary-hoverbg hover:text-jp-gray-890 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme focus:outline-none dark:text-opacity-50 ", }, borderRadius: { default: "rounded", @@ -134,13 +145,17 @@ let defaultUIConfig: t = { }, textColor: { primaryNormal: "text-white", - primaryOutline: "text-primary", + primaryOutline: "text-button-primary-text", primaryDisabled: "text-jp-gray-600 dark:text-jp-gray-text_darktheme dark:text-opacity-25", + secondaryNormal: "text-button-secondary-text hover:text-black dark:text-jp-gray-text_darktheme dark:hover:text-jp-gray-text_darktheme dark:hover:text-opacity-75", + secondaryNoBorder: "text-jp-gray-900 ", + secondaryLoading: "text-button-secondary-text hover:text-black dark:text-jp-gray-text_darktheme dark:text-opacity-75", + secondaryDisabled: "text-jp-gray-600 dark:text-jp-gray-text_darktheme dark:text-opacity-25", }, }, font: { textColor: { - primaryNormal: "text-primary", + primaryNormal: "text-typography", }, }, shadow: { @@ -151,14 +166,15 @@ let defaultUIConfig: t = { }, border: { borderColor: { - primaryNormal: "border border-primary", - primaryFocused: "focus:border-primary", + primaryNormal: "border border-outline", + primaryFocused: "focus:border-outline", }, }, primaryColor: "primary", + secondaryColor: "secondary", sidebarColor: { backgroundColor: { - primaryNormal: "bg-primary-sidebar", + sidebarNormal: "bg-sidebar", }, }, } diff --git a/src/components/Button.res b/src/components/Button.res index 20a23d3e0..e242955d2 100644 --- a/src/components/Button.res +++ b/src/components/Button.res @@ -121,15 +121,10 @@ let useGetBgColor = ( switch buttonState { | Focused | Normal => - showBorder - ? "bg-jp-gray-button_gray text-jp-gray-900 text-opacity-75 hover:bg-jp-gray-secondary_hover hover:text-jp-gray-890 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme dark:text-opacity-50 focus:outline-none" - : "text-jp-gray-900 hover:bg-jp-gray-lightmode_steelgray hover:bg-opacity-40 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme dark:text-opacity-50 dark:hover:bg-jp-gray-950 focus:outline-none" - | Loading => - showBorder - ? "bg-jp-gray-button_gray dark:bg-jp-gray-darkgray_background" - : "bg-jp-gray-lightmode_steelgray bg-opacity-40 dark:bg-jp-gray-950 dark:bg-opacity-100" + showBorder ? buttonConfig.secondaryNormal : buttonConfig.secondaryNoBorder + | Loading => showBorder ? buttonConfig.secondaryLoading : buttonConfig.secondaryNoBorder | Disabled => showBorder ? "bg-jp-gray-300 dark:bg-gray-800 dark:bg-opacity-10" : "px-4" - | NoHover => "bg-jp-gray-button_gray text-jp-gray-900 text-opacity-50 hover:bg-jp-gray-secondary_hover hover:text-jp-gray-890 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme focus:outline-none dark:text-opacity-50 " + | NoHover => buttonConfig.secondaryNoHover } | Pill => switch buttonState { @@ -218,9 +213,9 @@ let useGetTextColor = ( } | Secondary => switch buttonState { - | Disabled => "text-jp-gray-600 dark:text-jp-gray-text_darktheme dark:text-opacity-25" - | Loading => "text-jp-gray-950 hover:text-black dark:text-jp-gray-text_darktheme dark:text-opacity-75" - | _ => "text-jp-gray-950 hover:text-black dark:text-jp-gray-text_darktheme dark:hover:text-jp-gray-text_darktheme dark:hover:text-opacity-75" + | Disabled => textConfig.secondaryDisabled + | Loading => textConfig.secondaryLoading + | _ => textConfig.secondaryNormal } | SecondaryFilled => switch buttonState { diff --git a/src/context/ThemeProvider.res b/src/context/ThemeProvider.res index 98a8b1fa8..4fa0a3830 100644 --- a/src/context/ThemeProvider.res +++ b/src/context/ThemeProvider.res @@ -6,12 +6,6 @@ type themeType = LightTheme type x = {theme: string} -type customStyle = { - primaryColor: string, - primaryHover: string, - sidebar: string, -} - type customUIConfig = { globalUIConfig: UIConfig.t, theme: theme, @@ -19,10 +13,47 @@ type customUIConfig = { configCustomDomainTheme: JSON.t => unit, } -let defaultGlobalConfig: customStyle = { - primaryColor: "#006DF9", - primaryHover: "#005ED6", - sidebar: "#242F48", +let newDefaultConfig: HyperSwitchConfigTypes.customStylesTheme = { + settings: { + colors: { + primary: "#006DF9", + secondary: "#F7F7F7", + sidebar: "#242F48", + background: "#F7F8FB", + }, + typography: { + fontFamily: "Roboto, sans-serif", + fontSize: "14px", + headingFontSize: "24px", + textColor: "#006DF9", + linkColor: "#3498db", + linkHoverColor: "#005ED6", + }, + buttons: { + primary: { + backgroundColor: "#006DF9", + textColor: "#006df9", + hoverBackgroundColor: "#005ED6", + }, + secondary: { + backgroundColor: "#F7F7F7", + textColor: "#202124", + hoverBackgroundColor: "#EEEEEE", + }, + }, + borders: { + defaultRadius: "4px", + borderColor: "#006DF9", + }, + spacing: { + padding: "16px", + margin: "16px", + }, + }, + urls: { + faviconUrl: None, + logoUrl: None, + }, } let themeContext = { @@ -75,14 +106,84 @@ let make = (~children) => { | Dark => "dark" | Light => "" } + let configCustomDomainTheme = React.useCallback((uiConfg: JSON.t) => { open LogicUtils - let dict = uiConfg->getDictFromJsonObject->getDictfromDict("theme") - let {primaryColor, primaryHover, sidebar} = defaultGlobalConfig - let value: HyperSwitchConfigTypes.customStyle = { - primaryColor: dict->getString("primary_color", primaryColor), - primaryHover: dict->getString("primary_hover_color", primaryHover), - sidebar: dict->getString("sidebar_color", sidebar), + let dict = uiConfg->getDictFromJsonObject + let settings = dict->getDictfromDict("settings") + let url = dict->getDictfromDict("urls") + let colorsConfig = settings->getDictfromDict("colors") + let typography = settings->getDictfromDict("typography") + let borders = settings->getDictfromDict("borders") + let spacing = settings->getDictfromDict("spacing") + let colorsBtnPrimary = settings->getDictfromDict("buttons")->getDictfromDict("primary") + let colorsBtnSecondary = settings->getDictfromDict("buttons")->getDictfromDict("secondary") + let {settings: defaultSettings, _} = newDefaultConfig + let value: HyperSwitchConfigTypes.customStylesTheme = { + settings: { + colors: { + primary: colorsConfig->getString("primary", defaultSettings.colors.primary), + secondary: colorsConfig->getString("secondary", defaultSettings.colors.secondary), + sidebar: colorsConfig->getString("sidebar", defaultSettings.colors.sidebar), + background: colorsConfig->getString("background", defaultSettings.colors.background), + }, + typography: { + fontFamily: typography->getString("fontFamily", defaultSettings.typography.fontFamily), + fontSize: typography->getString("fontSize", defaultSettings.typography.fontSize), + headingFontSize: typography->getString( + "headingFontSize", + defaultSettings.typography.headingFontSize, + ), + textColor: typography->getString("textColor", defaultSettings.typography.textColor), + linkColor: typography->getString("linkColor", defaultSettings.typography.linkColor), + linkHoverColor: typography->getString( + "linkHoverColor", + defaultSettings.typography.linkHoverColor, + ), + }, + buttons: { + primary: { + backgroundColor: colorsBtnPrimary->getString( + "backgroundColor", + defaultSettings.buttons.primary.backgroundColor, + ), + textColor: colorsBtnPrimary->getString( + "textColor", + defaultSettings.buttons.primary.textColor, + ), + hoverBackgroundColor: colorsBtnPrimary->getString( + "hoverBackgroundColor", + defaultSettings.buttons.primary.hoverBackgroundColor, + ), + }, + secondary: { + backgroundColor: colorsBtnSecondary->getString( + "backgroundColor", + defaultSettings.buttons.secondary.backgroundColor, + ), + textColor: colorsBtnSecondary->getString( + "textColor", + defaultSettings.buttons.secondary.textColor, + ), + hoverBackgroundColor: colorsBtnSecondary->getString( + "hoverBackgroundColor", + defaultSettings.buttons.secondary.hoverBackgroundColor, + ), + }, + }, + borders: { + defaultRadius: borders->getString("defaultRadius", defaultSettings.borders.defaultRadius), + borderColor: borders->getString("borderColor", defaultSettings.borders.borderColor), + }, + spacing: { + padding: spacing->getString("padding", defaultSettings.spacing.padding), + margin: spacing->getString("margin", defaultSettings.spacing.margin), + }, + }, + urls: { + faviconUrl: url->getOptionString("faviconUrl"), + logoUrl: url->getOptionString("logoUrl"), + }, } Window.appendStyle(value) }, []) diff --git a/src/entryPoints/AuthModule/AuthWrapper.res b/src/entryPoints/AuthModule/AuthWrapper.res index 04b1e2af2..2bd7ebf50 100644 --- a/src/entryPoints/AuthModule/AuthWrapper.res +++ b/src/entryPoints/AuthModule/AuthWrapper.res @@ -5,7 +5,7 @@ module AuthHeaderWrapper = { open CommonAuthTypes let {branding} = HyperswitchAtom.featureFlagAtom->Recoil.useRecoilValueFromAtom - let (logoVariant, iconUrl) = switch (Window.env.logoUrl, branding) { + let (logoVariant, iconUrl) = switch (Window.env.urlThemeConfig.logoUrl, branding) { | (Some(url), true) => (IconWithURL, Some(url)) | (Some(url), false) => (IconWithURL, Some(url)) | _ => (IconWithText, None) diff --git a/src/entryPoints/AuthModule/PreLoginModule/AuthSelect.res b/src/entryPoints/AuthModule/PreLoginModule/AuthSelect.res index 787c20099..bd1be0179 100644 --- a/src/entryPoints/AuthModule/PreLoginModule/AuthSelect.res +++ b/src/entryPoints/AuthModule/PreLoginModule/AuthSelect.res @@ -11,7 +11,7 @@ let make = (~setSelectedAuthId) => { let {fetchAuthMethods} = AuthModuleHooks.useAuthMethods() let updateDetails = useUpdateMethod() let (screenState, setScreenState) = React.useState(_ => PageLoaderWrapper.Success) - let (logoVariant, iconUrl) = switch Window.env.logoUrl { + let (logoVariant, iconUrl) = switch Window.env.urlThemeConfig.logoUrl { | Some(url) => (IconWithURL, Some(url)) | _ => (IconWithText, None) } diff --git a/src/entryPoints/AuthModule/PreLoginModule/ResetPassword.res b/src/entryPoints/AuthModule/PreLoginModule/ResetPassword.res index a48fd15f2..31e135b43 100644 --- a/src/entryPoints/AuthModule/PreLoginModule/ResetPassword.res +++ b/src/entryPoints/AuthModule/PreLoginModule/ResetPassword.res @@ -45,7 +45,7 @@ let make = (~flowType) => { let {branding} = HyperswitchAtom.featureFlagAtom->Recoil.useRecoilValueFromAtom - let (logoVariant, iconUrl) = switch (Window.env.logoUrl, branding) { + let (logoVariant, iconUrl) = switch (Window.env.urlThemeConfig.logoUrl, branding) { | (Some(url), true) => (IconWithURL, Some(url)) | (Some(url), false) => (IconWithURL, Some(url)) | _ => (IconWithText, None) diff --git a/src/entryPoints/FeatureFlagUtils.res b/src/entryPoints/FeatureFlagUtils.res index 49b7a0fb9..ceefb5d7c 100644 --- a/src/entryPoints/FeatureFlagUtils.res +++ b/src/entryPoints/FeatureFlagUtils.res @@ -44,6 +44,7 @@ type featureFlag = { xFeatureRoute: bool, tenantUser: bool, clickToPay: bool, + devThemeFeature: bool, devReconv2Product: bool, } @@ -91,6 +92,7 @@ let featureFlagType = (featureFlags: JSON.t) => { taxProcessor: dict->getBool("tax_processor", false), xFeatureRoute: dict->getBool("x_feature_route", false), tenantUser: dict->getBool("tenant_user", false), + devThemeFeature: dict->getBool("dev_theme_feature", false), devReconv2Product: dict->getBool("dev_recon_v2_product", false), } } diff --git a/src/entryPoints/HyperSwitchApp.res b/src/entryPoints/HyperSwitchApp.res index 2ea787649..dd4a9ac6c 100644 --- a/src/entryPoints/HyperSwitchApp.res +++ b/src/entryPoints/HyperSwitchApp.res @@ -128,7 +128,7 @@ let make = () => { } - headerLeftActions={switch Window.env.logoUrl { + headerLeftActions={switch Window.env.urlThemeConfig.logoUrl { | Some(url) => <> image diff --git a/src/entryPoints/HyperSwitchEntry.res b/src/entryPoints/HyperSwitchEntry.res index c79afb7f8..4456f5ee6 100644 --- a/src/entryPoints/HyperSwitchEntry.res +++ b/src/entryPoints/HyperSwitchEntry.res @@ -7,7 +7,6 @@ module HyperSwitchEntryComponent = { let setFeatureFlag = HyperswitchAtom.featureFlagAtom->Recoil.useSetRecoilState let (screenState, setScreenState) = React.useState(_ => PageLoaderWrapper.Loading) let {configCustomDomainTheme} = React.useContext(ThemeProvider.themeContext) - let configureFavIcon = (faviconUrl: option) => { try { open DOMUtils @@ -21,16 +20,30 @@ module HyperSwitchEntryComponent = { } } - let configURL = (urlConfig: JSON.t) => { + let configThemeURL = (~configData: JSON.t, ~themesData: JSON.t, devThemeFeature) => { open LogicUtils open HyperSwitchConfigTypes try { - let dict = urlConfig->getDictFromJsonObject->getDictfromDict("endpoints") + let dict = configData->getDictFromJsonObject->getDictfromDict("endpoints") + let urlvalues = { + if !devThemeFeature { + let val = { + faviconUrl: dict->getString("favicon_url", "")->getNonEmptyString, + logoUrl: dict->getString("logo_url", "")->getNonEmptyString, + } + val + } else { + let urlsDict = themesData->getDictFromJsonObject->getDictfromDict("urls") + let val = { + faviconUrl: urlsDict->getString("faviconUrl", "")->getNonEmptyString, + logoUrl: urlsDict->getString("logoUrl", "")->getNonEmptyString, + } + val + } + } let value: urlConfig = { apiBaseUrl: dict->getString("api_url", ""), mixpanelToken: dict->getString("mixpanel_token", ""), - faviconUrl: dict->getString("favicon_url", "")->getNonEmptyString, - logoUrl: dict->getString("logo_url", "")->getNonEmptyString, sdkBaseUrl: dict->getString("sdk_url", "")->getNonEmptyString, agreementUrl: dict->getString("agreement_url", "")->getNonEmptyString, dssCertificateUrl: dict->getString("dss_certificate_url", "")->getNonEmptyString, @@ -39,24 +52,55 @@ module HyperSwitchEntryComponent = { ->getNonEmptyString, agreementVersion: dict->getString("agreement_version", "")->getNonEmptyString, reconIframeUrl: dict->getString("recon_iframe_url", "")->getNonEmptyString, + urlThemeConfig: urlvalues, } DOMUtils.window._env_ = value - configureFavIcon(value.faviconUrl)->ignore + configureFavIcon(value.urlThemeConfig.faviconUrl)->ignore } catch { | _ => Exn.raiseError("Error on configuring endpoint") } } - // Need to modify based on the usedcase let fetchConfig = async () => { try { + open LogicUtils let domain = HyperSwitchEntryUtils.getSessionData(~key="domain", ~defaultValue="default") let apiURL = `${GlobalVars.getHostUrlWithBasePath}/config/feature?domain=${domain}` let res = await fetchDetails(apiURL) let featureFlags = res->FeatureFlagUtils.featureFlagType setFeatureFlag(_ => featureFlags) - let _ = res->configCustomDomainTheme - let _ = res->configURL + let devThemeFeature = featureFlags.devThemeFeature + let themeJson = if !devThemeFeature { + let dict = res->getDictFromJsonObject->getDictfromDict("theme") + let defaultStyle = { + "settings": { + "colors": { + "primary": dict->getString("primary_color", ""), + "sidebar": dict->getString("sidebar_color", ""), + }, + "buttons": { + "primary": { + "backgroundColor": dict->getString("primary_color", ""), + "textColor": dict->getString("primary_color", ""), + "hoverBackgroundColor": dict->getString("primary_hover_color", ""), + }, + }, + }, + } + let _ = configThemeURL(~configData={res}, ~themesData=JSON.Encode.null, devThemeFeature) + defaultStyle->Identity.genericTypeToJson + } else { + try { + // make a API to fetch the theme + //call configThemeURL with the response of themes api as themesData + // let _ = configThemeURL(~configData={res}, ~themesData=themesData, devThemeFeature) + JSON.Encode.null + } catch { + | _ => JSON.Encode.null + } + } + let _ = themeJson->configCustomDomainTheme + // Delay added on Expecting feature flag recoil gets updated await HyperSwitchUtils.delay(1000) setScreenState(_ => PageLoaderWrapper.Success) diff --git a/src/entryPoints/configs/HyperSwitchConfigTypes.res b/src/entryPoints/configs/HyperSwitchConfigTypes.res index 566b2f95a..946e20d03 100644 --- a/src/entryPoints/configs/HyperSwitchConfigTypes.res +++ b/src/entryPoints/configs/HyperSwitchConfigTypes.res @@ -1,18 +1,67 @@ +type urlThemeConfig = { + faviconUrl: option, + logoUrl: option, +} type urlConfig = { apiBaseUrl: string, mixpanelToken: string, - faviconUrl: option, - logoUrl: option, sdkBaseUrl: option, agreementUrl: option, agreementVersion: option, applePayCertificateUrl: option, reconIframeUrl: option, dssCertificateUrl: option, + urlThemeConfig: urlThemeConfig, } -type customStyle = { - primaryColor: string, - primaryHover: string, +// themes struct type + +type colorPalette = { + primary: string, + secondary: string, sidebar: string, + background: string, +} + +type typographyConfig = { + fontFamily: string, + fontSize: string, + headingFontSize: string, + textColor: string, + linkColor: string, + linkHoverColor: string, +} + +type buttonStyleConfig = { + backgroundColor: string, + textColor: string, + hoverBackgroundColor: string, +} + +type borderConfig = { + defaultRadius: string, + borderColor: string, +} + +type spacingConfig = { + padding: string, + margin: string, +} + +type buttonConfig = { + primary: buttonStyleConfig, + secondary: buttonStyleConfig, +} + +type themeSettings = { + colors: colorPalette, + typography: typographyConfig, + buttons: buttonConfig, + borders: borderConfig, + spacing: spacingConfig, +} + +type customStylesTheme = { + settings: themeSettings, + urls: urlThemeConfig, } diff --git a/src/entryPoints/configs/HyperSwitchDefaultConfig.res b/src/entryPoints/configs/HyperSwitchDefaultConfig.res index e061dd12a..ec580aef8 100644 --- a/src/entryPoints/configs/HyperSwitchDefaultConfig.res +++ b/src/entryPoints/configs/HyperSwitchDefaultConfig.res @@ -1,5 +1,6 @@ let config: UIConfig.t = { primaryColor: "primary", + secondaryColor: "secondary", backgroundColor: "bg-primary", button: { height: { @@ -28,19 +29,20 @@ let config: UIConfig.t = { borderSecondaryBorderStyleClass: "border-border_gray border-opacity-20 dark:border-jp-gray-960 dark:border-opacity-100", }, backgroundColor: { - primaryNormal: "bg-primary hover:bg-primary-hover focus:outline-none", - primaryDisabled: "bg-primary opacity-60 dark:bg-jp-gray-950 dark:bg-opacity-50 border dark:border-jp-gray-disabled_border dark:border-opacity-50", - primaryNoHover: "bg-primary hover:bg-primary-hover focus:outline-none dark:text-opacity-50 text-opacity-50", - primaryLoading: "bg-primary", + primaryNormal: "bg-button-primary-bg hover:bg-button-primary-hoverbg focus:outline-none", + primaryDisabled: "bg-button-primary-bg opacity-60 dark:bg-jp-gray-950 dark:bg-opacity-50 border dark:border-jp-gray-disabled_border dark:border-opacity-50", + primaryNoHover: "bg-button-primary-bg hover:bg-button-primary-hoverbg focus:outline-none dark:text-opacity-50 text-opacity-50", + primaryLoading: "bg-button-primary-bg ", primaryOutline: "mix-blend-normal", paginationNormal: "border-left-1 opacity-80 border-right-1 font-normal border-left-1 text-jp-gray-900 text-opacity-50 hover:text-jp-gray-900 focus:outline-none", paginationLoading: "border-left-1 border-right-1 font-normal border-left-1 bg-jp-gray-200 dark:bg-jp-gray-800 dark:bg-opacity-10", paginationDisabled: "border-left-1 border-right-1 font-normal border-left-1 bg-jp-gray-300 dark:bg-jp-gray-950 dark:bg-opacity-50 border dark:border-jp-gray-disabled_border dark:border-opacity-50", paginationNoHover: "bg-white border-left-1 border-right-1 font-normal text-jp-gray-900 text-opacity-75 hover:text-jp-gray-900 dark:text-jp-gray-text_darktheme dark:text-opacity-75", dropdownDisabled: "bg-gray-200 dark:bg-jp-gray-950 dark:bg-opacity-50 border dark:border-jp-gray-disabled_border dark:border-opacity-50", - secondaryNormal: "bg-jp-gray-button_gray text-jp-gray-900 text-opacity-75 hover:bg-jp-gray-secondary_hover hover:text-jp-gray-890 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme dark:text-opacity-50 focus:outline-none", - secondaryLoading: "bg-jp-gray-button_gray dark:bg-jp-gray-darkgray_background", - secondaryNoHover: "bg-jp-gray-button_gray text-jp-gray-900 text-opacity-50 hover:bg-jp-gray-secondary_hover hover:text-jp-gray-890 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme focus:outline-none dark:text-opacity-50 ", + secondaryNormal: "bg-button-secondary-bg text-jp-gray-900 text-opacity-75 hover:bg-button-secondary-hoverbg hover:text-jp-gray-890 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme dark:text-opacity-50 focus:outline-none", + secondaryNoBorder: "hover:bg-jp-gray-lightmode_steelgray hover:bg-opacity-40 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme dark:text-opacity-50 dark:hover:bg-jp-gray-950 focus:outline-none", + secondaryLoading: "bg-button-secondary-bg dark:bg-jp-gray-darkgray_background", + secondaryNoHover: "bg-button-secondary-bg text-jp-gray-900 text-opacity-50 hover:bg-button-secondary-hoverbg hover:text-jp-gray-890 dark:bg-jp-gray-darkgray_background dark:text-jp-gray-text_darktheme focus:outline-none dark:text-opacity-50 ", }, borderRadius: { default: "rounded", @@ -50,6 +52,10 @@ let config: UIConfig.t = { primaryNormal: "text-white", primaryOutline: "text-blue-500", primaryDisabled: "text-jp-gray-600 dark:text-jp-gray-text_darktheme dark:text-opacity-25", + secondaryNormal: "text-button-secondary-text hover:text-black dark:text-jp-gray-text_darktheme dark:hover:text-jp-gray-text_darktheme dark:hover:text-opacity-75", + secondaryNoBorder: "text-jp-gray-900 ", + secondaryLoading: "text-button-secondary-text hover:text-black dark:text-jp-gray-text_darktheme dark:text-opacity-75", + secondaryDisabled: "text-jp-gray-600 dark:text-jp-gray-text_darktheme dark:text-opacity-25", }, }, font: { @@ -71,7 +77,7 @@ let config: UIConfig.t = { }, sidebarColor: { backgroundColor: { - primaryNormal: "bg-primary-sidebar", + sidebarNormal: "bg-sidebar", }, }, } diff --git a/src/libraries/Window.res b/src/libraries/Window.res index 296a97197..546122b55 100644 --- a/src/libraries/Window.res +++ b/src/libraries/Window.res @@ -253,7 +253,7 @@ type boundingClient = {x: int, y: int, width: int, height: int} @send external getBoundingClientRect: Dom.element => boundingClient = "getBoundingClientRect" @val @scope("window") -external appendStyle: HyperSwitchConfigTypes.customStyle => unit = "appendStyle" +external appendStyle: HyperSwitchConfigTypes.customStylesTheme => unit = "appendStyle" @val @scope("window") external env: HyperSwitchConfigTypes.urlConfig = "_env_" diff --git a/src/screens/Sidebar/Sidebar.res b/src/screens/Sidebar/Sidebar.res index ad5c758ab..9f9fe0246 100644 --- a/src/screens/Sidebar/Sidebar.res +++ b/src/screens/Sidebar/Sidebar.res @@ -524,7 +524,7 @@ let make = ( `
+ className={`${backgroundColor.sidebarNormal} flex group border-r border-jp-gray-500 relative`}>
ReactDOM.Ref.domRef} className={`flex h-full flex-col transition-all duration-100 ${sidebarClass} relative inset-0`} @@ -534,7 +534,7 @@ let make = ( className={`absolute z-40 h-screen flex ${transformClass} duration-300 ease-in-out ${sidebarMaxWidth} ${expansionClass}`}>
ReactDOM.Ref.domRef} - className={`${backgroundColor.primaryNormal} flex h-full flex-col transition-all duration-100 ${sidebarClass} relative inset-0`} + className={`${backgroundColor.sidebarNormal} flex h-full flex-col transition-all duration-100 ${sidebarClass} relative inset-0`} style={width: sidebarWidth}>
diff --git a/tailwind.config.js b/tailwind.config.js index 280a9a106..62318e8d2 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -89,6 +89,8 @@ module.exports = { "0px 1px 2px 0px rgba(0, 0, 0, 0.05), 0px 0px 0px 4px rgba(232, 243, 255, 1)", }, fontSize: { + base: "var(--base-font-size)", + heading: "var(--base-heading-font-size)", "fs-10": "10px", "fs-11": "11px", "fs-13": "13px", @@ -101,11 +103,38 @@ module.exports = { }, colors: { primary: { - DEFAULT: withOpacity("--color-primary"), // Default primary color - hover: withOpacity("--color-hover"), - sidebar: withOpacity("--color-sidebar"), - custom: "#006DF9", // Custom primary color + DEFAULT: withOpacity("--colors-primary"), + custom: "#006DF9", }, + secondary: { + DEFAULT: withOpacity("--colors-secondary"), + hover: withOpacity("--btn-secondary-hover-background-color"), + }, + sidebar: { + DEFAULT: withOpacity("--colors-sidebar"), + }, + + background: { + DEFAULT: withOpacity("--colors-background"), + }, + typography: { + DEFAULT: withOpacity("--base-text-color"), + link: withOpacity("--base-link-color"), + link_hover: withOpacity("--base-link-hover-color"), + }, + button: { + primary: { + bg: withOpacity("--btn-primary-background-color"), + text: withOpacity("--btn-primary-text-color"), + hoverbg: withOpacity("--btn-primary-hover-background-color"), + }, + secondary: { + bg: withOpacity("--btn-secondary-background-color"), + text: withOpacity("--btn-secondary-text-color"), + hoverbg: withOpacity("--btn-secondary-hover-background-color"), + }, + }, + outline: withOpacity("--borders-border-color"), blue: { 100: "#F1F2F4", 200: "#DAECFF", @@ -234,6 +263,12 @@ module.exports = { light_white: "#FFFFFF0D", unselected_white: "#9197A3", }, + borderRadius: { + DEFAULT: "var(--borders-default-radius)", + }, + spacing: { + DEFAULT: "var(--spacing-padding)", + }, }, }, plugins: [