From 1f719c1d0c945840f91fdeb8c82d898fa4d7cf5d Mon Sep 17 00:00:00 2001 From: Elia Date: Thu, 13 Jun 2024 17:50:01 +0300 Subject: [PATCH 1/8] =?UTF-8?q?=E2=9C=A8=20feat:=20initial=20new=20design?= =?UTF-8?q?=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/src/main.tsx | 3 +- package-lock.json | 4 +- src/assets/icons/index.tsx | 91 ++++- src/assets/illustrations.tsx | 24 ++ src/chat/button.tsx | 5 +- src/chat/chat.css | 120 +++---- src/chat/chat.sass | 2 +- src/chat/chat.tsx | 236 ++++++++++--- src/chat/chatbox.tsx | 658 ++++++++++++++++++++--------------- src/chat/input.tsx | 28 +- 10 files changed, 724 insertions(+), 447 deletions(-) create mode 100644 src/assets/illustrations.tsx diff --git a/example/src/main.tsx b/example/src/main.tsx index 6be81be..fe9eb6b 100644 --- a/example/src/main.tsx +++ b/example/src/main.tsx @@ -8,6 +8,7 @@ ReactDOM.createRoot( ).render( {/* @ts-ignore */} - + + {/* 1258 */} ); diff --git a/package-lock.json b/package-lock.json index 3987da9..4c3ea24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "react-sarufi-chatbox", - "version": "0.3.6", + "version": "0.4.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "react-sarufi-chatbox", - "version": "0.3.6", + "version": "0.4.8", "license": "ISC", "dependencies": { "axios": "^1.3.4" diff --git a/src/assets/icons/index.tsx b/src/assets/icons/index.tsx index 4aa9e99..72937e7 100644 --- a/src/assets/icons/index.tsx +++ b/src/assets/icons/index.tsx @@ -1,11 +1,46 @@ import React, { CSSProperties, FC } from "react"; -interface IconProps { +export interface IconProps { size?: number | string; className?: string; style?: CSSProperties; } +export const Attachment: FC = ({ size, style, className }) => ( + + + +); + +export const Calendar: FC = ({ size, style, className }) => ( + + + +); + // that x icon export const Close: FC = ({ size, style, className }) => { return ( @@ -51,30 +86,56 @@ export const TextComponent: FC = ({ size, style, className }) => { export const SendIcon: FC = ({ size, className, style }) => { return ( - ); }; +export const MicIcon: FC = ({ size, className, style }) => ( + + + +); + +export const MinusIcon: FC = ({ size, className, style }) => ( + + + +); + export const Pause: FC = ({ className, size }) => { return ( = ({ size, style, className }) => ( + + + + +); diff --git a/src/chat/button.tsx b/src/chat/button.tsx index ec74f0a..20d6328 100644 --- a/src/chat/button.tsx +++ b/src/chat/button.tsx @@ -21,16 +21,13 @@ const Button: FC = ({ label, mode, onClick, disabled }) => { cursor: disabled ? "default" : "pointer", opacity: disabled ? 0.4 : 1, fontSize: ".9rem", - padding: ".7rem 1rem", - position: "absolute", overflow: "hidden", transition: ".3s linear", zIndex: 100001, - right: "5px", border: "none", + backgroundColor: "transparent", ...(mode === "dark" ? { - backgroundColor: "transparent", color: "white", } : {}), diff --git a/src/chat/chat.css b/src/chat/chat.css index 1e25106..a6ac969 100644 --- a/src/chat/chat.css +++ b/src/chat/chat.css @@ -2,10 +2,10 @@ height: 50px; width: 50px; z-index: 100000; - -webkit-transition: height .25s; - transition: height .25s; + -webkit-transition: height 0.25s; + transition: height 0.25s; -webkit-box-sizing: border-box; - box-sizing: border-box; + box-sizing: border-box; font-family: var(--sarufi-font-family); font-size: var(--sarufi-font-size); bottom: 1rem; @@ -48,7 +48,9 @@ width: var(--sarufi-chatbox-width); max-width: 100%; max-height: 100%; - border-radius: 0; + border-radius: 18px; + /* overflow: hidden; */ + /* border: 1px solid blue; */ bottom: 1rem; right: 1rem; background: var(--clr-neutral-0); @@ -72,94 +74,88 @@ .sarufi-chat-container ul { -webkit-margin-before: 0; - margin-block-start: 0; + margin-block-start: 0; -webkit-margin-after: 0; - margin-block-end: 0; + margin-block-end: 0; list-style: none; -webkit-padding-start: 0; - padding-inline-start: 0; -} - -.sarufi-chat-container form { - width: 100%; - bottom: 0; - height: 60px; - -webkit-box-sizing: border-box; - box-sizing: border-box; + padding-inline-start: 0; } .sarufi-chat-container form button { position: absolute; - height: 50px; - width: 50px; - max-width: 50px !important; - bottom: 5px; + /* height: 50px; */ + /* width: 50px; */ + /* max-width: 50px !important; */ + /* bottom: 5px; */ + top: 7px; right: 5px; - padding: 1rem; - -webkit-box-sizing: border-box; - box-sizing: border-box; + /* padding: 1rem; */ + /* -webkit-box-sizing: border-box; + box-sizing: border-box; */ color: var(--sarufi-primary-color); } .sarufi-chat-container .choices { - height: calc( var(--sarufi-chatbox-height) - 82px - .5rem); - max-height: calc( 100% - 92px - .5rem); + height: calc(var(--sarufi-chatbox-height) - 82px - 0.5rem); + max-height: calc(100% - 92px - 0.5rem); width: var(--sarufi-chatbox-width); max-width: 100%; - bottom: calc( 40px + .2.2rem); + bottom: calc(40px + 0.2.2rem); } .sarufi-chat-container .sarufi-backdrop { - height: calc( var(--sarufi-chatbox-height) - 95px - .5rem); - max-height: calc( 100% - 105px - .5rem); + height: calc(var(--sarufi-chatbox-height) - 95px - 0.5rem); + max-height: calc(100% - 105px - 0.5rem); width: var(--sarufi-chatbox-width); max-width: 100%; - bottom: calc( 69px + .5rem); + bottom: calc(69px + 0.5rem); } .sarufi-chat-container .sarufi-options { - height: calc( var(--sarufi-chatbox-height) - 115px - .5rem); - max-height: calc( 100% - 94px - .5rem); - width: calc( var(--sarufi-chatbox-width) - 32px); + height: calc(var(--sarufi-chatbox-height) - 115px - 0.5rem); + max-height: calc(100% - 94px - 0.5rem); + width: calc(var(--sarufi-chatbox-width) - 32px); max-width: 100%; overflow-y: auto; position: fixed; - bottom: calc( 80px + .5rem); + bottom: calc(80px + 0.5rem); margin-top: 1rem; } .sarufi-field-group { width: 100%; -webkit-box-sizing: border-box; - box-sizing: border-box; + box-sizing: border-box; height: 100%; } .sarufi-field-group .input-group { - position: relative; + /* position: relative; */ display: block !important; width: 100%; -webkit-box-sizing: border-box; - box-sizing: border-box; + box-sizing: border-box; } -.sarufi-field-group .input-group textarea { +/* .sarufi-field-group .input-group textarea { resize: none; background-color: transparent; width: 100%; height: 100%; border: 1px solid transparent; - -webkit-transition: .25s; - transition: .25s; + -webkit-transition: 0.25s; + transition: 0.25s; font-family: inherit; -webkit-box-sizing: border-box; - box-sizing: border-box; + box-sizing: border-box; font-size: inherit; -} +} */ -.sarufi-field-group .input-group textarea:focus, .sarufi-field-group .input-group textarea:hover { - border-color: var(--sarufi-primary-color); - border-width: 1px; +.sarufi-field-group .input-group textarea:focus, +.sarufi-field-group .input-group textarea:hover { + /* border-color: var(--sarufi-primary-color); */ + /* border-width: 1px; */ outline: transparent !important; } @@ -180,7 +176,7 @@ -webkit-animation-fill-mode: both; animation-fill-mode: both; -webkit-box-sizing: border-box; - box-sizing: border-box; + box-sizing: border-box; } .sarufi-chat-loader .sarufi-bounce1 { @@ -254,12 +250,12 @@ } .sarufi-chat-container .sarufi-backdrop { width: 100%; - bottom: calc( 53.5px + .5rem); + bottom: calc(53.5px + 0.5rem); } .sarufi-chat-container .sarufi-options { width: 100%; max-width: 100%; - bottom: calc( 65px + .5rem); + bottom: calc(65px + 0.5rem); } } @@ -279,7 +275,7 @@ .sarufi-chat-container.open { bottom: 0; right: 0; - height: calc( 100% - 50px); + height: calc(100% - 50px); max-height: 100%; } .sarufi-chat-container.open.sarufi-right-align { @@ -289,18 +285,18 @@ left: 1rem; } .sarufi-chat-container .sarufi-backdrop { - height: calc( 100%px - 144px - .5rem); - max-height: calc( 100% - 144px - .5rem); + height: calc(100%px - 144px - 0.5rem); + max-height: calc(100% - 144px - 0.5rem); width: var(--sarufi-chatbox-width); max-width: 100%; - bottom: calc( 52px + .5rem); + bottom: calc(52px + 0.5rem); } .sarufi-chat-container .sarufi-options { - height: calc( 100% - 155px - .5rem); - max-height: calc( 100% - 140px - .5rem); - width: calc( var(--sarufi-chatbox-width) - 32px); + height: calc(100% - 155px - 0.5rem); + max-height: calc(100% - 140px - 0.5rem); + width: calc(var(--sarufi-chatbox-width) - 32px); max-width: 100%; - bottom: calc( 60px + .5rem); + bottom: calc(60px + 0.5rem); } } @@ -318,7 +314,7 @@ right: 0rem !important; } .sarufi-chat-container.open { - height: calc( 100% - 50px); + height: calc(100% - 50px); } .sarufi-chat-container.open.sarufi-right-align { right: 0rem; @@ -327,18 +323,18 @@ left: 0rem; } .sarufi-chat-container .sarufi-backdrop { - height: calc( 100%px - 144px - .5rem); - max-height: calc( 100% - 144px - .5rem); + height: calc(100%px - 144px - 0.5rem); + max-height: calc(100% - 144px - 0.5rem); width: var(--sarufi-chatbox-width); max-width: 100%; } .sarufi-chat-container .sarufi-options { - height: calc( 100% - 155px - .5rem); - max-height: calc( 100% - 140px - .5rem); - width: calc( var(--sarufi-chatbox-width) - 32px); + height: calc(100% - 155px - 0.5rem); + max-height: calc(100% - 140px - 0.5rem); + width: calc(var(--sarufi-chatbox-width) - 32px); max-width: 100%; - bottom: calc( 60px + .5rem); + bottom: calc(60px + 0.5rem); margin-top: 1rem; } } -/*# sourceMappingURL=chat.css.map */ \ No newline at end of file +/*# sourceMappingURL=chat.css.map */ diff --git a/src/chat/chat.sass b/src/chat/chat.sass index 2e856e1..fe05a91 100644 --- a/src/chat/chat.sass +++ b/src/chat/chat.sass @@ -51,7 +51,7 @@ form width: 100% bottom: 0 - height: 60px + // height: 60px box-sizing: border-box button position: absolute diff --git a/src/chat/chat.tsx b/src/chat/chat.tsx index 0931463..481a0e1 100644 --- a/src/chat/chat.tsx +++ b/src/chat/chat.tsx @@ -1,8 +1,9 @@ import axios from "axios"; import React, { CSSProperties, useEffect, useState } from "react"; -import { Close, TextComponent } from "../assets/icons"; +import { Close, MinusIcon } from "../assets/icons"; import "./chat.css"; import Chatbox from "./chatbox"; +import { SarufiIcon } from "../assets/illustrations"; interface ThemeType { buttonSize?: "sm" | "md" | "lg"; @@ -22,17 +23,26 @@ interface ThemeType { placement?: "left" | "right"; height?: string | number; width?: string | number; + secondaryColor?: string; } export type SarufiChatboxType = { botId: string | number; token?: string; theme?: ThemeType; + popUpShow?: boolean; }; -const Chat = ({ botId, token, theme: defaultTheme }: SarufiChatboxType) => { +const Chat = ({ + botId, + token, + theme: defaultTheme, + popUpShow = true, +}: SarufiChatboxType) => { const [open, setOpen] = useState(false); const [id, setId] = useState(new Date().valueOf()); + const [showPopup, setShowPopup] = useState(false); + const [dontShowPopup, setDontShowPopup] = useState(false); const [theme, setThemeConfig] = useState({ primaryColor: "#2776EA", borderColor: "lightgray", @@ -78,10 +88,22 @@ const Chat = ({ botId, token, theme: defaultTheme }: SarufiChatboxType) => { fetchTheme(); }, [defaultTheme]); + useEffect(() => { + if (dontShowPopup || !popUpShow) return; + if (showPopup) return; + + // Show popup after 5 seconds + setTimeout(() => { + setShowPopup(!showPopup); + }, 5000); + }, [showPopup]); + // set up styles const style = { "--sarufi-primary-color": theme?.mode === "dark" ? "#202C33" : theme?.primaryColor ?? "#2776EA", + "--sarufi-secondary-color": + theme?.mode === "dark" ? "#202C33" : theme?.secondaryColor ?? "#EDECE1", "--sarufi-font-size": `${theme?.fontSize ?? 14}px`, "--sarufi-font-family": theme?.fontFamily === "InterRegular" @@ -93,11 +115,10 @@ const Chat = ({ botId, token, theme: defaultTheme }: SarufiChatboxType) => { : "'Inter', sans-serif", "--sarufi-border-color": theme?.borderColor ?? "lightgray", "--sarufi-sent-box-bg": - theme?.mode === "dark" ? "#005C4B" : theme?.sentBoxBg ?? "#D8F9D4", + theme?.mode === "dark" ? "#005C4B" : theme?.primaryColor ?? "#2776EA", "--sarufi-received-box-bg": - theme?.mode === "dark" ? "#202C33" : theme?.receivedBoxBg ?? "white", - "--sarufi-sent-box-color": - theme?.mode === "dark" ? "white" : theme?.sentBoxColor ?? "black", + theme?.mode === "dark" ? "#202C33" : theme?.secondaryColor ?? "#EDECE1", + "--sarufi-sent-box-color": theme?.mode === "dark" ? "white" : "white", "--sarufi-received-box-color": theme?.mode === "dark" ? "white" : theme?.receivedBoxColor ?? "black", "--sarufi-sent-box-link-color": @@ -106,8 +127,7 @@ const Chat = ({ botId, token, theme: defaultTheme }: SarufiChatboxType) => { theme?.mode === "dark" ? "#53BDEB" : theme?.receivedBoxLinkColor ?? "black", - "--sarufi-chatbox-bg": - theme?.mode === "dark" ? "#0B141A" : theme?.chatboxBg ?? "#EDECE1", + "--sarufi-chatbox-bg": theme?.mode === "dark" ? "#0B141A" : "white", "--sarufi-chatbox-height": theme?.height ? theme?.height + "px" : "500px", "--sarufi-chatbox-width": theme?.width ? theme?.width + "px" : "400px", } as CSSProperties; @@ -120,6 +140,7 @@ const Chat = ({ botId, token, theme: defaultTheme }: SarufiChatboxType) => { ${open ? "sarufi-shadow-xl" : "sarufi-flex-center"}`} style={{ position: "fixed", + // border: "2px solid #000", fontFamily: theme?.fontFamily === "InterRegular" ? "'Inter', sans-serif" @@ -131,7 +152,11 @@ const Chat = ({ botId, token, theme: defaultTheme }: SarufiChatboxType) => { ...(!open ? { height: theme?.buttonSize === "lg" ? "70px" : "50px", - width: theme?.buttonSize === "lg" ? "70px" : "50px", + width: showPopup + ? "fit-content" + : theme?.buttonSize === "lg" + ? "70px" + : "50px", } : {}), ...style, @@ -139,65 +164,164 @@ const Chat = ({ botId, token, theme: defaultTheme }: SarufiChatboxType) => { > {!open && (
+

