From 90f00358d3b36a054826b686ec08176c312e2dcb Mon Sep 17 00:00:00 2001 From: Riley Love Date: Thu, 25 Jan 2024 15:23:24 -0600 Subject: [PATCH] refactor(theming and darkmode): better themeing with basic css variables and dark mode support --- packages/components/src/index.html | 2 - packages/components/src/index.tsx | 14 +++-- packages/react-inbox-next/README.md | 2 - .../src/components/Inbox/index.tsx | 12 +++-- .../src/components/Messages2.0/Header.tsx | 13 ++--- .../Messages2.0/LoadingMessages.tsx | 2 +- .../src/components/Messages2.0/Message.tsx | 3 +- .../components/Messages2.0/actions/Close.tsx | 4 +- .../Messages2.0/actions/MarkUnread.tsx | 4 +- .../components/Messages2.0/actions/index.tsx | 2 +- .../src/components/Messages2.0/index.tsx | 4 +- .../src/components/Messages2.0/styled.tsx | 35 ++----------- packages/react-inbox/src/types.ts | 4 ++ .../src/components/PreferenceList.tsx | 12 ++--- .../src/components/PreferencesV4.tsx | 5 +- packages/react-provider/README.md | 44 ++++++++++++++++ packages/react-provider/docs/2.dark-mode.md | 40 ++++++++++++++ packages/react-provider/src/index.tsx | 52 ++++++++++++++++--- packages/react-provider/src/theme.ts | 15 ++++++ packages/react-provider/src/types.ts | 15 ++++++ .../react-toast/src/components/Body/styled.ts | 14 ++--- .../src/components/Toast/styled.ts | 1 + 22 files changed, 216 insertions(+), 83 deletions(-) create mode 100644 packages/react-provider/docs/2.dark-mode.md create mode 100644 packages/react-provider/src/theme.ts diff --git a/packages/components/src/index.html b/packages/components/src/index.html index 79ef2ef4..dc1406b2 100644 --- a/packages/components/src/index.html +++ b/packages/components/src/index.html @@ -279,7 +279,6 @@ url: "wss://20en15n3ng.execute-api.us-east-1.amazonaws.com/dev", }, clientKey: "ZDJmNTVhMWQtYmZjMS00ODg0LThhMjQtZDg0ZDg5ZTlhN2Ri", - // JWT SMOKEY //clientKey: "YzQzMGM3OGYtYzQ2NC00NTdjLThkM2QtNWIwYzI4OTdmYmE5", //userId: "smokey12345", @@ -339,7 +338,6 @@ -
hello world
diff --git a/packages/components/src/index.tsx b/packages/components/src/index.tsx index 6dfe2111..4f807bf6 100644 --- a/packages/components/src/index.tsx +++ b/packages/components/src/index.tsx @@ -2,10 +2,11 @@ import React from "react"; import { render, unmountComponentAtNode } from "react-dom"; import { - CourierProvider, - WSOptions, Brand, + CourierProvider, Interceptor, + ProviderTheme, + WSOptions, } from "@trycourier/react-provider"; import { CourierComponents } from "./components"; import { InboxProps } from "@trycourier/react-inbox"; @@ -66,6 +67,7 @@ interface ICourierConfig { clientKey: string; enableMutationObserver?: boolean; inboxApiUrl?: string; + theme?: ProviderTheme; onRouteChange?: (route: string) => void; onMessage?: Interceptor; components?: { @@ -87,7 +89,6 @@ interface ICourierConfig { const initCourier = (courierConfig?: ICourierConfig) => { const { - tenantId, apiUrl, authorization, brandId, @@ -95,6 +96,8 @@ const initCourier = (courierConfig?: ICourierConfig) => { inboxApiUrl, onMessage, onRouteChange, + tenantId, + theme, userId, userSignature, wsOptions, @@ -119,18 +122,19 @@ const initCourier = (courierConfig?: ICourierConfig) => { render( , diff --git a/packages/react-inbox-next/README.md b/packages/react-inbox-next/README.md index 1f7ca894..e69de29b 100644 --- a/packages/react-inbox-next/README.md +++ b/packages/react-inbox-next/README.md @@ -1,2 +0,0 @@ - - diff --git a/packages/react-inbox/src/components/Inbox/index.tsx b/packages/react-inbox/src/components/Inbox/index.tsx index ca2c72cc..b736276f 100644 --- a/packages/react-inbox/src/components/Inbox/index.tsx +++ b/packages/react-inbox/src/components/Inbox/index.tsx @@ -266,6 +266,12 @@ const Inbox: React.FunctionComponent = (props) => { : bell; }; + const theme = useMemo(() => { + return deepExtend({}, props.theme ?? {}, { + brand, + }); + }, [props.theme, brand]); + if (!courierContext?.inbox) { return null; } @@ -273,11 +279,7 @@ const Inbox: React.FunctionComponent = (props) => { return ( <> - + {tippyProps.visible ? ( <> {isMobile ? ( diff --git a/packages/react-inbox/src/components/Messages2.0/Header.tsx b/packages/react-inbox/src/components/Messages2.0/Header.tsx index cc35091f..4d1e23ca 100644 --- a/packages/react-inbox/src/components/Messages2.0/Header.tsx +++ b/packages/react-inbox/src/components/Messages2.0/Header.tsx @@ -35,9 +35,9 @@ const Container = styled.div<{ view?: string }>(({ theme }) => { fontSize: 16, fontWeight: 700, height: "42px", - color: "rgb(36, 50, 75)", - backgroundColor: "#F2F6F9", - borderBottom: "1px solid rgb(222, 232, 240)", + background: "var(--ci-background)", + borderBottom: "1px solid", + borderColor: "var(--ci-structure)", borderTopLeftRadius: theme?.brand?.inapp?.borderRadius ?? "12px", borderTopRightRadius: theme?.brand?.inapp?.borderRadius ?? "12px", @@ -82,10 +82,11 @@ const DropdownOptionButton = styled.button<{ const cssProps = { background: active ? primaryColor : "transparent", border: "none", - borderBottom: selected ? undefined : "1px solid #DEE8F0", + borderBottom: selected ? undefined : "1px solid", + borderColor: "var(--ci-structure)", cursor: disabled ? "default" : "pointer", padding: selected ? "6px" : "12px", - color: active ? "white" : "rgba(28, 39, 58, 1)", + color: active ? "white" : "var(--ci-title-color)", fontWeight: active || selected ? 700 : 400, lineHeight: "21px", fontSize: "14px", @@ -156,7 +157,7 @@ const HeadingDropdownOptions = styled.div(({ theme }) => { position: "absolute", top: "42px", left: 0, - background: "white", + background: "var(--ci-background)", width: "100%", zIndex: 2, height: "343px", diff --git a/packages/react-inbox/src/components/Messages2.0/LoadingMessages.tsx b/packages/react-inbox/src/components/Messages2.0/LoadingMessages.tsx index cf1d129b..e0a760e0 100644 --- a/packages/react-inbox/src/components/Messages2.0/LoadingMessages.tsx +++ b/packages/react-inbox/src/components/Messages2.0/LoadingMessages.tsx @@ -12,7 +12,7 @@ const Styled = styled.div(({ theme }) => { width: "100%", height: "100%", - background: "white", + background: "var(--ci-background)", fontSize: 14, fontWeight: 600, color: theme?.brand?.inapp?.emptyState?.textColor ?? "inherit", diff --git a/packages/react-inbox/src/components/Messages2.0/Message.tsx b/packages/react-inbox/src/components/Messages2.0/Message.tsx index 92ded5f4..117b574d 100644 --- a/packages/react-inbox/src/components/Messages2.0/Message.tsx +++ b/packages/react-inbox/src/components/Messages2.0/Message.tsx @@ -70,7 +70,8 @@ const MessageContainer = styled.div(({ theme }) => { position: "relative", padding: "12px", minHeight: 60, - borderBottom: "1px solid rgb(222, 232, 240)", + borderBottom: "1px solid", + borderColor: "var(--ci-structure)", "&.read": { ".icon": { filter: "grayscale(100%)", diff --git a/packages/react-inbox/src/components/Messages2.0/actions/Close.tsx b/packages/react-inbox/src/components/Messages2.0/actions/Close.tsx index 38ebce4e..84a4c2f7 100644 --- a/packages/react-inbox/src/components/Messages2.0/actions/Close.tsx +++ b/packages/react-inbox/src/components/Messages2.0/actions/Close.tsx @@ -31,7 +31,7 @@ const CloseAction: React.FunctionComponent<{ > ) : ( @@ -44,7 +44,7 @@ const CloseAction: React.FunctionComponent<{ > )} diff --git a/packages/react-inbox/src/components/Messages2.0/actions/MarkUnread.tsx b/packages/react-inbox/src/components/Messages2.0/actions/MarkUnread.tsx index 94b23ecd..ac3bb89c 100644 --- a/packages/react-inbox/src/components/Messages2.0/actions/MarkUnread.tsx +++ b/packages/react-inbox/src/components/Messages2.0/actions/MarkUnread.tsx @@ -31,11 +31,11 @@ const MarkUnread: React.FunctionComponent<{ > diff --git a/packages/react-inbox/src/components/Messages2.0/actions/index.tsx b/packages/react-inbox/src/components/Messages2.0/actions/index.tsx index 26b76b6a..f5ed3fe5 100644 --- a/packages/react-inbox/src/components/Messages2.0/actions/index.tsx +++ b/packages/react-inbox/src/components/Messages2.0/actions/index.tsx @@ -256,7 +256,7 @@ const MessageActions: React.FunctionComponent<{ {formattedTime} {read && ( ( "*, &": { boxSizing: "border-box", }, - background: "#F2F6F9", + background: "var(--ci-background)", }), }, theme?.container @@ -83,7 +83,7 @@ const MessageList = styled.div<{ isMobile?: boolean }>( return deepExtend( { position: "relative", - background: "rgb(242, 246, 249)", + background: "var(--ci-background)", overflow: "scroll", display: "flex", height, diff --git a/packages/react-inbox/src/components/Messages2.0/styled.tsx b/packages/react-inbox/src/components/Messages2.0/styled.tsx index a3ba921f..880e26bb 100644 --- a/packages/react-inbox/src/components/Messages2.0/styled.tsx +++ b/packages/react-inbox/src/components/Messages2.0/styled.tsx @@ -10,35 +10,6 @@ export const PositionRelative = styled.div` position: relative; `; -export const Container = styled.div(({ theme }) => - deepExtend( - { - display: "flex", - position: "relative", - padding: "10px 12px 10px 30px", - backgroundColor: "#F9FAFB", - alignItems: "center", - marginTop: 6, - borderRadius: 6, - borderBottom: "1px solid rgba(203,213,224,.5)", - "&.read": { - backgroundColor: "#F7F6F9", - }, - "&:not(.read):hover": { - background: "#EDE4ED", - }, - }, - theme.message?.container ?? {} - ) -); - -export const Contents = styled.div(({ theme }) => ({ - marginRight: "auto", - marginLeft: 15, - textAlign: "left", - ...theme.message?.contents, -})); - export const Title = styled.div<{ read?: string }>(({ theme, read }) => deepExtend( { @@ -50,7 +21,7 @@ export const Title = styled.div<{ read?: string }>(({ theme, read }) => display: "-webkit-box", overflow: "hidden", textOverflow: "ellipsis", - color: read ? "rgba(86, 96, 116, 1)" : "rgb(28, 39, 58)", + color: read ? "var(--ci-text-color)" : "var(--ci-title-color)", }, theme.message?.title ) @@ -59,7 +30,7 @@ export const Title = styled.div<{ read?: string }>(({ theme, read }) => export const TextElement = styled.div(({ theme }) => deepExtend( { - color: "rgb(28, 39, 58) ", + color: "var(--ci-text-color)", marginTop: "1px", wordBreak: "break-word", fontSize: "12px", @@ -118,7 +89,7 @@ export const ActionElement = styled.button<{ export const TimeAgo = styled.div(({ theme }) => deepExtend( { - color: "rgb(86, 96, 116)", + color: "var(--ci-text-color)", fontSize: "12px", fontStyle: "normal", fontWeight: "400", diff --git a/packages/react-inbox/src/types.ts b/packages/react-inbox/src/types.ts index 320968e3..77021a1b 100644 --- a/packages/react-inbox/src/types.ts +++ b/packages/react-inbox/src/types.ts @@ -12,6 +12,10 @@ import { CSSObject } from "styled-components"; export interface InboxTheme { brand?: Brand; + colorMode?: "light" | "dark"; + variables?: { + background: string; + }; container?: CSSObject; emptyState?: CSSObject; footer?: CSSObject; diff --git a/packages/react-preferences/src/components/PreferenceList.tsx b/packages/react-preferences/src/components/PreferenceList.tsx index 5b84ce08..62b4f8be 100644 --- a/packages/react-preferences/src/components/PreferenceList.tsx +++ b/packages/react-preferences/src/components/PreferenceList.tsx @@ -10,7 +10,7 @@ export const StyledList = styled.div` display: flex; height: 433px; flex-direction: column; - background: rgba(255, 255, 255, 0.2); + background: var(--ci-background); `; const PreferenceV4Wrapper = styled.div` @@ -22,12 +22,12 @@ export const PreferenceList: React.FunctionComponent<{ theme?: any; draft?: boolean; }> = ({ theme, draft }) => { - const { brand, tenantId } = useCourier(); + const courierContext = useCourier(); const preferences = usePreferences(); useEffect(() => { - preferences.fetchRecipientPreferences(tenantId); - preferences.fetchPreferencePage(tenantId, draft); + preferences.fetchRecipientPreferences(courierContext.tenantId); + preferences.fetchPreferencePage(courierContext.tenantId, draft); }, []); const renderPreferences = () => { @@ -41,7 +41,7 @@ export const PreferenceList: React.FunctionComponent<{ ) { return ( - + ); } @@ -51,7 +51,7 @@ export const PreferenceList: React.FunctionComponent<{ !preferences.preferencePage?.sections?.nodes || preferences.preferencePage?.sections?.nodes.length < 1 ) { - return brand?.preferenceTemplates?.map((template) => ( + return courierContext.brand?.preferenceTemplates?.map((template) => ( @@ -152,3 +153,46 @@ const MyApp = ({ children }) => { ); }; ``` + + + +### [Dark Mode / Theme Variables](#dark-mode) + +Dark mode is supported by passing "theme.colorMode" to the CourierProvider + +```tsx +import { CourierProvider } from "@trycourier/react-provider"; + +const MyApp = ({ children }) => { + return ( + {children} + ); +}; +``` + +You can customize darkmode by passing in variables to the root level theme: + +````typescript +export interface ThemeVariables { + background?: string; + textColor?: string; + titleColor?: string; + structure?: string; + icon?: string; +}``` + +```tsx +import { CourierProvider } from "@trycourier/react-provider"; + +const MyApp = ({ children }) => { + return ( + {children} + ); +}; +```` diff --git a/packages/react-provider/docs/2.dark-mode.md b/packages/react-provider/docs/2.dark-mode.md new file mode 100644 index 00000000..61f0b9bd --- /dev/null +++ b/packages/react-provider/docs/2.dark-mode.md @@ -0,0 +1,40 @@ +### [Dark Mode / Theme Variables](#dark-mode) + +Dark mode is supported by passing "theme.colorMode" to the CourierProvider + +```tsx +import { CourierProvider } from "@trycourier/react-provider"; + +const MyApp = ({ children }) => { + return ( + {children} + ); +}; +``` + +You can customize darkmode by passing in variables to the root level theme: + +````typescript +export interface ThemeVariables { + background?: string; + textColor?: string; + titleColor?: string; + structure?: string; + icon?: string; +}``` + +```tsx +import { CourierProvider } from "@trycourier/react-provider"; + +const MyApp = ({ children }) => { + return ( + {children} + ); +}; +```` diff --git a/packages/react-provider/src/index.tsx b/packages/react-provider/src/index.tsx index 491a976e..d82bb76e 100644 --- a/packages/react-provider/src/index.tsx +++ b/packages/react-provider/src/index.tsx @@ -18,9 +18,11 @@ import { EventType, ICourierContext, ICourierProviderProps, + OnEvent, PinDetails, + ProviderTheme, + ThemeVariables, WSOptions, - OnEvent, } from "./types"; import { CourierTransport } from "./transports/courier"; import { @@ -39,6 +41,9 @@ import useCourierActions from "./hooks/use-courier-actions"; import { usePageVisible } from "./hooks/use-page-visible"; import useTransport from "./hooks/use-transport"; import useClientSourceId from "./hooks/use-client-source-id"; +import deepExtend from "deep-extend"; +import { darkVariables, lightVariables } from "./theme"; +import { createGlobalStyle } from "styled-components"; export * from "./transports"; export * from "./hooks"; @@ -49,26 +54,42 @@ export const registerMiddleware = _registerMiddleware; export type { Brand, + EventType, IActionElemental, ICourierContext, ICourierEventMessage, IInboxMessagePreview, + Interceptor, ITextElemental, Middleware, + OnEvent, PinDetails, + ProviderTheme, WSOptions, - EventType, - OnEvent, - Interceptor, }; +const GlobalThemeVariables = createGlobalStyle<{ + theme: { + variables: ThemeVariables; + }; +}>(({ theme }) => { + return { + ":root": { + "--ci-background": theme?.variables?.background, + "--ci-text-color": theme?.variables?.textColor, + "--ci-title-color": theme?.variables?.titleColor, + "--ci-structure": theme?.variables?.structure, + "--ci-icon": theme?.variables?.icon, + }, + }; +}); + export const CourierContext = React.createContext(undefined); export const CourierProvider: React.FunctionComponent< PropsWithChildren > = ({ - tenantId, apiUrl, authorization, brand, @@ -76,6 +97,8 @@ export const CourierProvider: React.FunctionComponent< children, clientKey, id, + tenantId, + theme: _theme, inboxApiUrl, localStorage = typeof window !== "undefined" ? window?.localStorage @@ -126,8 +149,19 @@ export const CourierProvider: React.FunctionComponent< wsOptions, }); + const theme = useMemo( + () => ({ + ..._theme, + variables: deepExtend( + {}, + _theme?.colorMode === "dark" ? darkVariables : lightVariables, + _theme?.variables ?? {} + ), + }), + [_theme] + ); + const [state, dispatch] = useReducer(reducer, { - tenantId, apiUrl, authorization, brand, @@ -138,6 +172,7 @@ export const CourierProvider: React.FunctionComponent< localStorage, middleware, onRouteChange, + tenantId, transport, userId, userSignature, @@ -210,20 +245,19 @@ export const CourierProvider: React.FunctionComponent< } actions.init({ - tenantId, apiUrl, authorization, brandId, clientKey, inboxApiUrl, localStorage, + tenantId, transport, userId, userSignature, ...parsedLocalStorageState, }); }, [ - tenantId, actions, apiUrl, authorization, @@ -231,6 +265,7 @@ export const CourierProvider: React.FunctionComponent< clientKey, inboxApiUrl, localStorage, + tenantId, transport, userId, userSignature, @@ -258,6 +293,7 @@ export const CourierProvider: React.FunctionComponent< dispatch, }} > + {children} ); diff --git a/packages/react-provider/src/theme.ts b/packages/react-provider/src/theme.ts new file mode 100644 index 00000000..a85a297b --- /dev/null +++ b/packages/react-provider/src/theme.ts @@ -0,0 +1,15 @@ +export const lightVariables = { + background: "#f2f6f9", + textColor: "#1c273a", + titleColor: "#1c273a", + structure: "#DEE8F0", + icon: "#566074", +}; + +export const darkVariables = { + background: "#262A34", + textColor: "#C9C8E1", + titleColor: "white", + structure: "#4F4D67", + icon: "#8786A9", +}; diff --git a/packages/react-provider/src/types.ts b/packages/react-provider/src/types.ts index e412c08c..218fa6bc 100644 --- a/packages/react-provider/src/types.ts +++ b/packages/react-provider/src/types.ts @@ -35,6 +35,7 @@ export interface Brand { inapp?: { disableCourierFooter?: boolean; borderRadius?: string; + renderActionsAsButtons?: boolean; disableMessageIcon?: boolean; placement?: "top" | "bottom" | "left" | "right"; emptyState?: { @@ -71,11 +72,25 @@ export type EventType = | "click" | "unpin"; +export interface ThemeVariables { + background?: string; + textColor?: string; + titleColor?: string; + textColorRead?: string; + structure?: string; + icon?: string; +} + +export interface ProviderTheme { + colorMode?: "dark" | "light"; + variables?: ThemeVariables; +} export interface ICourierProviderProps { tenantId?: string; apiUrl?: string; authorization?: string; brand?: Brand; + theme?: ProviderTheme; brandId?: string; clientKey?: string; id?: string; diff --git a/packages/react-toast/src/components/Body/styled.ts b/packages/react-toast/src/components/Body/styled.ts index ac9b8597..87b1161a 100644 --- a/packages/react-toast/src/components/Body/styled.ts +++ b/packages/react-toast/src/components/Body/styled.ts @@ -27,17 +27,17 @@ export const Dismiss = styled.button(({ theme }) => opacity: 0, cursor: "pointer", position: "absolute", - top: -10, + top: -15, fontFamily: `"Roboto", monospace`, right: -10, - width: 30, - height: 30, + width: 25, + height: 25, zIndex: 50, color: theme?.brand?.colors?.primary, background: "rgba(255, 255, 255, 0.8)", boxShadow: "0px 4px 4px rgba(0, 0, 0, 0.1)", transition: "opacity 100ms ease-in", - padding: 6, + padding: 3, border: "none", "&:hover": { background: "white", @@ -67,7 +67,7 @@ export const Message = styled.div(({ theme }) => fontStyle: "normal", lineHeight: "14px", alignSelf: "center", - color: "#73819B", + color: "var(--ci-text-color)", overflow: "hidden", }, theme?.message?.contents @@ -88,7 +88,7 @@ export const Title = styled.div(({ theme }) => textOverflow: "ellipsis", WebkitLineClamp: "1", WebkitBoxOrient: "vertical", - color: "#24324B", + color: "var(--ci-title-color)", }, theme.message?.title ) @@ -97,7 +97,7 @@ export const Title = styled.div(({ theme }) => export const TextElement = styled.div(({ theme }) => deepExtend( { - color: "#73819B", + color: "var(--ci-text-color)", marginTop: "1px", wordBreak: "break-word", fontSize: "12px", diff --git a/packages/react-toast/src/components/Toast/styled.ts b/packages/react-toast/src/components/Toast/styled.ts index f52fa5b4..59da696a 100644 --- a/packages/react-toast/src/components/Toast/styled.ts +++ b/packages/react-toast/src/components/Toast/styled.ts @@ -14,6 +14,7 @@ export const toastStyles = ({ theme }) => ({ width: 320, minHeight: 65, padding: 0, + background: "var(--ci-background)", borderRadius: "6px", borderBottomLeftRadius: theme?.brand?.inapp?.toast?.borderRadius ??