Need Help?

+

Ask me

+
)} - {open && ( +
-
-

+

- {theme?.title} -

+ +
+
+
+

+ {theme?.title} +

+

+ Online +

+
+
+
+
-
- )} + +
); }; diff --git a/src/chat/chatbox.tsx b/src/chat/chatbox.tsx index b38acfd..c7860a8 100644 --- a/src/chat/chatbox.tsx +++ b/src/chat/chatbox.tsx @@ -4,8 +4,24 @@ import { CSSProperties, useEffect, useRef, useState } from "react"; import Button from "./button"; import Input from "./input"; import ChatLoader from "./chat-loader"; -import Media from "./media"; -import { Close, SendIcon } from "../assets/icons"; +import { + Attachment, + Calendar, + Close, + MicIcon, + SendIcon, +} from "../assets/icons"; +import { SarufiIcon } from "../assets/illustrations"; + +type ChatType = { + message: string; + sent?: boolean; + received?: boolean; + chat: any; + type: string; + actions: any; + next_state: string; +}; const Chatbox = ({ id, @@ -28,10 +44,11 @@ const Chatbox = ({ fontFamily: string; fontSize: string | number; }) => { - const [chats, setChats] = useState([]); + const [chats, setChats] = useState([]); const [value, setValue] = useState(""); const [loading, setLoading] = useState(false); const ref = useRef(null); + const [chatId, setChatId] = useState(id); const onSubmit = (message: string, type?: string, itemId?: string) => { if (!message) return; @@ -47,7 +64,7 @@ const Chatbox = ({ }`, { message: itemId ?? message, - chat_id: id, + chat_id: chatId, bot_id: botId, message_type: type ?? "text", }, @@ -123,8 +140,11 @@ const Chatbox = ({ }, [chats]); useEffect(() => { - setChats([]); - setValue(""); + if (chatId !== id) { + setChatId(id); + setChats([]); + setValue(""); + } }, [open]); return ( @@ -134,7 +154,8 @@ const Chatbox = ({ id="sarufi-chat-container" style={{ background: "var(--sarufi-chatbox-bg)", - height: "calc( 100% - 100px )", + height: "calc( 100% - 160px )", + // height: "100%", overflowY: "auto", position: "relative", }} @@ -171,6 +192,7 @@ const Chatbox = ({ chat={chat} key={index} onSubmit={onSubmit} + primaryColor={primaryColor} mode={mode} fontFamily={fontFamily} fontSize={fontSize} @@ -195,37 +217,86 @@ const Chatbox = ({ -
+ { + e.preventDefault(); + onSubmit(value); + }} + > + onSubmit(value)} + onChange={(e) => { + setValue(e.target.value); + }} + /> +
- - ))} - - )} - {(!message || typeof message !== "string") && chat?.actions && menu && ( +
action?.send_reply_button) + ?.send_reply_button?.action?.buttons?.length > 0 || + ((!message || typeof message !== "string") && + chat?.actions && + menu) + ? "0rem" + : ".3rem", + ...(!hasMedia && !chat?.sent + ? { borderTopRightRadius: ".3rem" } + : {}), }} > -
+ {chat?.type === "button" && ( +
    - {menu["send_button"]?.button ?? menu["send_button"]?.action?.button} - -
- )} - {(!message || typeof message !== "string") && - chat?.actions && - menu && - openChoices && ( + {chat?.actions + ?.find((action: any) => action?.send_reply_button) + ?.send_reply_button?.action?.buttons?.map( + (el: any, i: number) => ( +
  • action?.send_reply_button + )?.send_reply_button?.action?.buttons?.length - + 1 + ? ".3rem" + : "0", + borderBottomRightRadius: + i === + chat?.actions?.find( + (action: any) => action?.send_reply_button + )?.send_reply_button?.action?.buttons?.length - + 1 + ? ".3rem" + : "0", + }} + key={i} + > + +
  • + ) + )} + + )} + {(!message || typeof message !== "string") && chat?.actions && menu && (
    + +
    + )} + {(!message || typeof message !== "string") && + chat?.actions && + menu && + openChoices && (
    setOpenChoices(false)} - /> -
    setOpenChoices(false)} + /> +
    - - {menu["send_button"]?.action?.sections[0]?.title && ( -

    setOpenChoices(false)} style={{ - width: "100%", - marginLeft: "1rem", - paddingRight: "2rem", - textAlign: "center", - fontFamily: "var(--sarufi-font-family)", + background: "none", + border: "none", + cursor: "pointer", + color: "var(--sarufi-received-box-color)", }} + className="sarufi-button" > - {menu["send_button"]?.action?.sections[0]?.title} -

    - )} + + + {menu["send_button"]?.action?.sections[0]?.title && ( +

    + {menu["send_button"]?.action?.sections[0]?.title} +

    + )} +
    +
      + {menu["send_button"]?.action?.sections[0]?.rows?.filter( + (el: any) => el?.title + )?.length > 0 && + menu["send_button"]?.action?.sections[0]?.rows + ?.filter((el: any) => el?.title) + ?.map((row: any) => ( +
    • { + onSubmit(row?.title, "interactive", row?.id); + setOpenChoices(false); + }} + > +
      +

      + {row?.title} +

      +

      + {row?.description} +

      +
      +
      + +
      +
    • + ))} +
    -
      - {menu["send_button"]?.action?.sections[0]?.rows?.filter( - (el: any) => el?.title - )?.length > 0 && - menu["send_button"]?.action?.sections[0]?.rows - ?.filter((el: any) => el?.title) - ?.map((row: any) => ( -
    • { - onSubmit(row?.title, "interactive", row?.id); - setOpenChoices(false); - }} - > -
      -

      - {row?.title} -

      -

      - {row?.description} -

      -
      -
      - -
      -
    • - ))} -
    -
    - )} + )} + ); }; diff --git a/src/chat/input.tsx b/src/chat/input.tsx index eb1e7bb..d55d3e6 100644 --- a/src/chat/input.tsx +++ b/src/chat/input.tsx @@ -40,19 +40,9 @@ const Input: FC = ({ const [isFocused, setFocused] = useState(true); return ( -
    -
    -