From 022dc040237e0faafccb4a709378a627337892e4 Mon Sep 17 00:00:00 2001 From: Lukas Podmelle <podmellelukas@gmail.com> Date: Wed, 10 Jan 2024 10:33:14 +0100 Subject: [PATCH 1/9] Redesign --- package-lock.json | 2 +- samples/feedyou/newdesign.html | 30 + src/App/App.tsx | 4 +- .../templates/ExpandableTemplate/Header.tsx | 113 +- .../Signature/Signature.tsx | 103 +- .../Signature/SignatureLink.tsx | 40 +- .../Signature/SignatureTemplate.tsx | 17 +- src/Shell.tsx | 488 +++---- src/scss/botchat-redesign.scss | 1122 +++++++++++++++++ src/themes/ExpandableBarTheme.tsx | 6 +- src/themes/ExpandableKnobTheme.tsx | 15 +- src/themes/ExpandableKnobThemeNew.tsx | 21 + src/themes/SidebarTheme.tsx | 14 +- src/themes/index.ts | 65 +- typing-indicator.gif | Bin 0 -> 14372 bytes 15 files changed, 1664 insertions(+), 376 deletions(-) create mode 100644 samples/feedyou/newdesign.html create mode 100644 src/scss/botchat-redesign.scss create mode 100644 src/themes/ExpandableKnobThemeNew.tsx create mode 100644 typing-indicator.gif diff --git a/package-lock.json b/package-lock.json index 9b278a2f98..f2b49f66be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@feedyou-ai/feedbot-webchat-v0", - "version": "0.14.1-10", + "version": "0.14.1-12", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/samples/feedyou/newdesign.html b/samples/feedyou/newdesign.html new file mode 100644 index 0000000000..41bcd8798d --- /dev/null +++ b/samples/feedyou/newdesign.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Chatbot</title> + </head> + <body> + <!-- href="https://cdn.feedyou.ai/webchat/latest/botchat.css + t.src = 'https://cdn.feedyou.ai/webchat/latest/botchat-es5.js'; + +--> + <link href="../../botchat-redesign.css" rel="stylesheet" /> + <script type="text/javascript"> + (function (f, y, b, o, t, s) { + (t = y.createElement(b)), (s = y.getElementsByTagName(b)[0]); + t.async = 1; + t.src = '../../botchat-es5.js'; + t.onload = function () { + BotChat.App({ + bot: { id: 'feedbot-demo-feedie-podmelle-lukas' }, + channel: { id: 'jtuqsq' }, + }); + }; + s.parentNode.insertBefore(t, s); + })(window, document, 'script'); + </script> + <!-- druhý bot na testing: feedbot-demo-webchat, gvyddu --> + </body> +</html> diff --git a/src/App/App.tsx b/src/App/App.tsx index 9ee0b42cd7..d3035ca4d5 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -10,7 +10,7 @@ import { generateUserId } from '../utils/generateUserId' export type AppProps = ChatProps & { theme?: Theme; // option to override theme settings from remote config defaultTheme?: Theme; // option to set default template when no remote config found (on default microsite for example) - header?: { textWhenCollapsed?: string; text: string, extraHtml?: string }; + header?: { textWhenCollapsed?: string; text: string, extraHtml?: string, image?: string }; channel?: { index?: number, id?: string }; autoExpandTimeout?: number; enableScreenshotUpload?: boolean; @@ -213,7 +213,7 @@ export const App = async (props: AppProps, container?: HTMLElement) => { // FEEDYOU configurable theming if (props.theme || !container) { - const theme = { mainColor: "#D83838", ...props.theme }; + const theme = { mainColor: "#0063f8", ...props.theme }; props.theme && (props.theme.enableScreenshotUpload = !!props.enableScreenshotUpload) const themeStyle = document.createElement("style"); themeStyle.type = "text/css"; diff --git a/src/App/templates/ExpandableTemplate/Header.tsx b/src/App/templates/ExpandableTemplate/Header.tsx index 0780a63180..2d25725345 100644 --- a/src/App/templates/ExpandableTemplate/Header.tsx +++ b/src/App/templates/ExpandableTemplate/Header.tsx @@ -1,11 +1,12 @@ -import * as React from 'react' -import { AppProps } from '../../App' +import * as React from 'react'; +import { AppProps } from '../../App'; +import { Theme } from '../../../themes'; export type Props = { - appProps: AppProps - onClick(): void - isCollapsed: boolean -} + appProps: AppProps; + onClick(): void; + isCollapsed: boolean; +}; export const Header: React.StatelessComponent<Props> = ({ appProps, @@ -13,39 +14,79 @@ export const Header: React.StatelessComponent<Props> = ({ isCollapsed, }) => { const { - theme: { mainColor }, + theme: { mainColor, template }, header: { extraHtml }, - } = appProps - - const backgroundColor = mainColor || '#e51836' - const title = getTitle(appProps, isCollapsed) - - + } = appProps; + + let backgroundColor; + {/* LP: for dev; pak změnit type na 'expandable-knob-new' */} + if (template.type !== 'expandable-knob') { + backgroundColor = mainColor || '#e51836'; + } + + const title = getTitle(appProps, isCollapsed); + const avatar = + template.avatar || + "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 36 36' fill='none'%3E%3Cpath d='M36 18c0 9.941-8.059 18-18 18S0 27.941 0 18 8.059 0 18 0s18 8.059 18 18Z' fill='%23F7F9FB'/%3E%3Cpath d='M14.024 18.026h-.003v-.005h.004l.001.002v.001l-.002.002Zm3.978 0v-.001l.001-.002v-.002h-.006v.004l.002.001h.003Zm3.976 0h-.003l-.001-.002v-.002l.001-.001h.004v.003l-.001.002Z' fill='%23385B75'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M18.718 9.023a8.912 8.912 0 0 1 5.669 2.59 8.91 8.91 0 0 1-10.583 14.12l-3.073 1.182a1.274 1.274 0 0 1-1.646-1.646l1.182-3.073a8.91 8.91 0 0 1 8.45-13.173ZM14.023 17a1.024 1.024 0 1 0 0 2.047 1.024 1.024 0 0 0 0-2.047Zm3.409.172a1.023 1.023 0 1 1 1.136 1.702 1.023 1.023 0 0 1-1.136-1.702ZM21.977 17a1.023 1.023 0 1 0 0 2.047 1.023 1.023 0 0 0 0-2.047Z' fill='%23385B75'/%3E%3C/svg%3E"; + return ( - <div className="feedbot-header" onClick={onClick} style={{ backgroundColor }}> - <span className="feedbot-title"> - {title} - </span> - - {extraHtml && <span className="feedbot-extra-html" dangerouslySetInnerHTML={{ __html: extraHtml }}/>} - + <div + className='feedbot-header' + onClick={onClick} + style={{ backgroundColor, justifyContent: 'space-between' }}> + <div className='feedbot-header-name'> + {/* LP: for dev; pak změnit type na 'expandable-knob-new' */} + {template.type === 'expandable-knob' ? ( + <div className='feedbot-avatar' style={{backgroundImage: `url("${avatar}")`}}> + </div> + ) : null} + <div className='feedbot-name'> + <span className='feedbot-title'>{title}</span> + {/* LP: for dev; pak změnit type na 'expandable-knob-new' */} + {template.type === 'expandable-knob' ? ( + <span className='feedbot-supportive-title'> + {/* LP: for dev; pak text odstranit */} + S čím vám dnes mohu pomoci? + {template.supportiveTitle} + </span> + ): null} + </div> + </div> + + {extraHtml && ( + <span + className='feedbot-extra-html' + dangerouslySetInnerHTML={{ __html: extraHtml }} + /> + )} + <a - onClick={e => e.preventDefault()} - className="feedbot-minimize" - href="#" - >_</a> + onClick={(e) => e.preventDefault()} + className='feedbot-minimize' + href='#'> + {/* LP: for dev; pak změnit type na 'expandable-knob-new' */} + {template.type === 'expandable-knob' ? ( + <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26" fill="none"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M17.5303 8.46967C17.8232 8.76256 17.8232 9.23744 17.5303 9.53033L9.53033 17.5303C9.23744 17.8232 8.76256 17.8232 8.46967 17.5303C8.17678 17.2374 8.17678 16.7626 8.46967 16.4697L16.4697 8.46967C16.7626 8.17678 17.2374 8.17678 17.5303 8.46967Z" fill="currentColor"/> + <path fill-rule="evenodd" clip-rule="evenodd" d="M8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L17.5303 16.4697C17.8232 16.7626 17.8232 17.2374 17.5303 17.5303C17.2374 17.8232 16.7626 17.8232 16.4697 17.5303L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967Z" fill="currentColor"/> + <path fill-rule="evenodd" clip-rule="evenodd" d="M13 1.75C6.7868 1.75 1.75 6.7868 1.75 13C1.75 19.2132 6.7868 24.25 13 24.25C19.2132 24.25 24.25 19.2132 24.25 13C24.25 6.7868 19.2132 1.75 13 1.75ZM0.25 13C0.25 5.95837 5.95837 0.25 13 0.25C20.0416 0.25 25.75 5.95837 25.75 13C25.75 20.0416 20.0416 25.75 13 25.75C5.95837 25.75 0.25 20.0416 0.25 13Z" fill="currentColor"/> + </svg> + ) : ( + '_' + )} + </a> </div> - ) -} + ); +}; const getTitle = (props: AppProps, isCollapsed: boolean) => { - const { text, textWhenCollapsed } = props.header - - const titleWhenExpanded = text || 'Chatbot' - const titleWhenCollapsed = textWhenCollapsed || titleWhenExpanded - const titleToShow = isCollapsed ? titleWhenCollapsed : titleWhenExpanded - - return titleToShow -} - -export type HeaderProps = Props + const { text, textWhenCollapsed } = props.header; + + const titleWhenExpanded = text || 'Chatbot'; + const titleWhenCollapsed = textWhenCollapsed || titleWhenExpanded; + const titleToShow = isCollapsed ? titleWhenCollapsed : titleWhenExpanded; + + return titleToShow; +}; + +export type HeaderProps = Props; diff --git a/src/App/templates/ExpandableTemplate/Signature/Signature.tsx b/src/App/templates/ExpandableTemplate/Signature/Signature.tsx index c90ac10954..b43f797cc5 100644 --- a/src/App/templates/ExpandableTemplate/Signature/Signature.tsx +++ b/src/App/templates/ExpandableTemplate/Signature/Signature.tsx @@ -1,64 +1,67 @@ -import * as React from 'react' -import { SignatureLink } from './SignatureLink' -import { Theme } from '../../../../themes' -import { SignatureTemplate } from './SignatureTemplate' +import * as React from 'react'; +import { SignatureLink } from './SignatureLink'; +import { Theme } from '../../../../themes'; +import { SignatureTemplate } from './SignatureTemplate'; -type SignatureSchema = Theme['signature'] -const FEEDYOU_LOGO_IMG_SRC = 'https://cdn.feedyou.ai/webchat/feedyou_logo_red.png' +type SignatureSchema = Theme['signature']; +const FEEDYOU_LOGO_IMG_SRC = + 'https://cdn.feedyou.ai/webchat/feedyou_logo_red.png'; export type Props = { - signature: SignatureSchema - botId: string -} + signature: SignatureSchema; + botId: string; +}; -const getLinkQueryString = (botId: string) => `?utm_source=webchat&utm_medium=chatbot&utm_campaign=${botId}` +const getLinkQueryString = (botId: string) => + `?utm_source=webchat&utm_medium=chatbot&utm_campaign=${botId}`; -export const Signature: React.StatelessComponent<Props> = ({ signature, botId }) => { - const { partnerLogoUrl, partnerLinkUrl, partnerLogoStyle, mode } = signature - const attachQueryStringToUrl = (url: string) => `${url}${getLinkQueryString(botId)}` - - const enhancedFeedyouUrl = attachQueryStringToUrl('https://feedyou.ai') - const enhancedPartnerUrl = partnerLinkUrl ? attachQueryStringToUrl(partnerLinkUrl) : enhancedFeedyouUrl - - const feedyouLink = <SignatureLink - href={enhancedFeedyouUrl} - imgSrc={FEEDYOU_LOGO_IMG_SRC} - /> - - const partnerLink = <SignatureLink - href={enhancedPartnerUrl} - imgSrc={partnerLogoUrl} - className="partner-logo" - customStyles={partnerLogoStyle} - /> - - if(mode === 'none') { - return null +export const Signature: React.StatelessComponent<Props> = ({ + signature, + botId, +}) => { + const { partnerLogoUrl, partnerLinkUrl, partnerLogoStyle, mode } = + signature; + const attachQueryStringToUrl = (url: string) => + `${url}${getLinkQueryString(botId)}`; + + const enhancedFeedyouUrl = attachQueryStringToUrl('https://feedyou.ai'); + const enhancedPartnerUrl = partnerLinkUrl + ? attachQueryStringToUrl(partnerLinkUrl) + : enhancedFeedyouUrl; + + const feedyouLink = ( + <SignatureLink href={enhancedFeedyouUrl} text='Feedyou' /> + ); + + const partnerLink = ( + <SignatureLink + href={enhancedPartnerUrl} + text={'partnerName'} // todo partner name + imgSrc={partnerLogoUrl} + className='partner-logo' + customStyles={partnerLogoStyle} + /> + ); + + if (mode === 'none') { + return null; } - - if (partnerLogoUrl && mode === 'both') { + + if (mode === 'both') { return ( <SignatureTemplate> {partnerLink} - <div style={{ alignSelf: 'center' }}>&</div> + <div style={{ alignSelf: 'center' }}>& </div> {feedyouLink} </SignatureTemplate> - ) + ); } - - if (partnerLogoUrl && mode === 'partner') { - return ( - <SignatureTemplate> - {partnerLink} - </SignatureTemplate> - ) + + if (mode === 'partner') { + return <SignatureTemplate>{partnerLink}</SignatureTemplate>; } - - return ( - <SignatureTemplate> - {feedyouLink} - </SignatureTemplate> - ) -} -export type SignatureProps = Props + return <SignatureTemplate>{feedyouLink}</SignatureTemplate>; +}; + +export type SignatureProps = Props; diff --git a/src/App/templates/ExpandableTemplate/Signature/SignatureLink.tsx b/src/App/templates/ExpandableTemplate/Signature/SignatureLink.tsx index c6b587f913..1edc3d8af1 100644 --- a/src/App/templates/ExpandableTemplate/Signature/SignatureLink.tsx +++ b/src/App/templates/ExpandableTemplate/Signature/SignatureLink.tsx @@ -1,28 +1,36 @@ -import * as React from 'react' -import cx from 'classnames' -import { CustomStylesForCssClass } from '../../../CustomStylesForCssClass' +import * as React from 'react'; +import cx from 'classnames'; +import { CustomStylesForCssClass } from '../../../CustomStylesForCssClass'; export type Props = { - href: string, - imgSrc: string - className?: string - customStyles?: string -} + href: string; + imgSrc?: string; + className?: string; + customStyles?: string; + text?: string; +}; export const SignatureLink: React.StatelessComponent<Props> = ({ href, imgSrc, className, customStyles, + text, }) => { return ( - <a className={cx('signature-link', className)} target="_blank" href={href}> - <img src={imgSrc} alt="Logo" /> - {className && customStyles && - <CustomStylesForCssClass cssClass={className} styles={customStyles} />} + <a + className={cx('signature-link', className)} + target='_blank' + href={href}> + {imgSrc ? <img src={imgSrc} /> : text} + {className && customStyles && ( + <CustomStylesForCssClass + cssClass={className} + styles={customStyles} + /> + )} </a> - ) -} + ); +}; - -export type SignatureLinkProps = Props +export type SignatureLinkProps = Props; diff --git a/src/App/templates/ExpandableTemplate/Signature/SignatureTemplate.tsx b/src/App/templates/ExpandableTemplate/Signature/SignatureTemplate.tsx index 12a439fe13..eb508dd803 100644 --- a/src/App/templates/ExpandableTemplate/Signature/SignatureTemplate.tsx +++ b/src/App/templates/ExpandableTemplate/Signature/SignatureTemplate.tsx @@ -1,17 +1,16 @@ -import * as React from 'react' +import * as React from 'react'; export type Props = { - botId: string -} + botId: string; +}; export const SignatureTemplate: React.StatelessComponent = ({ children }) => { return ( - <div className="feedbot-signature"> - <div className="feedbot-signature-row"> - <div style={{ alignSelf: 'center' }}>with ❤️ by</div> + <div className='feedbot-signature'> + <div className='feedbot-signature-row'> + <div style={{ alignSelf: 'center' }}>powered by</div> {children} </div> </div> - ) -} - + ); +}; diff --git a/src/Shell.tsx b/src/Shell.tsx index 66e4a3ebab..c1b8305d1b 100644 --- a/src/Shell.tsx +++ b/src/Shell.tsx @@ -4,255 +4,307 @@ import { User } from 'botframework-directlinejs'; import { classList } from './Chat'; import { Dispatch, connect } from 'react-redux'; import { Strings } from './Strings'; -import { Speech } from './SpeechModule' -import { ChatActions, ListeningState, sendMessage, sendFiles, sendScreenshot } from './Store'; +import { Speech } from './SpeechModule'; +import { + ChatActions, + ListeningState, + sendMessage, + sendFiles, + sendScreenshot, +} from './Store'; -import * as html2canvas from 'html2canvas' +import * as html2canvas from 'html2canvas'; interface Props { - inputText: string, - strings: Strings, - listeningState: ListeningState, - showUploadButton: boolean, - uploadCapture: 'image/*' | 'video/*' | 'audio/*' | string, - disableInput: boolean + inputText: string; + strings: Strings; + listeningState: ListeningState; + showUploadButton: boolean; + uploadCapture: 'image/*' | 'video/*' | 'audio/*' | string; + disableInput: boolean; - onChangeText: (inputText: string) => void + onChangeText: (inputText: string) => void; - sendMessage: (inputText: string) => void, - sendFiles: (files: FileList) => void, - sendScreenshot: (screen: string) => void, - stopListening: () => void, - startListening: () => void + sendMessage: (inputText: string) => void; + sendFiles: (files: FileList) => void; + sendScreenshot: (screen: string) => void; + stopListening: () => void; + startListening: () => void; } export interface ShellFunctions { - focus: (appendKey?: string) => void + focus: (appendKey?: string) => void; } class ShellContainer extends React.Component<Props> implements ShellFunctions { - private textInput: HTMLTextAreaElement; - private fileInput: HTMLInputElement; + private textInput: HTMLTextAreaElement; + private fileInput: HTMLInputElement; - componentDidUpdate(prevProps: Props) { - if (prevProps.disableInput === true && this.props.disableInput === false) { - this.textInput.focus(); - } - } + componentDidUpdate(prevProps: Props) { + if ( + prevProps.disableInput === true && + this.props.disableInput === false + ) { + this.textInput.focus(); + } + } - private sendMessage() { - if (this.props.inputText.trim().length > 0) { - this.props.sendMessage(this.props.inputText); - } - } + private sendMessage() { + if (this.props.inputText.trim().length > 0) { + this.props.sendMessage(this.props.inputText); + } + } - private handleSendButtonKeyPress(evt: React.KeyboardEvent<HTMLButtonElement>) { - if (evt.key === 'Enter' || evt.key === ' ') { - evt.preventDefault(); - this.sendMessage(); - this.textInput.focus(); - } - } + private handleSendButtonKeyPress( + evt: React.KeyboardEvent<HTMLButtonElement> + ) { + if (evt.key === 'Enter' || evt.key === ' ') { + evt.preventDefault(); + this.sendMessage(); + this.textInput.focus(); + } + } - private handleUploadButtonKeyPress(evt: React.KeyboardEvent<HTMLLabelElement>) { - if (evt.key === 'Enter' || evt.key === ' ') { - evt.preventDefault(); - this.fileInput.click(); - } - } + private handleUploadButtonKeyPress( + evt: React.KeyboardEvent<HTMLLabelElement> + ) { + if (evt.key === 'Enter' || evt.key === ' ') { + evt.preventDefault(); + this.fileInput.click(); + } + } - private onKeyPress(e: React.KeyboardEvent<HTMLTextAreaElement>) { - if (e.key === 'Enter' && !e.shiftKey) { - this.sendMessage(); - e.stopPropagation() - e.preventDefault() - } - } + private onKeyPress(e: React.KeyboardEvent<HTMLTextAreaElement>) { + if (e.key === 'Enter' && !e.shiftKey) { + this.sendMessage(); + e.stopPropagation(); + e.preventDefault(); + } + } - private onClickSend() { - this.sendMessage(); - } + private onClickSend() { + this.sendMessage(); + } - private onChangeFile() { - this.props.sendFiles(this.fileInput.files); - this.fileInput.value = null; - this.textInput.focus(); - } + private onChangeFile() { + this.props.sendFiles(this.fileInput.files); + this.fileInput.value = null; + this.textInput.focus(); + } - private onTextInputFocus() { - if (this.props.listeningState === ListeningState.STARTED) { - this.props.stopListening(); - } - } + private onTextInputFocus() { + if (this.props.listeningState === ListeningState.STARTED) { + this.props.stopListening(); + } + } - private onClickMic() { - if (this.props.listeningState === ListeningState.STARTED) { - this.props.stopListening(); - } else if (this.props.listeningState === ListeningState.STOPPED) { - this.props.startListening(); - } - } + private onClickMic() { + if (this.props.listeningState === ListeningState.STARTED) { + this.props.stopListening(); + } else if (this.props.listeningState === ListeningState.STOPPED) { + this.props.startListening(); + } + } - public focus(appendKey?: string) { - this.textInput.focus(); + public focus(appendKey?: string) { + this.textInput.focus(); - if (appendKey) { - this.props.onChangeText(this.props.inputText + appendKey); - } - } + if (appendKey) { + this.props.onChangeText(this.props.inputText + appendKey); + } + } - private async takeScreenshot() { - const screen = await html2canvas(document.body, { allowTaint: true, useCORS: true }).then((canvas) => { - const dataURI = canvas.toDataURL("image/png"); + private async takeScreenshot() { + const screen = await html2canvas(document.body, { + allowTaint: true, + useCORS: true, + }).then((canvas) => { + const dataURI = canvas.toDataURL('image/png'); - return dataURI - }) - this.props.sendScreenshot(screen); - } + return dataURI; + }); + this.props.sendScreenshot(screen); + } - render() { - const className = classList( - 'wc-console', - this.props.inputText.length > 0 && 'has-text', - this.props.showUploadButton && 'has-upload-button', - this.props.disableInput && 'disable-input' - ); + render() { + const className = classList( + 'wc-console', + this.props.inputText.length > 0 && 'has-text', + this.props.showUploadButton && 'has-upload-button', + this.props.disableInput && 'disable-input' + ); - const showMicButton = this.props.listeningState !== ListeningState.STOPPED || (Speech.SpeechRecognizer.speechIsAvailable() && !this.props.inputText.length); + const showMicButton = + this.props.listeningState !== ListeningState.STOPPED || + (Speech.SpeechRecognizer.speechIsAvailable() && + !this.props.inputText.length); - const sendButtonClassName = classList( - 'wc-send', - showMicButton && 'hidden' - ); + const sendButtonClassName = classList( + 'wc-send', + showMicButton && 'hidden' + ); - const micButtonClassName = classList( - 'wc-mic', - !showMicButton && 'hidden', - this.props.listeningState === ListeningState.STARTED && 'active', - this.props.listeningState !== ListeningState.STARTED && 'inactive' - ); + const micButtonClassName = classList( + 'wc-mic', + !showMicButton && 'hidden', + this.props.listeningState === ListeningState.STARTED && 'active', + this.props.listeningState !== ListeningState.STARTED && 'inactive' + ); - const placeholder = this.props.listeningState === ListeningState.STARTED ? this.props.strings.listeningIndicator : this.props.strings.consolePlaceholder; + const placeholder = + this.props.listeningState === ListeningState.STARTED + ? this.props.strings.listeningIndicator + : this.props.strings.consolePlaceholder; - return ( - <div className={className}> - { - this.props.showUploadButton && - <label - className="wc-upload" - htmlFor="wc-upload-input" - onKeyPress={evt => this.handleUploadButtonKeyPress(evt)} - tabIndex={0} - > - <svg> - <path d="M19.96 4.79m-2 0a2 2 0 0 1 4 0 2 2 0 0 1-4 0zM8.32 4.19L2.5 15.53 22.45 15.53 17.46 8.56 14.42 11.18 8.32 4.19ZM1.04 1L1.04 17 24.96 17 24.96 1 1.04 1ZM1.03 0L24.96 0C25.54 0 26 0.45 26 0.99L26 17.01C26 17.55 25.53 18 24.96 18L1.03 18C0.46 18 0 17.55 0 17.01L0 0.99C0 0.45 0.47 0 1.03 0Z" /> - </svg> - </label> - } - { - this.props.showUploadButton && - <input - id="wc-upload-input" - tabIndex={-1} - type="file" - ref={input => this.fileInput = input} - // multiple - onChange={() => this.onChangeFile()} - aria-label={this.props.strings.uploadFile} - role="button" - capture={!!this.props.uploadCapture} - accept={this.props.uploadCapture} - /> - } - { - this.props.showUploadButton && - <button className="wc-upload-screenshot" onClick={() => { this.takeScreenshot() }}><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="camera" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#8a8a8a" d="M512 144v288c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V144c0-26.5 21.5-48 48-48h88l12.3-32.9c7-18.7 24.9-31.1 44.9-31.1h125.5c20 0 37.9 12.4 44.9 31.1L376 96h88c26.5 0 48 21.5 48 48zM376 288c0-66.2-53.8-120-120-120s-120 53.8-120 120 53.8 120 120 120 120-53.8 120-120zm-32 0c0 48.5-39.5 88-88 88s-88-39.5-88-88 39.5-88 88-88 88 39.5 88 88z"></path></svg></button> - } - <div className="wc-textbox"> - <textarea - className="wc-shellinput" - ref={input => this.textInput = input} - autoFocus - value={this.props.inputText} - onChange={_ => this.props.onChangeText(this.textInput.value)} - onKeyPress={e => this.onKeyPress(e)} - onFocus={() => this.onTextInputFocus()} - placeholder={placeholder} - disabled={this.props.disableInput} - aria-label={this.props.inputText ? null : placeholder} - aria-live="polite" - ></textarea> - </div> - <button - className={sendButtonClassName} - onClick={() => this.onClickSend()} - aria-label={this.props.strings.send} - role="button" - onKeyPress={evt => this.handleSendButtonKeyPress(evt)} - tabIndex={0} - type="button" - > - <svg> - <path d="M26.79 9.38A0.31 0.31 0 0 0 26.79 8.79L0.41 0.02C0.36 0 0.34 0 0.32 0 0.14 0 0 0.13 0 0.29 0 0.33 0.01 0.37 0.03 0.41L3.44 9.08 0.03 17.76A0.29 0.29 0 0 0 0.01 17.8 0.28 0.28 0 0 0 0.01 17.86C0.01 18.02 0.14 18.16 0.3 18.16A0.3 0.3 0 0 0 0.41 18.14L26.79 9.38ZM0.81 0.79L24.84 8.79 3.98 8.79 0.81 0.79ZM3.98 9.37L24.84 9.37 0.81 17.37 3.98 9.37Z" /> - </svg> - </button> - <button - className={micButtonClassName} - onClick={() => this.onClickMic()} - aria-label={this.props.strings.speak} - role="button" - tabIndex={0} - type="button" - > - <svg width="28" height="22" viewBox="0 0 58 58" > - <path d="M 44 28 C 43.448 28 43 28.447 43 29 L 43 35 C 43 42.72 36.72 49 29 49 C 21.28 49 15 42.72 15 35 L 15 29 C 15 28.447 14.552 28 14 28 C 13.448 28 13 28.447 13 29 L 13 35 C 13 43.485 19.644 50.429 28 50.949 L 28 56 L 23 56 C 22.448 56 22 56.447 22 57 C 22 57.553 22.448 58 23 58 L 35 58 C 35.552 58 36 57.553 36 57 C 36 56.447 35.552 56 35 56 L 30 56 L 30 50.949 C 38.356 50.429 45 43.484 45 35 L 45 29 C 45 28.447 44.552 28 44 28 Z" /> - <path id="micFilling" d="M 28.97 44.438 L 28.97 44.438 C 23.773 44.438 19.521 40.033 19.521 34.649 L 19.521 11.156 C 19.521 5.772 23.773 1.368 28.97 1.368 L 28.97 1.368 C 34.166 1.368 38.418 5.772 38.418 11.156 L 38.418 34.649 C 38.418 40.033 34.166 44.438 28.97 44.438 Z" /> - <path d="M 29 46 C 35.065 46 40 41.065 40 35 L 40 11 C 40 4.935 35.065 0 29 0 C 22.935 0 18 4.935 18 11 L 18 35 C 18 41.065 22.935 46 29 46 Z M 20 11 C 20 6.037 24.038 2 29 2 C 33.962 2 38 6.037 38 11 L 38 35 C 38 39.963 33.962 44 29 44 C 24.038 44 20 39.963 20 35 L 20 11 Z" /> - </svg> - </button> - </div> - ); - } + return ( + <div className={className}> + {this.props.showUploadButton && ( + <label + className='wc-upload' + htmlFor='wc-upload-input' + onKeyPress={(evt) => + this.handleUploadButtonKeyPress(evt) + } + tabIndex={0}> + <svg> + <path d='M0.850098 3.99961C0.850098 2.25991 2.2604 0.849609 4.0001 0.849609H10.0001C10.199 0.849609 10.3898 0.928627 10.5304 1.06928L16.5304 7.06928C16.6711 7.20993 16.7501 7.4007 16.7501 7.59961V15.9996C16.7501 17.7393 15.3398 19.1496 13.6001 19.1496H4.0001C2.2604 19.1496 0.850098 17.7393 0.850098 15.9996V3.99961ZM4.0001 2.34961C3.08883 2.34961 2.3501 3.08834 2.3501 3.99961V15.9996C2.3501 16.9109 3.08883 17.6496 4.0001 17.6496H13.6001C14.5114 17.6496 15.2501 16.9109 15.2501 15.9996V7.91027L9.68944 2.34961H4.0001Z' /> + <path d='M10 0.849609C10.4142 0.849609 10.75 1.1854 10.75 1.59961V5.19961C10.75 6.11088 11.4887 6.84961 12.4 6.84961H16C16.4142 6.84961 16.75 7.1854 16.75 7.59961C16.75 8.01382 16.4142 8.34961 16 8.34961H12.4C10.6603 8.34961 9.25 6.93931 9.25 5.19961V1.59961C9.25 1.1854 9.58579 0.849609 10 0.849609Z' /> + </svg> + </label> + )} + {this.props.showUploadButton && ( + <input + id='wc-upload-input' + tabIndex={-1} + type='file' + ref={(input) => (this.fileInput = input)} + // multiple + onChange={() => this.onChangeFile()} + aria-label={this.props.strings.uploadFile} + role='button' + capture={!!this.props.uploadCapture} + accept={this.props.uploadCapture} + /> + )} + {this.props.showUploadButton && ( + <button + className='wc-upload-screenshot' + onClick={() => { + this.takeScreenshot(); + }}> + <svg + aria-hidden='true' + focusable='false' + data-prefix='fas' + data-icon='camera' + role='img' + xmlns='http://www.w3.org/2000/svg' + viewBox='0 0 512 512'> + <path + fill='#8a8a8a' + d='M512 144v288c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V144c0-26.5 21.5-48 48-48h88l12.3-32.9c7-18.7 24.9-31.1 44.9-31.1h125.5c20 0 37.9 12.4 44.9 31.1L376 96h88c26.5 0 48 21.5 48 48zM376 288c0-66.2-53.8-120-120-120s-120 53.8-120 120 53.8 120 120 120 120-53.8 120-120zm-32 0c0 48.5-39.5 88-88 88s-88-39.5-88-88 39.5-88 88-88 88 39.5 88 88z'></path> + </svg> + </button> + )} + <div className='wc-textbox'> + <textarea + className='wc-shellinput' + ref={(input) => (this.textInput = input)} + autoFocus + value={this.props.inputText} + onChange={(_) => + this.props.onChangeText(this.textInput.value) + } + onKeyPress={(e) => this.onKeyPress(e)} + onFocus={() => this.onTextInputFocus()} + placeholder={placeholder} + disabled={this.props.disableInput} + aria-label={this.props.inputText ? null : placeholder} + aria-live='polite'></textarea> + </div> + <button + className={sendButtonClassName} + onClick={() => this.onClickSend()} + aria-label={this.props.strings.send} + role='button' + onKeyPress={(evt) => this.handleSendButtonKeyPress(evt)} + tabIndex={0} + type='button'> + <svg> + <path d='M3.03792 2.33702C2.89522 2.26748 2.73411 2.245 2.57777 2.27287C2.42024 2.30095 2.27582 2.37868 2.16561 2.49469C2.05541 2.61071 1.9852 2.75893 1.96524 2.91769C1.9458 3.0723 1.97502 3.22902 2.04866 3.36612L7.09739 11.601C7.24476 11.8414 7.24487 12.1441 7.09769 12.3845L2.04877 20.6337C1.97505 20.7708 1.94579 20.9276 1.96524 21.0823C1.9852 21.2411 2.05541 21.3893 2.16561 21.5053C2.27582 21.6213 2.42024 21.6991 2.57777 21.7271C2.73406 21.755 2.89513 21.7325 3.03779 21.6631L22.047 11.9935L3.03792 2.33702ZM2.31455 0.796147C2.78714 0.711908 3.27429 0.781026 3.7048 0.9934L3.7127 0.997297L22.7267 10.6563C22.9734 10.7818 23.1809 10.9733 23.3257 11.2093C23.4704 11.4453 23.547 11.7167 23.547 11.9935C23.547 12.2703 23.4704 12.5417 23.3257 12.7777C23.1809 13.0137 22.9738 13.205 22.727 13.3305C22.727 13.3305 22.7271 13.3305 22.727 13.3305L3.70482 23.0067C3.27432 23.219 2.78714 23.2881 2.31455 23.2039C1.84196 23.1196 1.40869 22.8865 1.07808 22.5384C0.74746 22.1904 0.536825 21.7457 0.476953 21.2694C0.41708 20.7931 0.51111 20.3102 0.7453 19.8911C0.750131 19.8825 0.755132 19.8739 0.760301 19.8655L5.57846 11.9933L0.760597 4.13502C0.755323 4.12642 0.750223 4.11771 0.7453 4.1089C0.51111 3.68986 0.41708 3.2069 0.476953 2.73061C0.536825 2.25431 0.747461 1.80965 1.07808 1.46161C1.40869 1.11357 1.84196 0.880386 2.31455 0.796147Z' /> + <path d='M5.70299 11.993C5.70299 11.5788 6.03877 11.243 6.45299 11.243H22.797C23.2112 11.243 23.547 11.5788 23.547 11.993C23.547 12.4072 23.2112 12.743 22.797 12.743H6.45299C6.03877 12.743 5.70299 12.4072 5.70299 11.993Z' /> + </svg> + </button> + <button + className={micButtonClassName} + onClick={() => this.onClickMic()} + aria-label={this.props.strings.speak} + role='button' + tabIndex={0} + type='button'> + <svg width='28' height='22' viewBox='0 0 58 58'> + <path d='M 44 28 C 43.448 28 43 28.447 43 29 L 43 35 C 43 42.72 36.72 49 29 49 C 21.28 49 15 42.72 15 35 L 15 29 C 15 28.447 14.552 28 14 28 C 13.448 28 13 28.447 13 29 L 13 35 C 13 43.485 19.644 50.429 28 50.949 L 28 56 L 23 56 C 22.448 56 22 56.447 22 57 C 22 57.553 22.448 58 23 58 L 35 58 C 35.552 58 36 57.553 36 57 C 36 56.447 35.552 56 35 56 L 30 56 L 30 50.949 C 38.356 50.429 45 43.484 45 35 L 45 29 C 45 28.447 44.552 28 44 28 Z' /> + <path + id='micFilling' + d='M 28.97 44.438 L 28.97 44.438 C 23.773 44.438 19.521 40.033 19.521 34.649 L 19.521 11.156 C 19.521 5.772 23.773 1.368 28.97 1.368 L 28.97 1.368 C 34.166 1.368 38.418 5.772 38.418 11.156 L 38.418 34.649 C 38.418 40.033 34.166 44.438 28.97 44.438 Z' + /> + <path d='M 29 46 C 35.065 46 40 41.065 40 35 L 40 11 C 40 4.935 35.065 0 29 0 C 22.935 0 18 4.935 18 11 L 18 35 C 18 41.065 22.935 46 29 46 Z M 20 11 C 20 6.037 24.038 2 29 2 C 33.962 2 38 6.037 38 11 L 38 35 C 38 39.963 33.962 44 29 44 C 24.038 44 20 39.963 20 35 L 20 11 Z' /> + </svg> + </button> + </div> + ); + } } export const Shell = connect( - (state: ChatState) => ({ - // passed down to ShellContainer - inputText: state.shell.input, - showUploadButton: state.format.showUploadButton, - uploadCapture: state.format.uploadCapture, - disableInput: state.format.disableInput, - strings: state.format.strings, - // only used to create helper functions below - locale: state.format.locale, - user: state.connection.user, - listeningState: state.shell.listeningState - }), { - // passed down to ShellContainer - onChangeText: (input: string) => ({ type: 'Update_Input', input, source: "text" } as ChatActions), - stopListening: () => ({ type: 'Listening_Stopping' }), - startListening: () => ({ type: 'Listening_Starting' }), - // only used to create helper functions below - sendMessage, - sendFiles, - sendScreenshot -}, (stateProps: any, dispatchProps: any, ownProps: any): Props => ({ - // from stateProps - inputText: stateProps.inputText, - showUploadButton: stateProps.showUploadButton, - uploadCapture: stateProps.uploadCapture, - disableInput: stateProps.disableInput, - strings: stateProps.strings, - listeningState: stateProps.listeningState, - // from dispatchProps - onChangeText: dispatchProps.onChangeText, - // helper functions - sendMessage: (text: string) => dispatchProps.sendMessage(text, stateProps.user, stateProps.locale), - sendFiles: (files: FileList) => dispatchProps.sendFiles(files, stateProps.user, stateProps.locale), - sendScreenshot: (screen: string) => dispatchProps.sendScreenshot(screen, stateProps.user, stateProps.locale), - startListening: () => dispatchProps.startListening(), - stopListening: () => dispatchProps.stopListening() -}), { - withRef: true -} + (state: ChatState) => ({ + // passed down to ShellContainer + inputText: state.shell.input, + showUploadButton: state.format.showUploadButton, + uploadCapture: state.format.uploadCapture, + disableInput: state.format.disableInput, + strings: state.format.strings, + // only used to create helper functions below + locale: state.format.locale, + user: state.connection.user, + listeningState: state.shell.listeningState, + }), + { + // passed down to ShellContainer + onChangeText: (input: string) => + ({ type: 'Update_Input', input, source: 'text' } as ChatActions), + stopListening: () => ({ type: 'Listening_Stopping' }), + startListening: () => ({ type: 'Listening_Starting' }), + // only used to create helper functions below + sendMessage, + sendFiles, + sendScreenshot, + }, + (stateProps: any, dispatchProps: any, ownProps: any): Props => ({ + // from stateProps + inputText: stateProps.inputText, + showUploadButton: stateProps.showUploadButton, + uploadCapture: stateProps.uploadCapture, + disableInput: stateProps.disableInput, + strings: stateProps.strings, + listeningState: stateProps.listeningState, + // from dispatchProps + onChangeText: dispatchProps.onChangeText, + // helper functions + sendMessage: (text: string) => + dispatchProps.sendMessage(text, stateProps.user, stateProps.locale), + sendFiles: (files: FileList) => + dispatchProps.sendFiles(files, stateProps.user, stateProps.locale), + sendScreenshot: (screen: string) => + dispatchProps.sendScreenshot( + screen, + stateProps.user, + stateProps.locale + ), + startListening: () => dispatchProps.startListening(), + stopListening: () => dispatchProps.stopListening(), + }), + { + withRef: true, + } )(ShellContainer); diff --git a/src/scss/botchat-redesign.scss b/src/scss/botchat-redesign.scss new file mode 100644 index 0000000000..04fff57e7b --- /dev/null +++ b/src/scss/botchat-redesign.scss @@ -0,0 +1,1122 @@ +@import 'includes/card-size'; +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap'); + +// Colors +$black: #16364d; +$white: #ffffff; +$silver: #c7d0d7; +$silverLight: #f7f9fb; +$silverMedium: #dae0e5; +$grey: #628198; +$greyDark: #385b75; +$blue: #0063f8; +$blueLight: #ebf6ff; +$blueDark: #0a4fbd; +$shadow: rgba(0, 0, 0, 0.05); +$c_messageFromMe: $blue; +$c_messageFromThem: $white; + +// Settings +$chatWindowWidth: 420px; +$chatWindowHeight: 620px; + +$actionsHeight: 40px; +$consoleHeight: 70px; + +$fontFamily: 'Inter', sans-serif; +$fontSize: 15px; + +$headerTotalHeight: 38px; + +$actionTransition: 0.2s cubic-bezier(0, 0, 0.5, 1); + +$card_narrow: 216px; +$card_normal: 320px; +$card_wide: 416px; + +$card_borderWidth: 1px; +$card_padding: 8px; + +.feedbot-wrapper { + width: $chatWindowWidth; + height: $chatWindowHeight; + bottom: 24px; + right: 24px; + border-radius: 10px; + background-color: $white; + min-width: 275px; + max-width: 87%; + max-height: 90%; + position: fixed; + z-index: 100000; +} +.feedbot-wrapper:not(.collapsed) { + border: 1px solid $silverMedium; +} +.feedbot-header { + border-top-left-radius: 10px; + border-top-right-radius: 10px; + color: $greyDark; + z-index: 10; + padding: 18px; + cursor: pointer; + font-size: 1em; + display: flex; + align-items: center; + font-family: $fontFamily; +} +.feedbot-header .feedbot-title { + font-weight: 500; + flex-grow: 1; +} +.feedbot-header .feedbot-minimize { + font-weight: bolder; + font-family: Verdana; + color: $greyDark; + font-size: 1.2em; + line-height: 0.9em; +} +.feedbot-wrapper .wc-chatview-panel { + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; +} +.wc-app .wc-chatview-panel { + top: 75px; +} +.wc-app .wc-header { + display: none; +} +.wc-message-wrapper:not([data-activity-id='retry']) .wc-message-from { + display: none; +} +.wc-carousel + .wc-hscroll + > ul + > li + > .wc-card + > div + > .ac-container + > .ac-container + .ac-image { + border-radius: 10px 10px 0 0; +} +.wc-carousel .wc-hscroll ul { + display: flex; + align-items: stretch; +} +.wc-carousel .wc-hscroll > ul > li > .wc-card, +.wc-carousel .wc-hscroll > ul > li > .wc-card > div { + height: 100%; +} +.feedbot-wrapper .wc-adaptive-card, +.feedbot-wrapper .wc-card { + max-width: 100%; +} +.wc-carousel .wc-hscroll > ul > li > .wc-card > div > .ac-container { + padding: 0 0 10px 0 !important; + height: 100%; + justify-content: space-between !important; +} +.wc-carousel + .wc-hscroll + > ul + > li + > .wc-card + > div + > .ac-container + > .ac-container + .ac-textBlock { + padding: 0px 13px; + white-space: unset !important; + text-overflow: unset !important; + overflow: unset !important; + font-family: $fontFamily !important; +} +.wc-carousel .wc-hscroll > ul > li > .wc-card > div .ac-actionSet { + margin: 4px 13px 13px !important; +} +.feedbot-wrapper .wc-app .wc-card button, +.feedbot-wrapper .wc-app .wc-card button > div { + text-overflow: initial !important; + white-space: initial !important; +} +.feedbot-wrapper .wc-suggested-actions .wc-hscroll > ul > li button:focus, +.feedbot-wrapper .wc-console .wc-mic, +.wc-console .wc-send, +.feedbot-wrapper .wc-app .wc-card button { + outline: 0; +} +.wc-carousel .wc-hscroll > ul > li { + padding: 4px !important; +} +.feedbot-wrapper.collapsed { + border-radius: 40px; + width: 75px; + min-width: auto; + height: 75px; +} +.feedbot-wrapper.collapsed .feedbot-header { + border-radius: 40px; + height: 100%; + width: 100%; + padding: 0px; + text-indent: 999%; + white-space: nowrap; + overflow: hidden; + font-size: 0px; + /* background image (chat icon) is set in redesign template */ + background-size: 45px; + background-position: center; + background-repeat: no-repeat; + transition: transform 0.3s ease; +} +.feedbot-wrapper.collapsed .feedbot-header:hover { + transform: scale(1.05); + transition: transform 0.3s ease; +} +.feedbot-wrapper.collapsed .wc-chatview-panel { + display: none; +} +.feedbot-wrapper.collapsed .feedbot-signature, +.feedbot-wrapper.collapsed .feedbot-header .feedbot-extra-html, +.feedbot-disabled div.feedbot { + display: none; +} +.wc-list.tiles .ac-actionSet { + flex-direction: row !important; + flex-wrap: wrap; + justify-content: center; +} + +.wc-list.tiles .ac-container { + padding-top: 0px; + margin-top: -5px; +} + +.wc-list.tiles .ac-pushButton { + flex-basis: 44% !important; + min-height: 120px !important; + margin: 3% !important; + flex-direction: column !important; + position: relative; + padding: 16px; + top: 0; +} + +.feedbot-wrapper .wc-app .wc-list.tiles .wc-card .ac-pushButton:active { + background-color: unset; +} + +.wc-list.tiles .ac-pushButton img { + width: 36px !important; + height: 36px !important; + margin-right: 0px !important; + margin-bottom: 10px !important; +} + +.wc-list.tiles .ac-pushButton div { + overflow: unset !important; + text-overflow: unset !important; + white-space: unset !important; +} + +@media (max-width: 450px) { + .feedbot-wrapper .wc-list.tiles .wc-card { + border: none; + width: 100%; + } + .wc-list.tiles .ac-pushButton { + min-height: 95px !important; + } + .wc-list.tiles .ac-container { + padding: 0 !important; + margin-top: 0 !important; + } +} +.wc-upload-screenshot { + display: none !important; +} +.feedbot-wrapper.collapsed .feedbot-signature { + display: none; +} + +.feedbot-wrapper .feedbot-signature { + position: absolute; + bottom: -23px; + font-size: 13px; + right: -5px; + opacity: 0.3; + font-family: $fontFamily; + + height: 22px; + display: block; + align-items: center; + -webkit-transition: opacity 0.3s ease-in-out; + -moz-transition: opacity 0.3s ease-in-out; + -ms-transition: opacity 0.3s ease-in-out; + -o-transition: opacity 0.3s ease-in-out; + transition: opacity 0.3s ease-in-out; +} + +.feedbot-wrapper .feedbot-signature:hover { + opacity: 0.8; +} + +.feedbot-signature a { + transition: 0.3s; + color: black; + text-decoration: none; + margin: 0 4px; + display: flex; + align-items: center; +} + +.feedbot-signature a:hover { + cursor: pointer; +} + +.feedbot-signature a img { + height: 22px; +} + +.feedbot-signature-row { + display: flex; + height: 100%; +} +/* +* +* +/* reset */ +body .wc-app, +.wc-app button, +.wc-app input, +.wc-app textarea, +.popup-message { + font-family: $fontFamily; + font-size: $fontSize; +} + +.wc-app button { + background-color: $white; + border: 1px solid $silverMedium; + border-radius: 1px; + color: $blueDark; + cursor: pointer; + transition: color 0.2s ease, background-color 0.2s ease; +} + +.wc-app h1, +.wc-app h2, +.wc-app h3, +.wc-app h4, +.wc-app p, +.wc-app ul, +.wc-app ol { + margin: 0; + padding: 0; +} + +.wc-app audio, +.wc-app video { + display: block; +} + +/* docking */ + +.wc-hidden { + visibility: hidden; +} + +.wc-time { + color: $silver; + margin-bottom: 10px; +} + +.wc-message-groups { + background: $silverLight; + bottom: $consoleHeight; + left: 0; + transform: translateY(0); + outline: 0; + overflow-x: hidden; + overflow-y: scroll; + padding: 10px 5px 0px 10px; + position: absolute; + right: 0; + top: 0; + transition: transform $actionTransition; + border-top: 1px solid $silverMedium; + + &.no-header { + top: 0; + } +} + +.wc-message-group-content { + overflow: hidden; +} + +.wc-suggested-actions { + background-color: $silverLight; + bottom: $consoleHeight; + height: 0; + left: 0; + overflow: hidden; + position: absolute; + right: 0; + transition: height $actionTransition; + + .wc-hscroll > ul { + height: $actionsHeight; + padding: 0px 18px; + + > li { + display: inline-block; + margin-right: 6px; + max-width: 40%; + + button { + background-color: $white; + color: $blueDark; + min-height: 40px; + overflow: hidden; + padding: 0 16px; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; + border-radius: 5px; + border-width: 1px; + font-weight: bold; + } + + button:focus, + button:hover { + background-color: $blueLight; + border-color: $blueDark; + border-width: 1px; + color: $blueDark; + } + + button:active { + background-color: $blueLight; + border-color: $blueDark; + border-width: 1px; + color: $blueDark; + } + } + } + + button.scroll { + background-color: $silver; + height: $actionsHeight; + overflow: hidden; + padding: 0; + position: absolute; + top: 0; + width: 28px; + } + + button.scroll:disabled { + display: none; + } + + button.scroll:focus, + button.scroll:hover { + background-color: $grey; + } + + button.scroll svg { + fill: $white; + + //TODO: use proper svg coords in the path data + path { + transform: translateY(6px); + } + } + + button.scroll.previous { + left: 0; + } + + button.scroll.next { + right: 0; + } +} + +.wc-message-pane.show-actions { + .wc-message-groups { + top: $headerTotalHeight + $actionsHeight; + transform: translateY(-$actionsHeight); + } + .wc-suggested-actions { + height: $actionsHeight; + } +} + +/* views */ + +.wc-chatview-panel { + overflow: hidden; + position: absolute; + right: 0; + left: 0; + top: 0; + bottom: 0; + background: $silverLight; +} + +/* messages */ + +.wc-message-wrapper { + animation: animationFrames 2s; + animation-iteration-count: 1; + clear: both; + margin-bottom: 10px; + overflow: hidden; + position: relative; + /*transition: max-height 2s ease-in-out;*/ +} + +@keyframes animationFrames { + 0% { + /*max-height: 0;*/ + opacity: 0; + } + 20% { + opacity: 1; + } + 100% { + /*max-height: 2000px;*/ + } +} + +.wc-message { + position: relative; +} + +.wc-message-wrapper.carousel .wc-message { + max-width: none; + padding-right: 8px; +} + +.wc-message svg.wc-message-callout { + display: none; +} + +.wc-message-content { + box-shadow: 0px 1px 2px 0px $shadow; + padding: 13px; + word-break: break-word; + border: 1px solid $silverMedium; +} + +.wc-message-content.clickable { + cursor: pointer; +} + +.wc-message-content.selected { + box-shadow: 0px 1px 1px 0px $blueDark; +} + +.wc-message-content img { + max-height: 320px; + max-width: 100%; + border-radius: 4px; +} + +.wc-message-content .video iframe { + border: 0; +} + +.wc-message-content audio, +.wc-message-content video { + max-width: 100%; +} + +.wc-message-content audio + h1, +.wc-message-content video + h1 { + margin-top: 11px; +} + +.wc-message-from { + clear: both; + color: $silver; + font-size: 11px; + margin-top: 5px; +} + +/* cards */ + +.wc-card { + background-color: $white; + border-radius: 10px; + border: 1px solid $silverMedium; + + .non-adaptive-content { + margin: $card_padding $card_padding 0 $card_padding; + } + + button { + border: 1px solid $silverMedium; + color: $blueDark; + background: $white; + font-weight: bold; + min-height: 40px; + border-radius: 5px; + flex: auto; + } + + button:hover { + background-color: $blueLight; + border: 2px solid $blueDark; + color: $blueDark; + } + + button:active { + background-color: $blueLight; + border: 2px solid $blueDark; + color: $blueDark; + } + + &.receipt table { + border-collapse: collapse; + width: 100%; + } + + &.receipt th, + &.receipt td { + text-align: right; + vertical-align: top; + } + + &.receipt th:first-child, + &.receipt td:first-child { + text-align: left; + } + + &.receipt th { + color: $grey; + font-size: inherit; + font-weight: normal; + line-height: 1.75; + } + + &.receipt thead tr:last-child th { + padding-bottom: 16px; + } + + &.receipt th[colspan='2'] { + color: inherit; + font-size: 15px; + font-weight: 700; + } + + &.receipt td { + padding: 4px 8px 0 8px; + } + + &.receipt td img { + float: left; + margin: 5px 8px 8px 0; + max-height: 50px; + max-width: 50px; + } + + &.receipt div.title { + font-weight: bolder; + } + + &.receipt div.subtitle { + font-weight: lighter; + } + + &.receipt tbody tr, + &.receipt tfoot tr { + border-top: 1px solid $silver; + } + + &.receipt tbody tr:first-child, + &.receipt tfoot tr:first-child { + border-top-width: 2px; + } + + &.receipt tfoot td { + line-height: 2.25; + } + + &.receipt tfoot .total { + font-weight: bold; + } + + &.thumbnail img { + float: right; + margin-bottom: 10px; + margin-left: 10px; + width: 100px; + } + + &.signin h1 { + margin: 10px 24px 16px 14px; + } + + &.error { + text-align: center; + + .error-icon { + fill: $silver; + height: 56px; + margin-bottom: 2px; + margin-top: 20px; + padding-left: 12px; + } + + .error-text { + color: $silver; + font-weight: 600; + letter-spacing: 0.5px; + margin-bottom: 20px; + text-align: inherit; + } + } +} + +/* alternate chat sizes */ + +.wc-message { + max-width: 91%; +} + +@include card-size($card_normal); + +.wc-wide { + @include card-size($card_wide); +} + +.wc-narrow { + @include card-size($card_narrow); +} + +/* adaptive card adjustments from wc-card */ +.wc-adaptive-card { + p { + margin-left: 0; + margin-right: 0; + } +} + +/* list */ +.wc-list { + .ac-container { + padding: 0 !important; + } +} +.wc-list > img { + margin-top: 10px; +} + +.wc-list > .wc-card { + margin-top: 8px; + border: 0 !important; +} + +.wc-list > .wc-card:first-child { + margin-top: 0; +} + +/* horizontal scroll */ + +.wc-hscroll-outer { + /* allow horizontal scrolling but hide the scrollbar */ + overflow: hidden; +} + +.wc-hscroll { + /* allow horizontal scrolling but hide the scrollbar */ + overflow-x: scroll; + overflow-y: hidden; +} + +.wc-hscroll > ul { + white-space: nowrap; +} + +.wc-hscroll > ul > li { + display: inline-block; + vertical-align: top; + white-space: normal; +} + +/* carousel */ + +.wc-carousel { + position: relative; + margin-top: 8px; + + button.scroll { + background-color: $blue; + height: 31px; + overflow: hidden; + padding: 0px; + position: absolute; + top: 50%; + width: 43px; + border-radius: 100px; + border: 2px solid $blueDark; + } + + button.scroll:disabled { + display: none; + } + + button.scroll:focus, + button.scroll:hover { + background-color: $blueDark; + border: 2px solid $blueDark; + } + + button.scroll svg { + fill: $white; + } + + button.scroll.previous { + left: -16px; + } + + button.scroll.next { + right: -16px; + } + + .wc-hscroll > ul { + margin-left: -4px; + } + + .wc-hscroll > ul > li { + padding: 0 4px; + } + + .wc-hscroll > ul > li:last-child { + padding-right: 0; + } + + li { + p { + min-height: 4em; + white-space: normal; + } + + .wc-adaptive-card p { + min-height: initial; + } + + .wc-adaptive-card-content > div > div > .ac-textBlock:first-child { + margin-top: 8px; + } + } +} + +/* from me */ + +.wc-message-from-me { + float: right; + margin-right: 6px; +} + +.wc-message-from-me.wc-message-from { + text-align: right; +} + +.wc-message-from-me .wc-message-content { + background-color: $c_messageFromMe; + border: 1px solid $blueDark; + color: $white; + border-radius: 10px 10px 4px 10px; +} + +/* from bot */ + +.wc-message-from-bot { + float: left; + margin-left: 8px; +} + +.wc-message-from-bot .wc-message-content { + background-color: $c_messageFromThem; + color: $black; + border-radius: 10px 10px 10px 4px; +} + +/* console */ + +.wc-console { + background: #ffffff; + border: 1px solid #c7d0d7; + border-radius: 5px; + margin: 18px; + bottom: 0; + box-sizing: border-box; + height: 45px; + left: 0; + position: absolute; + right: 0; + box-shadow: 0px 1px 2px 0px $shadow; + + > .wc-upload, + > .wc-textbox, + > .wc-send, + > .wc-mic { + position: absolute; + top: 0; + vertical-align: middle; + } + + label, + button { + cursor: pointer; + display: inline-block; + height: 43px; + } + + svg { + fill: $silver; + margin: 9px; + } + + input[type='text'], + textarea { + border: none; + height: calc(100% - 12px); + outline: none; + padding: 0; + resize: none; + width: 100%; + padding-top: 12px; + background-color: transparent; + color: $black; + min-height: initial; + box-shadow: none; + line-height: initial; + box-sizing: initial; + } + + textarea::placeholder { + color: $silver; + } + + &.has-text .wc-send svg { + fill: $blue; + } + &.has-text { + border: 1px solid $grey; + } + + .wc-upload { + cursor: pointer; + position: relative; + + svg { + height: 25px; + width: 25px; + } + } + + #wc-upload-input { + font-size: 0; + height: 0; + left: 0; + opacity: 0; + outline: 0; + position: absolute; + top: 0; + width: 0; + } + + .wc-send { + right: 0; + } + + .wc-send.hidden { + visibility: hidden; + } + + &.has-upload-button .wc-textbox { + left: 48px; + } + + .wc-textbox { + bottom: 0; + left: 11px; + right: 49px; + + input { + background-color: transparent; + word-break: break-word; + } + } + + .wc-mic, + .wc-send { + background-color: transparent; + border: 0; + padding: 0; + right: 0; + + &.hidden { + visibility: hidden; + } + } + + .wc-send { + svg { + height: 25px; + width: 25px; + } + } + + .wc-mic { + &.active path#micFilling { + fill: rgb(78, 55, 135); + } + + &.inactive path#micFilling { + visibility: hidden; + } + } +} + +.wc-console.disable-input { + display: none; +} + +.wc-console.has-text .wc-send svg { + fill: $blue; +} + +/* animation */ + +.wc-typing { + background-image: url('data:image/gif;base64,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'); + background-repeat: no-repeat; + height: 22px; + width: 50px; + background-size: contain; + background-position: center; +} + +.wc-animate-scroll { + left: 0; + position: absolute; + transition: left 0.8s ease; +} + +.wc-animate-scroll-rapid { + left: 0; + position: absolute; + transition: left 0.4s ease; +} + +.wc-animate-scroll-near { + left: 0; + position: absolute; + transition: left 0.3s ease-in-out; +} + +/* text formats */ + +.format-markdown > p { + margin-bottom: 0px; +} + +.format-markdown code { + white-space: pre-wrap; +} + +.format-markdown ol { + padding-left: 30px !important; /* numbers are right-aligned to the period */ +} + +.format-markdown ul { + padding-left: 33px !important; +} + +/* browser scrollbar customization */ + +.wc-app ::-webkit-scrollbar { + width: 8px; +} + +.wc-app ::-webkit-scrollbar * { + background-color: transparent; +} + +.wc-app ::-webkit-scrollbar-thumb { + background-color: $silver; +} + +/* download button for Unknown media */ + +.wc-icon-download { + display: inline-block; + height: 20px; + margin-left: 12px; + vertical-align: middle; + width: 15px; + + background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="25" viewBox="0 0 24 25" fill="none"><path d="M12 15.7828V1.78278" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M23 15.7828V22.7828C23 23.048 22.8946 23.3023 22.7071 23.4899C22.5196 23.6774 22.2652 23.7828 22 23.7828H2C1.73478 23.7828 1.48043 23.6774 1.29289 23.4899C1.10536 23.3023 1 23.048 1 22.7828V15.7828" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M17 10.7828L12 15.7828L7 10.7828" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'); + background-repeat: no-repeat; + background-size: contain; +} + +.wc-text-download { + display: inline-block; + font-weight: 500; + text-decoration: none; + color: $white; +} + +.ac-textBlock img.emoji { + width: auto !important; +} + +/* redesigned header */ +.feedbot-header { + .feedbot-header-name { + display: flex; + align-items: center; + gap: 10px; + } + + .feedbot-avatar, + .feedbot-avatar img { + width: 40px; + height: 40px; + border-radius: 100px; + background-position: center; + background-size: cover; + } +} +.feedbot-wrapper.collapsed .feedbot-avatar { + display: none; +} +.feedbot-wrapper.collapsed .feedbot-header:before { + position: absolute; + content: ''; + top: 0px; + left: 0; + right: 0; + bottom: 0; + border-radius: 100px; + z-index: 0; + filter: brightness(0.7); + /* border-color is set in redesign template */ + border-style: solid; + border-width: 2px; +} +.feedbot-name { + display: flex; + flex-direction: column; + gap: 3px; +} +.feedbot-supportive-title { + font-size: 13px; + color: $grey; +} diff --git a/src/themes/ExpandableBarTheme.tsx b/src/themes/ExpandableBarTheme.tsx index 8fdf46acd7..422b08601a 100644 --- a/src/themes/ExpandableBarTheme.tsx +++ b/src/themes/ExpandableBarTheme.tsx @@ -1,5 +1,5 @@ -import { BaseTheme } from './BaseTheme' -import { Theme } from './index' +import { BaseTheme } from './BaseTheme'; +import { Theme } from './index'; export const ExpandableBarTheme = (theme: Theme) => ` .feedbot-reset { @@ -92,4 +92,4 @@ export const ExpandableBarTheme = (theme: Theme) => ` .wc-carousel .wc-hscroll > ul > li > .wc-card > div > .ac-container > .ac-container .ac-image{ border-radius: 5px 5px 0 0; } -` +`; diff --git a/src/themes/ExpandableKnobTheme.tsx b/src/themes/ExpandableKnobTheme.tsx index b237e623d5..cff918bc36 100644 --- a/src/themes/ExpandableKnobTheme.tsx +++ b/src/themes/ExpandableKnobTheme.tsx @@ -1,5 +1,5 @@ -import { ExpandableBarTheme } from './ExpandableBarTheme' -import { Theme } from './index' +import { ExpandableBarTheme } from './ExpandableBarTheme'; +import { Theme } from './index'; export const ExpandableKnobTheme = (theme: Theme) => ` .feedbot-reset { @@ -56,10 +56,11 @@ export const ExpandableKnobTheme = (theme: Theme) => ` height: 100%; padding: 0px; - background-image: url(${(theme.template && theme.template.iconUrl) - ? theme.template.iconUrl - : 'https://cdn.feedyou.ai/webchat/message-icon.png' -}); + background-image: url(${ + theme.template && theme.template.iconUrl + ? theme.template.iconUrl + : 'https://cdn.feedyou.ai/webchat/message-icon.png' + }); background-size: 50px 50px; background-position: 12px 12px; background-repeat: no-repeat; @@ -124,4 +125,4 @@ export const ExpandableKnobTheme = (theme: Theme) => ` } ${ExpandableBarTheme(theme)} -` +`; diff --git a/src/themes/ExpandableKnobThemeNew.tsx b/src/themes/ExpandableKnobThemeNew.tsx new file mode 100644 index 0000000000..05a6eb6c52 --- /dev/null +++ b/src/themes/ExpandableKnobThemeNew.tsx @@ -0,0 +1,21 @@ +import { Theme } from './index'; + +export const ExpandableKnobThemeNew = (theme: Theme) => ` +.feedbot-wrapper.collapsed .feedbot-header { + background-image: url(${ + theme.template && theme.template.iconUrl + ? theme.template.iconUrl + : '"' + + "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 36 36' fill='none'%3E%3Cpath d='M11.291 18.562L11.2898 18.5622L11.2882 18.5618L11.2869 18.5606L11.2864 18.5589L11.2867 18.5576L11.2879 18.5561L11.2898 18.5556C11.2904 18.5557 11.2916 18.5561 11.2921 18.5565C11.2926 18.557 11.2929 18.5576 11.293 18.5582L11.2928 18.5602L11.291 18.562Z' fill='white'/%3E%3Cpath d='M18.0016 18.5617L18.0025 18.5607L18.0031 18.5589L18.0026 18.5572C18.0022 18.5568 18.001 18.5558 18.0004 18.5556L17.9985 18.5558L17.997 18.557L17.9964 18.5589L17.9967 18.5602L17.9974 18.5612C17.9979 18.5617 17.9985 18.562 17.9991 18.5622L18.0016 18.5617Z' fill='white'/%3E%3Cpath d='M24.711 18.562L24.7097 18.5622C24.7091 18.5621 24.7078 18.5617 24.7074 18.5612C24.7069 18.5608 24.7066 18.5602 24.7065 18.5595L24.7066 18.5576L24.7079 18.5561L24.7097 18.5556C24.71 18.5556 24.7109 18.5558 24.7112 18.5559C24.7115 18.5561 24.7109 18.5557 24.7112 18.5559C24.7117 18.5564 24.7129 18.5576 24.713 18.5582L24.7128 18.5602L24.711 18.562Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M19.4346 0.0453898C23.7144 0.351612 27.7405 2.19149 30.7729 5.22729C33.8088 8.25967 35.6487 12.2851 35.9546 16.5651C36.2605 20.8451 35.0123 25.0922 32.4388 28.5258C29.865 31.9593 26.1395 34.3494 21.9456 35.2567C17.7523 36.1643 13.3715 35.5283 9.60866 33.4655L3.46231 35.8297C2.84593 36.0668 2.16128 36.0551 1.55306 35.7973C0.944834 35.5394 0.460637 35.0551 0.202461 34.4469C-0.0557144 33.8388 -0.0673678 33.1541 0.169763 32.5377L2.53452 26.3913C0.471719 22.6289 -0.164628 18.2483 0.742975 14.0544C1.65029 9.86051 4.04038 6.1346 7.47397 3.56116C10.9074 0.98764 15.1546 -0.260549 19.4346 0.0453898ZM11.2898 17.4372C11.0679 17.4372 10.8511 17.503 10.6666 17.6263C10.4821 17.7495 10.3384 17.9247 10.2535 18.1297C10.1686 18.3346 10.1464 18.5601 10.1897 18.7777C10.2329 18.9953 10.3398 19.1952 10.4966 19.352C10.6535 19.5089 10.8534 19.6157 11.0709 19.659C11.2885 19.7023 11.514 19.6801 11.719 19.5952C11.924 19.5103 12.0991 19.3665 12.2224 19.1821C12.3456 18.9976 12.4114 18.7807 12.4114 18.5589C12.4114 18.2614 12.2932 17.9761 12.0829 17.7658C11.8725 17.5554 11.5872 17.4372 11.2898 17.4372ZM17.3766 17.6263C17.561 17.503 17.7779 17.4372 17.9997 17.4372C18.2972 17.4372 18.5825 17.5554 18.7929 17.7658C19.0032 17.9761 19.1214 18.2614 19.1214 18.5589C19.1214 18.7807 19.0556 18.9976 18.9324 19.1821C18.8091 19.3665 18.6339 19.5103 18.429 19.5952C18.224 19.6801 17.9985 19.7023 17.7809 19.659C17.5633 19.6157 17.3635 19.5089 17.2066 19.352C17.0497 19.1952 16.9429 18.9953 16.8996 18.7777C16.8564 18.5601 16.8786 18.3346 16.9635 18.1297C17.0484 17.9247 17.1921 17.7495 17.3766 17.6263ZM24.7097 17.4372C24.4879 17.4372 24.271 17.503 24.0866 17.6263C23.9021 17.7495 23.7583 17.9247 23.6734 18.1297C23.5885 18.3346 23.5663 18.5601 23.6096 18.7777C23.6529 18.9953 23.7597 19.1952 23.9166 19.352C24.0735 19.5089 24.2733 19.6157 24.4909 19.659C24.7085 19.7023 24.934 19.6801 25.139 19.5952C25.3439 19.5103 25.5191 19.3665 25.6423 19.1821C25.7656 18.9976 25.8314 18.7807 25.8314 18.5589C25.8314 18.2614 25.7132 17.9761 25.5028 17.7658C25.2925 17.5554 25.0072 17.4372 24.7097 17.4372Z' fill='white'/%3E%3C/svg%3E" + + '"' + }); +} +.feedbot-wrapper.collapsed .feedbot-header { + background-color: ${theme.mainColor ? theme.mainColor : '#0063f8'}; +} +.feedbot-wrapper.collapsed .feedbot-header:before { + border-color: ${theme.mainColor ? theme.mainColor : '#0063f8'}; +} + +${theme.customCss || ''} +`; diff --git a/src/themes/SidebarTheme.tsx b/src/themes/SidebarTheme.tsx index cd3d95f13a..a313884be4 100644 --- a/src/themes/SidebarTheme.tsx +++ b/src/themes/SidebarTheme.tsx @@ -1,9 +1,9 @@ -import { ExpandableKnobTheme } from './ExpandableKnobTheme' -import { Theme } from './index' +import { ExpandableKnobTheme } from './ExpandableKnobTheme'; +import { Theme } from './index'; function getSidebarBackgroundColor(theme: Theme) { - return '#e1e1e1' - + return '#e1e1e1'; + // TODO make background tint configurable in theme /*const color = theme.mainColor if(color.startsWith("rgb")){ @@ -79,7 +79,9 @@ export const SidebarTheme = (theme: Theme) => ` @supports ((-webkit-backdrop-filter: blur(40px)) or (backdrop-filter: blur(40px))) { .feedbot-wrapper { max-height: 100%; - background: linear-gradient(45deg, ${getSidebarBackgroundColor(theme)}33, #E1E1E1CE); + background: linear-gradient(45deg, ${getSidebarBackgroundColor( + theme + )}33, #E1E1E1CE); backdrop-filter: blur(40px); -webkit-backdrop-filter: blur(40px); } @@ -137,4 +139,4 @@ export const SidebarTheme = (theme: Theme) => ` justify-content: center; } -` +`; diff --git a/src/themes/index.ts b/src/themes/index.ts index 9bd784270b..3924c3a7f2 100644 --- a/src/themes/index.ts +++ b/src/themes/index.ts @@ -1,49 +1,58 @@ -import { ExpandableBarTheme } from './ExpandableBarTheme' -import { FullScreenTheme } from './FullScreenTheme' -import { ExpandableKnobTheme } from './ExpandableKnobTheme' -import { SidebarTheme } from './SidebarTheme' +import { ExpandableBarTheme } from './ExpandableBarTheme'; +import { FullScreenTheme } from './FullScreenTheme'; +import { ExpandableKnobTheme } from './ExpandableKnobTheme'; +import { SidebarTheme } from './SidebarTheme'; +import { ExpandableKnobThemeNew } from './ExpandableKnobThemeNew'; export type Theme = { mainColor: string; template?: { // Dost možná tu nějaký propy chyběj, // tak je neváhej připsat! :) - autoExpandTimeout?: number, - type?: string, - headerText?: string, - collapsedHeaderText?: string, + autoExpandTimeout?: number; + type?: string; + headerText?: string; + collapsedHeaderText?: string; popupMessage?: { - title: string, - description: string, - timeout: number - }, - iconUrl?: string, - customScript?: string, - logoUrl?: string, + title: string; + description: string; + timeout: number; + }; + iconUrl?: string; + customScript?: string; + logoUrl?: string; + avatar?: string; + supportiveTitle?: string; }; customCss?: string; - showSignature?: boolean, - enableScreenshotUpload?: boolean + showSignature?: boolean; + enableScreenshotUpload?: boolean; signature?: { - partnerLogoUrl: string, - partnerLogoStyle: string, - partnerLinkUrl: string, - mode: string - } + partnerLogoUrl: string; + partnerLogoStyle: string; + partnerLinkUrl: string; + mode: string; + }; }; export function getStyleForTheme(theme: Theme, remoteConfig: boolean): string { switch (theme && theme.template && theme.template.type) { case 'expandable-bar': - return ExpandableBarTheme(theme) + return ExpandableBarTheme(theme); case 'full-screen': - return FullScreenTheme(theme) + return FullScreenTheme(theme); case 'expandable-knob': - return ExpandableKnobTheme(theme) + return ExpandableKnobThemeNew( + theme + ); /* LP: pro dev; vrátit zpět ExpandableKnobTheme(theme);*/ case 'sidebar': - return SidebarTheme(theme) + return SidebarTheme(theme); + case 'expandable-knob-new': + return ExpandableKnobThemeNew(theme); } - + // backward compatibility - knob is new default for remote config, old default is bar - return remoteConfig ? ExpandableKnobTheme(theme) : ExpandableBarTheme(theme) + return remoteConfig + ? ExpandableKnobTheme(theme) + : ExpandableBarTheme(theme); } diff --git a/typing-indicator.gif b/typing-indicator.gif new file mode 100644 index 0000000000000000000000000000000000000000..379608723e4e58c49ff8db83e5abb522aac2caab GIT binary patch literal 14372 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 C@2E5Y literal 0 HcmV?d00001 From cbbb2f8700d6e74c5c0b1cab82df23de3b9c95b6 Mon Sep 17 00:00:00 2001 From: Lukas Podmelle <podmellelukas@gmail.com> Date: Wed, 10 Jan 2024 14:43:10 +0100 Subject: [PATCH 2/9] Redesign --- .prettierignore | 1 - .../ExpandableTemplate/ExpandableTemplate.tsx | 2 +- .../templates/ExpandableTemplate/Header.tsx | 18 ++---- .../Signature/Signature.tsx | 64 ++++++++++++++++--- .../Signature/SignatureLink.tsx | 4 +- src/scss/botchat-redesign.scss | 38 +++++------ src/themes/ExpandableKnobThemeNew.tsx | 10 ++- src/themes/SidebarThemeNew.tsx | 34 ++++++++++ src/themes/index.ts | 8 ++- 9 files changed, 127 insertions(+), 52 deletions(-) create mode 100644 src/themes/SidebarThemeNew.tsx diff --git a/.prettierignore b/.prettierignore index f59ec20aab..e69de29bb2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +0,0 @@ -* \ No newline at end of file diff --git a/src/App/templates/ExpandableTemplate/ExpandableTemplate.tsx b/src/App/templates/ExpandableTemplate/ExpandableTemplate.tsx index 3d7b5bf552..1026da6b68 100644 --- a/src/App/templates/ExpandableTemplate/ExpandableTemplate.tsx +++ b/src/App/templates/ExpandableTemplate/ExpandableTemplate.tsx @@ -94,7 +94,7 @@ export class ExpandableTemplate extends React.Component<Props, State> { </div> {signature && showSignature && - <Signature signature={signature} botId={bot.id} /> + <Signature signature={signature} botId={bot.id} appProps={this.props} /> } {this.doesTemplateSupportPopupMsg() && !initialized && popupMessage && ( diff --git a/src/App/templates/ExpandableTemplate/Header.tsx b/src/App/templates/ExpandableTemplate/Header.tsx index 2d25725345..9a8e80272c 100644 --- a/src/App/templates/ExpandableTemplate/Header.tsx +++ b/src/App/templates/ExpandableTemplate/Header.tsx @@ -19,9 +19,8 @@ export const Header: React.StatelessComponent<Props> = ({ } = appProps; let backgroundColor; - {/* LP: for dev; pak změnit type na 'expandable-knob-new' */} - if (template.type !== 'expandable-knob') { - backgroundColor = mainColor || '#e51836'; + if (template.type !== 'expandable-knob-new' && template.type !== 'sidebar-new') { + backgroundColor = mainColor || '#fb584e'; } const title = getTitle(appProps, isCollapsed); @@ -33,20 +32,16 @@ export const Header: React.StatelessComponent<Props> = ({ <div className='feedbot-header' onClick={onClick} - style={{ backgroundColor, justifyContent: 'space-between' }}> + style={{ backgroundColor }}> <div className='feedbot-header-name'> - {/* LP: for dev; pak změnit type na 'expandable-knob-new' */} - {template.type === 'expandable-knob' ? ( + {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( <div className='feedbot-avatar' style={{backgroundImage: `url("${avatar}")`}}> </div> ) : null} <div className='feedbot-name'> <span className='feedbot-title'>{title}</span> - {/* LP: for dev; pak změnit type na 'expandable-knob-new' */} - {template.type === 'expandable-knob' ? ( + {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( <span className='feedbot-supportive-title'> - {/* LP: for dev; pak text odstranit */} - S čím vám dnes mohu pomoci? {template.supportiveTitle} </span> ): null} @@ -64,8 +59,7 @@ export const Header: React.StatelessComponent<Props> = ({ onClick={(e) => e.preventDefault()} className='feedbot-minimize' href='#'> - {/* LP: for dev; pak změnit type na 'expandable-knob-new' */} - {template.type === 'expandable-knob' ? ( + {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26" fill="none"> <path fill-rule="evenodd" clip-rule="evenodd" d="M17.5303 8.46967C17.8232 8.76256 17.8232 9.23744 17.5303 9.53033L9.53033 17.5303C9.23744 17.8232 8.76256 17.8232 8.46967 17.5303C8.17678 17.2374 8.17678 16.7626 8.46967 16.4697L16.4697 8.46967C16.7626 8.17678 17.2374 8.17678 17.5303 8.46967Z" fill="currentColor"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L17.5303 16.4697C17.8232 16.7626 17.8232 17.2374 17.5303 17.5303C17.2374 17.8232 16.7626 17.8232 16.4697 17.5303L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967Z" fill="currentColor"/> diff --git a/src/App/templates/ExpandableTemplate/Signature/Signature.tsx b/src/App/templates/ExpandableTemplate/Signature/Signature.tsx index b43f797cc5..c79bf7a509 100644 --- a/src/App/templates/ExpandableTemplate/Signature/Signature.tsx +++ b/src/App/templates/ExpandableTemplate/Signature/Signature.tsx @@ -1,6 +1,8 @@ import * as React from 'react'; import { SignatureLink } from './SignatureLink'; import { Theme } from '../../../../themes'; +import { AppProps } from '../../../App'; + import { SignatureTemplate } from './SignatureTemplate'; type SignatureSchema = Theme['signature']; @@ -9,18 +11,28 @@ const FEEDYOU_LOGO_IMG_SRC = export type Props = { signature: SignatureSchema; + appProps: AppProps; botId: string; }; - const getLinkQueryString = (botId: string) => `?utm_source=webchat&utm_medium=chatbot&utm_campaign=${botId}`; export const Signature: React.StatelessComponent<Props> = ({ signature, botId, + appProps, }) => { - const { partnerLogoUrl, partnerLinkUrl, partnerLogoStyle, mode } = - signature; + const { + partnerLogoUrl, + partnerLinkUrl, + partnerLogoStyle, + mode, + partnerName, + } = signature; + const { + theme: { template }, + } = appProps; + const attachQueryStringToUrl = (url: string) => `${url}${getLinkQueryString(botId)}`; @@ -30,16 +42,19 @@ export const Signature: React.StatelessComponent<Props> = ({ : enhancedFeedyouUrl; const feedyouLink = ( - <SignatureLink href={enhancedFeedyouUrl} text='Feedyou' /> + <SignatureLink + href={enhancedFeedyouUrl} + imgSrc={FEEDYOU_LOGO_IMG_SRC} + /> ); const partnerLink = ( <SignatureLink href={enhancedPartnerUrl} - text={'partnerName'} // todo partner name imgSrc={partnerLogoUrl} className='partner-logo' customStyles={partnerLogoStyle} + text={partnerName} /> ); @@ -47,17 +62,50 @@ export const Signature: React.StatelessComponent<Props> = ({ return null; } - if (mode === 'both') { + /* Webchat Redesign Signature */ + if ( + template.type === 'expandable-knob-new' || + template.type === 'sidebar-new' + ) { + if (mode === 'both') { + return ( + <SignatureTemplate> + {partnerLink} &{' '} + <a + className='signature-link' + target='_blank' + href={enhancedFeedyouUrl}> + Feedyou + </a> + </SignatureTemplate> + ); + } + if (mode === 'partner') { + return <SignatureTemplate>{partnerLink}</SignatureTemplate>; + } + return ( + <SignatureTemplate> + <a + className='signature-link' + target='_blank' + href={enhancedFeedyouUrl}> + Feedyou + </a> + </SignatureTemplate> + ); + } + + if (partnerLogoUrl && mode === 'both') { return ( <SignatureTemplate> {partnerLink} - <div style={{ alignSelf: 'center' }}>& </div> + <div style={{ alignSelf: 'center' }}>&</div> {feedyouLink} </SignatureTemplate> ); } - if (mode === 'partner') { + if (partnerLogoUrl && mode === 'partner') { return <SignatureTemplate>{partnerLink}</SignatureTemplate>; } diff --git a/src/App/templates/ExpandableTemplate/Signature/SignatureLink.tsx b/src/App/templates/ExpandableTemplate/Signature/SignatureLink.tsx index 1edc3d8af1..5b8811d85d 100644 --- a/src/App/templates/ExpandableTemplate/Signature/SignatureLink.tsx +++ b/src/App/templates/ExpandableTemplate/Signature/SignatureLink.tsx @@ -4,7 +4,7 @@ import { CustomStylesForCssClass } from '../../../CustomStylesForCssClass'; export type Props = { href: string; - imgSrc?: string; + imgSrc: string; className?: string; customStyles?: string; text?: string; @@ -22,7 +22,7 @@ export const SignatureLink: React.StatelessComponent<Props> = ({ className={cx('signature-link', className)} target='_blank' href={href}> - {imgSrc ? <img src={imgSrc} /> : text} + {imgSrc ? <img src={imgSrc} alt='Logo' /> : text} {className && customStyles && ( <CustomStylesForCssClass cssClass={className} diff --git a/src/scss/botchat-redesign.scss b/src/scss/botchat-redesign.scss index 04fff57e7b..3a19e07886 100644 --- a/src/scss/botchat-redesign.scss +++ b/src/scss/botchat-redesign.scss @@ -62,6 +62,7 @@ $card_padding: 8px; cursor: pointer; font-size: 1em; display: flex; + justify-content: space-between; align-items: center; font-family: $fontFamily; } @@ -76,9 +77,13 @@ $card_padding: 8px; font-size: 1.2em; line-height: 0.9em; } +.feedbot-header .feedbot-minimize:hover { + color: $black; +} .feedbot-wrapper .wc-chatview-panel { border-bottom-right-radius: 10px; border-bottom-left-radius: 10px; + border-top: 1px solid $silverMedium; } .wc-app .wc-chatview-panel { top: 75px; @@ -154,6 +159,8 @@ $card_padding: 8px; width: 75px; min-width: auto; height: 75px; + bottom: 24px; + right: 24px; } .feedbot-wrapper.collapsed .feedbot-header { border-radius: 40px; @@ -164,7 +171,7 @@ $card_padding: 8px; white-space: nowrap; overflow: hidden; font-size: 0px; - /* background image (chat icon) is set in redesign template */ + /* background image (chat icon) is set in redesign themes (./themes) */ background-size: 45px; background-position: center; background-repeat: no-repeat; @@ -240,14 +247,12 @@ $card_padding: 8px; display: none; } -.feedbot-wrapper .feedbot-signature { +.feedbot-signature { position: absolute; - bottom: -23px; - font-size: 13px; - right: -5px; opacity: 0.3; + color: $black; font-family: $fontFamily; - + /* positioning is set in redesign themes (./themes) */ height: 22px; display: block; align-items: center; @@ -263,8 +268,7 @@ $card_padding: 8px; } .feedbot-signature a { - transition: 0.3s; - color: black; + color: $black; text-decoration: none; margin: 0 4px; display: flex; @@ -273,6 +277,8 @@ $card_padding: 8px; .feedbot-signature a:hover { cursor: pointer; + color: $black; + text-decoration: underline; } .feedbot-signature a img { @@ -281,6 +287,7 @@ $card_padding: 8px; .feedbot-signature-row { display: flex; + align-items: center; height: 100%; } /* @@ -345,7 +352,6 @@ body .wc-app, right: 0; top: 0; transition: transform $actionTransition; - border-top: 1px solid $silverMedium; &.no-header { top: 0; @@ -1097,20 +1103,6 @@ body .wc-app, .feedbot-wrapper.collapsed .feedbot-avatar { display: none; } -.feedbot-wrapper.collapsed .feedbot-header:before { - position: absolute; - content: ''; - top: 0px; - left: 0; - right: 0; - bottom: 0; - border-radius: 100px; - z-index: 0; - filter: brightness(0.7); - /* border-color is set in redesign template */ - border-style: solid; - border-width: 2px; -} .feedbot-name { display: flex; flex-direction: column; diff --git a/src/themes/ExpandableKnobThemeNew.tsx b/src/themes/ExpandableKnobThemeNew.tsx index 05a6eb6c52..14cdce4e62 100644 --- a/src/themes/ExpandableKnobThemeNew.tsx +++ b/src/themes/ExpandableKnobThemeNew.tsx @@ -1,6 +1,7 @@ import { Theme } from './index'; export const ExpandableKnobThemeNew = (theme: Theme) => ` +/* webchat redesign */ .feedbot-wrapper.collapsed .feedbot-header { background-image: url(${ theme.template && theme.template.iconUrl @@ -13,9 +14,14 @@ export const ExpandableKnobThemeNew = (theme: Theme) => ` .feedbot-wrapper.collapsed .feedbot-header { background-color: ${theme.mainColor ? theme.mainColor : '#0063f8'}; } -.feedbot-wrapper.collapsed .feedbot-header:before { - border-color: ${theme.mainColor ? theme.mainColor : '#0063f8'}; + +/* expandable knob specific */ +.feedbot-signature { + bottom: -23px; + font-size: 13px; + right: -5px; } +/* theme custom css */ ${theme.customCss || ''} `; diff --git a/src/themes/SidebarThemeNew.tsx b/src/themes/SidebarThemeNew.tsx new file mode 100644 index 0000000000..4c3ec5bd9a --- /dev/null +++ b/src/themes/SidebarThemeNew.tsx @@ -0,0 +1,34 @@ +import { Theme } from './index'; + +export const SidebarThemeNew = (theme: Theme) => ` +/* webchat redesign */ +.feedbot-wrapper.collapsed .feedbot-header { + background-image: url(${ + theme.template && theme.template.iconUrl + ? theme.template.iconUrl + : '"' + + "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 36 36' fill='none'%3E%3Cpath d='M11.291 18.562L11.2898 18.5622L11.2882 18.5618L11.2869 18.5606L11.2864 18.5589L11.2867 18.5576L11.2879 18.5561L11.2898 18.5556C11.2904 18.5557 11.2916 18.5561 11.2921 18.5565C11.2926 18.557 11.2929 18.5576 11.293 18.5582L11.2928 18.5602L11.291 18.562Z' fill='white'/%3E%3Cpath d='M18.0016 18.5617L18.0025 18.5607L18.0031 18.5589L18.0026 18.5572C18.0022 18.5568 18.001 18.5558 18.0004 18.5556L17.9985 18.5558L17.997 18.557L17.9964 18.5589L17.9967 18.5602L17.9974 18.5612C17.9979 18.5617 17.9985 18.562 17.9991 18.5622L18.0016 18.5617Z' fill='white'/%3E%3Cpath d='M24.711 18.562L24.7097 18.5622C24.7091 18.5621 24.7078 18.5617 24.7074 18.5612C24.7069 18.5608 24.7066 18.5602 24.7065 18.5595L24.7066 18.5576L24.7079 18.5561L24.7097 18.5556C24.71 18.5556 24.7109 18.5558 24.7112 18.5559C24.7115 18.5561 24.7109 18.5557 24.7112 18.5559C24.7117 18.5564 24.7129 18.5576 24.713 18.5582L24.7128 18.5602L24.711 18.562Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M19.4346 0.0453898C23.7144 0.351612 27.7405 2.19149 30.7729 5.22729C33.8088 8.25967 35.6487 12.2851 35.9546 16.5651C36.2605 20.8451 35.0123 25.0922 32.4388 28.5258C29.865 31.9593 26.1395 34.3494 21.9456 35.2567C17.7523 36.1643 13.3715 35.5283 9.60866 33.4655L3.46231 35.8297C2.84593 36.0668 2.16128 36.0551 1.55306 35.7973C0.944834 35.5394 0.460637 35.0551 0.202461 34.4469C-0.0557144 33.8388 -0.0673678 33.1541 0.169763 32.5377L2.53452 26.3913C0.471719 22.6289 -0.164628 18.2483 0.742975 14.0544C1.65029 9.86051 4.04038 6.1346 7.47397 3.56116C10.9074 0.98764 15.1546 -0.260549 19.4346 0.0453898ZM11.2898 17.4372C11.0679 17.4372 10.8511 17.503 10.6666 17.6263C10.4821 17.7495 10.3384 17.9247 10.2535 18.1297C10.1686 18.3346 10.1464 18.5601 10.1897 18.7777C10.2329 18.9953 10.3398 19.1952 10.4966 19.352C10.6535 19.5089 10.8534 19.6157 11.0709 19.659C11.2885 19.7023 11.514 19.6801 11.719 19.5952C11.924 19.5103 12.0991 19.3665 12.2224 19.1821C12.3456 18.9976 12.4114 18.7807 12.4114 18.5589C12.4114 18.2614 12.2932 17.9761 12.0829 17.7658C11.8725 17.5554 11.5872 17.4372 11.2898 17.4372ZM17.3766 17.6263C17.561 17.503 17.7779 17.4372 17.9997 17.4372C18.2972 17.4372 18.5825 17.5554 18.7929 17.7658C19.0032 17.9761 19.1214 18.2614 19.1214 18.5589C19.1214 18.7807 19.0556 18.9976 18.9324 19.1821C18.8091 19.3665 18.6339 19.5103 18.429 19.5952C18.224 19.6801 17.9985 19.7023 17.7809 19.659C17.5633 19.6157 17.3635 19.5089 17.2066 19.352C17.0497 19.1952 16.9429 18.9953 16.8996 18.7777C16.8564 18.5601 16.8786 18.3346 16.9635 18.1297C17.0484 17.9247 17.1921 17.7495 17.3766 17.6263ZM24.7097 17.4372C24.4879 17.4372 24.271 17.503 24.0866 17.6263C23.9021 17.7495 23.7583 17.9247 23.6734 18.1297C23.5885 18.3346 23.5663 18.5601 23.6096 18.7777C23.6529 18.9953 23.7597 19.1952 23.9166 19.352C24.0735 19.5089 24.2733 19.6157 24.4909 19.659C24.7085 19.7023 24.934 19.6801 25.139 19.5952C25.3439 19.5103 25.5191 19.3665 25.6423 19.1821C25.7656 18.9976 25.8314 18.7807 25.8314 18.5589C25.8314 18.2614 25.7132 17.9761 25.5028 17.7658C25.2925 17.5554 25.0072 17.4372 24.7097 17.4372Z' fill='white'/%3E%3C/svg%3E" + + '"' + }); +} +.feedbot-wrapper.collapsed .feedbot-header { + background-color: ${theme.mainColor ? theme.mainColor : '#0063f8'}; +} + +/* sidebar specific */ +.feedbot-wrapper { + height: 100vh; + bottom: 0; + right: 0; + max-height: 100%; + border-radius: 0; +} +.feedbot-signature { + bottom: -2px; + font-size: 11px; + right: 13px; +} + +/* theme custom css */ +${theme.customCss || ''} +`; diff --git a/src/themes/index.ts b/src/themes/index.ts index 3924c3a7f2..abb877499c 100644 --- a/src/themes/index.ts +++ b/src/themes/index.ts @@ -3,6 +3,7 @@ import { FullScreenTheme } from './FullScreenTheme'; import { ExpandableKnobTheme } from './ExpandableKnobTheme'; import { SidebarTheme } from './SidebarTheme'; import { ExpandableKnobThemeNew } from './ExpandableKnobThemeNew'; +import { SidebarThemeNew } from './SidebarThemeNew'; export type Theme = { mainColor: string; @@ -31,6 +32,7 @@ export type Theme = { partnerLogoUrl: string; partnerLogoStyle: string; partnerLinkUrl: string; + partnerName: string; mode: string; }; }; @@ -42,13 +44,13 @@ export function getStyleForTheme(theme: Theme, remoteConfig: boolean): string { case 'full-screen': return FullScreenTheme(theme); case 'expandable-knob': - return ExpandableKnobThemeNew( - theme - ); /* LP: pro dev; vrátit zpět ExpandableKnobTheme(theme);*/ + return ExpandableKnobTheme(theme); case 'sidebar': return SidebarTheme(theme); case 'expandable-knob-new': return ExpandableKnobThemeNew(theme); + case 'sidebar-new': + return SidebarThemeNew(theme); } // backward compatibility - knob is new default for remote config, old default is bar From e11d93f38f0777439e2fdefaeff1ccce9b3580cb Mon Sep 17 00:00:00 2001 From: Lukas Podmelle <podmellelukas@gmail.com> Date: Sat, 13 Jan 2024 00:46:40 +0100 Subject: [PATCH 3/9] Redesign & Persistent menu --- .prettierignore | 1 + .../templates/ExpandableTemplate/Header.tsx | 145 ++++++++++++------ src/scss/botchat-redesign.scss | 51 +++++- src/themes/index.ts | 4 + 4 files changed, 153 insertions(+), 48 deletions(-) diff --git a/.prettierignore b/.prettierignore index e69de29bb2..f59ec20aab 100644 --- a/.prettierignore +++ b/.prettierignore @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/src/App/templates/ExpandableTemplate/Header.tsx b/src/App/templates/ExpandableTemplate/Header.tsx index 9a8e80272c..00f54d27d6 100644 --- a/src/App/templates/ExpandableTemplate/Header.tsx +++ b/src/App/templates/ExpandableTemplate/Header.tsx @@ -8,45 +8,59 @@ export type Props = { isCollapsed: boolean; }; -export const Header: React.StatelessComponent<Props> = ({ - appProps, - onClick, - isCollapsed, -}) => { - const { - theme: { mainColor, template }, - header: { extraHtml }, - } = appProps; +export type State = { + isMenuOpen: boolean; +} - let backgroundColor; - if (template.type !== 'expandable-knob-new' && template.type !== 'sidebar-new') { - backgroundColor = mainColor || '#fb584e'; - } +export class Header extends React.Component<Props, State> { + state: State = {isMenuOpen: false} - const title = getTitle(appProps, isCollapsed); - const avatar = - template.avatar || - "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 36 36' fill='none'%3E%3Cpath d='M36 18c0 9.941-8.059 18-18 18S0 27.941 0 18 8.059 0 18 0s18 8.059 18 18Z' fill='%23F7F9FB'/%3E%3Cpath d='M14.024 18.026h-.003v-.005h.004l.001.002v.001l-.002.002Zm3.978 0v-.001l.001-.002v-.002h-.006v.004l.002.001h.003Zm3.976 0h-.003l-.001-.002v-.002l.001-.001h.004v.003l-.001.002Z' fill='%23385B75'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M18.718 9.023a8.912 8.912 0 0 1 5.669 2.59 8.91 8.91 0 0 1-10.583 14.12l-3.073 1.182a1.274 1.274 0 0 1-1.646-1.646l1.182-3.073a8.91 8.91 0 0 1 8.45-13.173ZM14.023 17a1.024 1.024 0 1 0 0 2.047 1.024 1.024 0 0 0 0-2.047Zm3.409.172a1.023 1.023 0 1 1 1.136 1.702 1.023 1.023 0 0 1-1.136-1.702ZM21.977 17a1.023 1.023 0 1 0 0 2.047 1.023 1.023 0 0 0 0-2.047Z' fill='%23385B75'/%3E%3C/svg%3E"; + render() { + const { + appProps: { + theme: { mainColor, template }, + header: { extraHtml }, + }, + isCollapsed, + onClick, + } = this.props; - return ( - <div - className='feedbot-header' - onClick={onClick} - style={{ backgroundColor }}> - <div className='feedbot-header-name'> - {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( - <div className='feedbot-avatar' style={{backgroundImage: `url("${avatar}")`}}> + const handlePersistentMenuToggle = () => { + this.setState((prevState) => ({ + isMenuOpen: !prevState.isMenuOpen + })) + } + + let backgroundColor; + if (template.type !== 'expandable-knob-new' && template.type !== 'sidebar-new') { + backgroundColor = mainColor || '#fb584e'; + } + + const title = getTitle(this.props.appProps, isCollapsed); + + const avatar = + template.avatar || + "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 36 36' fill='none'%3E%3Cpath d='M36 18c0 9.941-8.059 18-18 18S0 27.941 0 18 8.059 0 18 0s18 8.059 18 18Z' fill='%23F7F9FB'/%3E%3Cpath d='M14.024 18.026h-.003v-.005h.004l.001.002v.001l-.002.002Zm3.978 0v-.001l.001-.002v-.002h-.006v.004l.002.001h.003Zm3.976 0h-.003l-.001-.002v-.002l.001-.001h.004v.003l-.001.002Z' fill='%23385B75'/%3E%3Cpath fillRule='evenodd' clipRule='evenodd' d='M18.718 9.023a8.912 8.912 0 0 1 5.669 2.59 8.91 8.91 0 0 1-10.583 14.12l-3.073 1.182a1.274 1.274 0 0 1-1.646-1.646l1.182-3.073a8.91 8.91 0 0 1 8.45-13.173ZM14.023 17a1.024 1.024 0 1 0 0 2.047 1.024 1.024 0 0 0 0-2.047Zm3.409.172a1.023 1.023 0 1 1 1.136 1.702 1.023 1.023 0 0 1-1.136-1.702ZM21.977 17a1.023 1.023 0 1 0 0 2.047 1.023 1.023 0 0 0 0-2.047Z' fill='%23385B75'/%3E%3C/svg%3E"; + + return ( + <div + className='feedbot-header' + onClick={isCollapsed && onClick} + style={{ backgroundColor }}> + <div className='feedbot-header-name'> + {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( + <div className='feedbot-avatar' style={{backgroundImage: `url("${avatar}")`}}> + </div> + ) : null} + <div className='feedbot-name'> + <span className='feedbot-title'>{title}</span> + {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( + <span className='feedbot-supportive-title'> + {template.supportiveTitle} + </span> + ): null} </div> - ) : null} - <div className='feedbot-name'> - <span className='feedbot-title'>{title}</span> - {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( - <span className='feedbot-supportive-title'> - {template.supportiveTitle} - </span> - ): null} </div> - </div> {extraHtml && ( <span @@ -55,23 +69,52 @@ export const Header: React.StatelessComponent<Props> = ({ /> )} - <a - onClick={(e) => e.preventDefault()} - className='feedbot-minimize' - href='#'> + <div className='feedbot-header-actions'> {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( - <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26" fill="none"> - <path fill-rule="evenodd" clip-rule="evenodd" d="M17.5303 8.46967C17.8232 8.76256 17.8232 9.23744 17.5303 9.53033L9.53033 17.5303C9.23744 17.8232 8.76256 17.8232 8.46967 17.5303C8.17678 17.2374 8.17678 16.7626 8.46967 16.4697L16.4697 8.46967C16.7626 8.17678 17.2374 8.17678 17.5303 8.46967Z" fill="currentColor"/> - <path fill-rule="evenodd" clip-rule="evenodd" d="M8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L17.5303 16.4697C17.8232 16.7626 17.8232 17.2374 17.5303 17.5303C17.2374 17.8232 16.7626 17.8232 16.4697 17.5303L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967Z" fill="currentColor"/> - <path fill-rule="evenodd" clip-rule="evenodd" d="M13 1.75C6.7868 1.75 1.75 6.7868 1.75 13C1.75 19.2132 6.7868 24.25 13 24.25C19.2132 24.25 24.25 19.2132 24.25 13C24.25 6.7868 19.2132 1.75 13 1.75ZM0.25 13C0.25 5.95837 5.95837 0.25 13 0.25C20.0416 0.25 25.75 5.95837 25.75 13C25.75 20.0416 20.0416 25.75 13 25.75C5.95837 25.75 0.25 20.0416 0.25 13Z" fill="currentColor"/> - </svg> + <div className='feedbot-persistent-menu' onClick={handlePersistentMenuToggle}> + <a className='feedbot-persistent-menu-toggle'> + <svg xmlns="http://www.w3.org/2000/svg" width="3" height="17" viewBox="0 0 3 17" fill="none"> + <path d="M3 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm0 7a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm0 7a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z" fill="currentColor"/> + </svg> + </a> + {this.state.isMenuOpen && + <ul className='feedbot-persistent-menu-links'> + {template.persistentMenu.map((menuItem, index) => + <li key={index}> + <a onClick={() => handleTriggerDialog(menuItem.dialog)}> + {menuItem.title} + </a> + </li> + )} + </ul> + } + </div> ) : ( - '_' + null )} - </a> + <a + onClick={(e) => { + e.preventDefault() + onClick() + }} + className='feedbot-minimize' + href='#'> + {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( + <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26" fill="none"> + <path fillRule="evenodd" clipRule="evenodd" d="M17.5303 8.46967C17.8232 8.76256 17.8232 9.23744 17.5303 9.53033L9.53033 17.5303C9.23744 17.8232 8.76256 17.8232 8.46967 17.5303C8.17678 17.2374 8.17678 16.7626 8.46967 16.4697L16.4697 8.46967C16.7626 8.17678 17.2374 8.17678 17.5303 8.46967Z" fill="currentColor"/> + <path fillRule="evenodd" clipRule="evenodd" d="M8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L17.5303 16.4697C17.8232 16.7626 17.8232 17.2374 17.5303 17.5303C17.2374 17.8232 16.7626 17.8232 16.4697 17.5303L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967Z" fill="currentColor"/> + <path fillRule="evenodd" clipRule="evenodd" d="M13 1.75C6.7868 1.75 1.75 6.7868 1.75 13C1.75 19.2132 6.7868 24.25 13 24.25C19.2132 24.25 24.25 19.2132 24.25 13C24.25 6.7868 19.2132 1.75 13 1.75ZM0.25 13C0.25 5.95837 5.95837 0.25 13 0.25C20.0416 0.25 25.75 5.95837 25.75 13C25.75 20.0416 20.0416 25.75 13 25.75C5.95837 25.75 0.25 20.0416 0.25 13Z" fill="currentColor"/> + </svg> + ) : ( + '_' + )} + </a> + </div> </div> - ); -}; + ) + } + +} const getTitle = (props: AppProps, isCollapsed: boolean) => { const { text, textWhenCollapsed } = props.header; @@ -83,4 +126,12 @@ const getTitle = (props: AppProps, isCollapsed: boolean) => { return titleToShow; }; +/** + * Triggers a specified dialog for the persistent menu item from Channel settings + * @param dialogId Dialog ID to be triggered on persistent menu item click + */ +const handleTriggerDialog = (dialogId: string) => { + window.dispatchEvent(new CustomEvent('feedbot:trigger-dialog', { detail: dialogId })) +} + export type HeaderProps = Props; diff --git a/src/scss/botchat-redesign.scss b/src/scss/botchat-redesign.scss index 3a19e07886..ff8b68e319 100644 --- a/src/scss/botchat-redesign.scss +++ b/src/scss/botchat-redesign.scss @@ -59,7 +59,6 @@ $card_padding: 8px; color: $greyDark; z-index: 10; padding: 18px; - cursor: pointer; font-size: 1em; display: flex; justify-content: space-between; @@ -167,6 +166,7 @@ $card_padding: 8px; height: 100%; width: 100%; padding: 0px; + cursor: pointer; text-indent: 999%; white-space: nowrap; overflow: hidden; @@ -1098,6 +1098,7 @@ body .wc-app, border-radius: 100px; background-position: center; background-size: cover; + aspect-ratio: 1/1; } } .feedbot-wrapper.collapsed .feedbot-avatar { @@ -1112,3 +1113,51 @@ body .wc-app, font-size: 13px; color: $grey; } +.feedbot-header-actions { + display: flex; + align-items: center; + gap: 8px; +} +.feedbot-persistent-menu-toggle { + color: $greyDark; + padding: 5px 5px; + cursor: pointer; +} +.feedbot-persistent-menu-toggle:hover { + color: $black; +} +.feedbot-persistent-menu { + line-height: 0.9em; + position: relative; +} +.feedbot-persistent-menu-links { + position: absolute; + top: 35px; + right: -35px; + background-color: $white; + border: 1px solid $silver; + border-radius: 6px; + box-shadow: 0px 1px 2px 0px $shadow; + z-index: 1; + list-style: none; + padding: 10px 0; + margin: 0; + min-width: 10em; + max-width: 17em; +} +.feedbot-persistent-menu-links a { + padding: 10px 20px; + display: block; + box-sizing: border-box; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; + transition: background-color .2s ease, border .2 ease; +} +.feedbot-persistent-menu-links a:hover { + background-color: $blueLight; + outline: 2px solid $blueDark; + border-radius: 4px; + transition: background-color .2s ease, border .2 ease; +} \ No newline at end of file diff --git a/src/themes/index.ts b/src/themes/index.ts index abb877499c..d8eef09d6c 100644 --- a/src/themes/index.ts +++ b/src/themes/index.ts @@ -24,6 +24,10 @@ export type Theme = { logoUrl?: string; avatar?: string; supportiveTitle?: string; + persistentMenu?: [{ + title: string; + dialog: string; + }] }; customCss?: string; showSignature?: boolean; From 9370da8aaaa8866bad6c23dbb352c1036c05de9b Mon Sep 17 00:00:00 2001 From: Lukas Podmelle <podmellelukas@gmail.com> Date: Tue, 16 Jan 2024 12:29:02 +0100 Subject: [PATCH 4/9] Redesign & Persistent menu --- .../templates/ExpandableTemplate/Header.tsx | 34 +++++++++++++++++-- src/scss/botchat-redesign.scss | 20 +++++++++-- src/themes/ExpandableKnobThemeNew.tsx | 8 ++--- src/themes/SidebarThemeNew.tsx | 6 ++-- src/themes/index.ts | 1 + 5 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/App/templates/ExpandableTemplate/Header.tsx b/src/App/templates/ExpandableTemplate/Header.tsx index 00f54d27d6..06c64c171b 100644 --- a/src/App/templates/ExpandableTemplate/Header.tsx +++ b/src/App/templates/ExpandableTemplate/Header.tsx @@ -42,6 +42,12 @@ export class Header extends React.Component<Props, State> { template.avatar || "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 36 36' fill='none'%3E%3Cpath d='M36 18c0 9.941-8.059 18-18 18S0 27.941 0 18 8.059 0 18 0s18 8.059 18 18Z' fill='%23F7F9FB'/%3E%3Cpath d='M14.024 18.026h-.003v-.005h.004l.001.002v.001l-.002.002Zm3.978 0v-.001l.001-.002v-.002h-.006v.004l.002.001h.003Zm3.976 0h-.003l-.001-.002v-.002l.001-.001h.004v.003l-.001.002Z' fill='%23385B75'/%3E%3Cpath fillRule='evenodd' clipRule='evenodd' d='M18.718 9.023a8.912 8.912 0 0 1 5.669 2.59 8.91 8.91 0 0 1-10.583 14.12l-3.073 1.182a1.274 1.274 0 0 1-1.646-1.646l1.182-3.073a8.91 8.91 0 0 1 8.45-13.173ZM14.023 17a1.024 1.024 0 1 0 0 2.047 1.024 1.024 0 0 0 0-2.047Zm3.409.172a1.023 1.023 0 1 1 1.136 1.702 1.023 1.023 0 0 1-1.136-1.702ZM21.977 17a1.023 1.023 0 1 0 0 2.047 1.023 1.023 0 0 0 0-2.047Z' fill='%23385B75'/%3E%3C/svg%3E"; + const startOverIcon = + <svg xmlns="http://www.w3.org/2000/svg" width="17" height="15" viewBox="0 0 17 15" fill="none"> + <path d="M4.429 1 1 4.429l3.429 3.428" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /> + <path d="M1 4.429h10.286a4.571 4.571 0 0 1 0 9.142H5.57" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /> + </svg> + return ( <div className='feedbot-header' @@ -49,7 +55,9 @@ export class Header extends React.Component<Props, State> { style={{ backgroundColor }}> <div className='feedbot-header-name'> {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( - <div className='feedbot-avatar' style={{backgroundImage: `url("${avatar}")`}}> + <div + className='feedbot-avatar' + style={{backgroundImage: `url("${avatar}")`}}> </div> ) : null} <div className='feedbot-name'> @@ -70,7 +78,7 @@ export class Header extends React.Component<Props, State> { )} <div className='feedbot-header-actions'> - {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( + {(template.type === 'expandable-knob-new' || template.type === 'sidebar-new') && (checkFeedbotTestMode() || template.persistentMenu.length > 0) ? ( <div className='feedbot-persistent-menu' onClick={handlePersistentMenuToggle}> <a className='feedbot-persistent-menu-toggle'> <svg xmlns="http://www.w3.org/2000/svg" width="3" height="17" viewBox="0 0 3 17" fill="none"> @@ -79,6 +87,13 @@ export class Header extends React.Component<Props, State> { </a> {this.state.isMenuOpen && <ul className='feedbot-persistent-menu-links'> + + { checkFeedbotTestMode() && + <li className='feedbot-persistent-menu-debug'> + <a onClick={handleStartOver}><span>Start over</span>{startOverIcon}</a> + </li> + } + {template.persistentMenu.map((menuItem, index) => <li key={index}> <a onClick={() => handleTriggerDialog(menuItem.dialog)}> @@ -134,4 +149,19 @@ const handleTriggerDialog = (dialogId: string) => { window.dispatchEvent(new CustomEvent('feedbot:trigger-dialog', { detail: dialogId })) } +/** + * Restart conversation via custom event + */ +const handleStartOver = () => { + window.dispatchEvent(new CustomEvent('feedbot:start-over')) +} + +/** + * Check URL if test mode is being used (#feedbot-test-mode) + * @returns Boolean + */ +const checkFeedbotTestMode = (): boolean => { + return window.location.hash === '#feedbot-test-mode' +} + export type HeaderProps = Props; diff --git a/src/scss/botchat-redesign.scss b/src/scss/botchat-redesign.scss index ff8b68e319..9fa2c8becc 100644 --- a/src/scss/botchat-redesign.scss +++ b/src/scss/botchat-redesign.scss @@ -1117,6 +1117,7 @@ body .wc-app, display: flex; align-items: center; gap: 8px; + user-select: none; } .feedbot-persistent-menu-toggle { color: $greyDark; @@ -1142,8 +1143,9 @@ body .wc-app, list-style: none; padding: 10px 0; margin: 0; - min-width: 10em; + min-width: 11em; max-width: 17em; + font-size: 15px; } .feedbot-persistent-menu-links a { padding: 10px 20px; @@ -1160,4 +1162,18 @@ body .wc-app, outline: 2px solid $blueDark; border-radius: 4px; transition: background-color .2s ease, border .2 ease; -} \ No newline at end of file +} +.feedbot-persistent-menu-debug a { + display: flex !important; + justify-content: space-between; + gap: 14px; +} +.feedbot-persistent-menu-debug svg { + color: $blue; +} +// .feedbot-persistent-menu-debug:last-child::after { +// content: ''; +// display: block; +// border-bottom: 1px solid $silver; +// margin: 10px 0; +// } diff --git a/src/themes/ExpandableKnobThemeNew.tsx b/src/themes/ExpandableKnobThemeNew.tsx index 14cdce4e62..5af8e64f6b 100644 --- a/src/themes/ExpandableKnobThemeNew.tsx +++ b/src/themes/ExpandableKnobThemeNew.tsx @@ -7,12 +7,12 @@ export const ExpandableKnobThemeNew = (theme: Theme) => ` theme.template && theme.template.iconUrl ? theme.template.iconUrl : '"' + - "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 36 36' fill='none'%3E%3Cpath d='M11.291 18.562L11.2898 18.5622L11.2882 18.5618L11.2869 18.5606L11.2864 18.5589L11.2867 18.5576L11.2879 18.5561L11.2898 18.5556C11.2904 18.5557 11.2916 18.5561 11.2921 18.5565C11.2926 18.557 11.2929 18.5576 11.293 18.5582L11.2928 18.5602L11.291 18.562Z' fill='white'/%3E%3Cpath d='M18.0016 18.5617L18.0025 18.5607L18.0031 18.5589L18.0026 18.5572C18.0022 18.5568 18.001 18.5558 18.0004 18.5556L17.9985 18.5558L17.997 18.557L17.9964 18.5589L17.9967 18.5602L17.9974 18.5612C17.9979 18.5617 17.9985 18.562 17.9991 18.5622L18.0016 18.5617Z' fill='white'/%3E%3Cpath d='M24.711 18.562L24.7097 18.5622C24.7091 18.5621 24.7078 18.5617 24.7074 18.5612C24.7069 18.5608 24.7066 18.5602 24.7065 18.5595L24.7066 18.5576L24.7079 18.5561L24.7097 18.5556C24.71 18.5556 24.7109 18.5558 24.7112 18.5559C24.7115 18.5561 24.7109 18.5557 24.7112 18.5559C24.7117 18.5564 24.7129 18.5576 24.713 18.5582L24.7128 18.5602L24.711 18.562Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M19.4346 0.0453898C23.7144 0.351612 27.7405 2.19149 30.7729 5.22729C33.8088 8.25967 35.6487 12.2851 35.9546 16.5651C36.2605 20.8451 35.0123 25.0922 32.4388 28.5258C29.865 31.9593 26.1395 34.3494 21.9456 35.2567C17.7523 36.1643 13.3715 35.5283 9.60866 33.4655L3.46231 35.8297C2.84593 36.0668 2.16128 36.0551 1.55306 35.7973C0.944834 35.5394 0.460637 35.0551 0.202461 34.4469C-0.0557144 33.8388 -0.0673678 33.1541 0.169763 32.5377L2.53452 26.3913C0.471719 22.6289 -0.164628 18.2483 0.742975 14.0544C1.65029 9.86051 4.04038 6.1346 7.47397 3.56116C10.9074 0.98764 15.1546 -0.260549 19.4346 0.0453898ZM11.2898 17.4372C11.0679 17.4372 10.8511 17.503 10.6666 17.6263C10.4821 17.7495 10.3384 17.9247 10.2535 18.1297C10.1686 18.3346 10.1464 18.5601 10.1897 18.7777C10.2329 18.9953 10.3398 19.1952 10.4966 19.352C10.6535 19.5089 10.8534 19.6157 11.0709 19.659C11.2885 19.7023 11.514 19.6801 11.719 19.5952C11.924 19.5103 12.0991 19.3665 12.2224 19.1821C12.3456 18.9976 12.4114 18.7807 12.4114 18.5589C12.4114 18.2614 12.2932 17.9761 12.0829 17.7658C11.8725 17.5554 11.5872 17.4372 11.2898 17.4372ZM17.3766 17.6263C17.561 17.503 17.7779 17.4372 17.9997 17.4372C18.2972 17.4372 18.5825 17.5554 18.7929 17.7658C19.0032 17.9761 19.1214 18.2614 19.1214 18.5589C19.1214 18.7807 19.0556 18.9976 18.9324 19.1821C18.8091 19.3665 18.6339 19.5103 18.429 19.5952C18.224 19.6801 17.9985 19.7023 17.7809 19.659C17.5633 19.6157 17.3635 19.5089 17.2066 19.352C17.0497 19.1952 16.9429 18.9953 16.8996 18.7777C16.8564 18.5601 16.8786 18.3346 16.9635 18.1297C17.0484 17.9247 17.1921 17.7495 17.3766 17.6263ZM24.7097 17.4372C24.4879 17.4372 24.271 17.503 24.0866 17.6263C23.9021 17.7495 23.7583 17.9247 23.6734 18.1297C23.5885 18.3346 23.5663 18.5601 23.6096 18.7777C23.6529 18.9953 23.7597 19.1952 23.9166 19.352C24.0735 19.5089 24.2733 19.6157 24.4909 19.659C24.7085 19.7023 24.934 19.6801 25.139 19.5952C25.3439 19.5103 25.5191 19.3665 25.6423 19.1821C25.7656 18.9976 25.8314 18.7807 25.8314 18.5589C25.8314 18.2614 25.7132 17.9761 25.5028 17.7658C25.2925 17.5554 25.0072 17.4372 24.7097 17.4372Z' fill='white'/%3E%3C/svg%3E" + - '"' + "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 36 36' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M19.435.045a17.824 17.824 0 0 1 11.338 5.182 17.82 17.82 0 0 1 1.666 23.299 17.825 17.825 0 0 1-22.83 4.94L3.462 35.83A2.548 2.548 0 0 1 .17 32.538l2.365-6.147A17.821 17.821 0 0 1 19.435.045ZM11.29 17.437a1.121 1.121 0 1 0 0 2.243 1.121 1.121 0 0 0 0-2.243Zm6.087.19a1.121 1.121 0 1 1 1.246 1.863 1.121 1.121 0 0 1-1.246-1.864Zm7.333-.19a1.121 1.121 0 1 0 0 2.242 1.121 1.121 0 0 0 0-2.242Z' fill='white'/%3E%3C/svg%3E" + + '"' }); } .feedbot-wrapper.collapsed .feedbot-header { - background-color: ${theme.mainColor ? theme.mainColor : '#0063f8'}; + background-color: ${theme.mainColor ? theme.mainColor : '#0063F8'}; } /* expandable knob specific */ @@ -22,6 +22,6 @@ export const ExpandableKnobThemeNew = (theme: Theme) => ` right: -5px; } -/* theme custom css */ +/* user custom css */ ${theme.customCss || ''} `; diff --git a/src/themes/SidebarThemeNew.tsx b/src/themes/SidebarThemeNew.tsx index 4c3ec5bd9a..09ba9afcd7 100644 --- a/src/themes/SidebarThemeNew.tsx +++ b/src/themes/SidebarThemeNew.tsx @@ -7,8 +7,8 @@ export const SidebarThemeNew = (theme: Theme) => ` theme.template && theme.template.iconUrl ? theme.template.iconUrl : '"' + - "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 36 36' fill='none'%3E%3Cpath d='M11.291 18.562L11.2898 18.5622L11.2882 18.5618L11.2869 18.5606L11.2864 18.5589L11.2867 18.5576L11.2879 18.5561L11.2898 18.5556C11.2904 18.5557 11.2916 18.5561 11.2921 18.5565C11.2926 18.557 11.2929 18.5576 11.293 18.5582L11.2928 18.5602L11.291 18.562Z' fill='white'/%3E%3Cpath d='M18.0016 18.5617L18.0025 18.5607L18.0031 18.5589L18.0026 18.5572C18.0022 18.5568 18.001 18.5558 18.0004 18.5556L17.9985 18.5558L17.997 18.557L17.9964 18.5589L17.9967 18.5602L17.9974 18.5612C17.9979 18.5617 17.9985 18.562 17.9991 18.5622L18.0016 18.5617Z' fill='white'/%3E%3Cpath d='M24.711 18.562L24.7097 18.5622C24.7091 18.5621 24.7078 18.5617 24.7074 18.5612C24.7069 18.5608 24.7066 18.5602 24.7065 18.5595L24.7066 18.5576L24.7079 18.5561L24.7097 18.5556C24.71 18.5556 24.7109 18.5558 24.7112 18.5559C24.7115 18.5561 24.7109 18.5557 24.7112 18.5559C24.7117 18.5564 24.7129 18.5576 24.713 18.5582L24.7128 18.5602L24.711 18.562Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M19.4346 0.0453898C23.7144 0.351612 27.7405 2.19149 30.7729 5.22729C33.8088 8.25967 35.6487 12.2851 35.9546 16.5651C36.2605 20.8451 35.0123 25.0922 32.4388 28.5258C29.865 31.9593 26.1395 34.3494 21.9456 35.2567C17.7523 36.1643 13.3715 35.5283 9.60866 33.4655L3.46231 35.8297C2.84593 36.0668 2.16128 36.0551 1.55306 35.7973C0.944834 35.5394 0.460637 35.0551 0.202461 34.4469C-0.0557144 33.8388 -0.0673678 33.1541 0.169763 32.5377L2.53452 26.3913C0.471719 22.6289 -0.164628 18.2483 0.742975 14.0544C1.65029 9.86051 4.04038 6.1346 7.47397 3.56116C10.9074 0.98764 15.1546 -0.260549 19.4346 0.0453898ZM11.2898 17.4372C11.0679 17.4372 10.8511 17.503 10.6666 17.6263C10.4821 17.7495 10.3384 17.9247 10.2535 18.1297C10.1686 18.3346 10.1464 18.5601 10.1897 18.7777C10.2329 18.9953 10.3398 19.1952 10.4966 19.352C10.6535 19.5089 10.8534 19.6157 11.0709 19.659C11.2885 19.7023 11.514 19.6801 11.719 19.5952C11.924 19.5103 12.0991 19.3665 12.2224 19.1821C12.3456 18.9976 12.4114 18.7807 12.4114 18.5589C12.4114 18.2614 12.2932 17.9761 12.0829 17.7658C11.8725 17.5554 11.5872 17.4372 11.2898 17.4372ZM17.3766 17.6263C17.561 17.503 17.7779 17.4372 17.9997 17.4372C18.2972 17.4372 18.5825 17.5554 18.7929 17.7658C19.0032 17.9761 19.1214 18.2614 19.1214 18.5589C19.1214 18.7807 19.0556 18.9976 18.9324 19.1821C18.8091 19.3665 18.6339 19.5103 18.429 19.5952C18.224 19.6801 17.9985 19.7023 17.7809 19.659C17.5633 19.6157 17.3635 19.5089 17.2066 19.352C17.0497 19.1952 16.9429 18.9953 16.8996 18.7777C16.8564 18.5601 16.8786 18.3346 16.9635 18.1297C17.0484 17.9247 17.1921 17.7495 17.3766 17.6263ZM24.7097 17.4372C24.4879 17.4372 24.271 17.503 24.0866 17.6263C23.9021 17.7495 23.7583 17.9247 23.6734 18.1297C23.5885 18.3346 23.5663 18.5601 23.6096 18.7777C23.6529 18.9953 23.7597 19.1952 23.9166 19.352C24.0735 19.5089 24.2733 19.6157 24.4909 19.659C24.7085 19.7023 24.934 19.6801 25.139 19.5952C25.3439 19.5103 25.5191 19.3665 25.6423 19.1821C25.7656 18.9976 25.8314 18.7807 25.8314 18.5589C25.8314 18.2614 25.7132 17.9761 25.5028 17.7658C25.2925 17.5554 25.0072 17.4372 24.7097 17.4372Z' fill='white'/%3E%3C/svg%3E" + - '"' + "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 36 36' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M19.435.045a17.824 17.824 0 0 1 11.338 5.182 17.82 17.82 0 0 1 1.666 23.299 17.825 17.825 0 0 1-22.83 4.94L3.462 35.83A2.548 2.548 0 0 1 .17 32.538l2.365-6.147A17.821 17.821 0 0 1 19.435.045ZM11.29 17.437a1.121 1.121 0 1 0 0 2.243 1.121 1.121 0 0 0 0-2.243Zm6.087.19a1.121 1.121 0 1 1 1.246 1.863 1.121 1.121 0 0 1-1.246-1.864Zm7.333-.19a1.121 1.121 0 1 0 0 2.242 1.121 1.121 0 0 0 0-2.242Z' fill='white'/%3E%3C/svg%3E" + + '"' }); } .feedbot-wrapper.collapsed .feedbot-header { @@ -29,6 +29,6 @@ export const SidebarThemeNew = (theme: Theme) => ` right: 13px; } -/* theme custom css */ +/* user custom css */ ${theme.customCss || ''} `; diff --git a/src/themes/index.ts b/src/themes/index.ts index d8eef09d6c..81564df8b6 100644 --- a/src/themes/index.ts +++ b/src/themes/index.ts @@ -7,6 +7,7 @@ import { SidebarThemeNew } from './SidebarThemeNew'; export type Theme = { mainColor: string; + secondaryColor: string; template?: { // Dost možná tu nějaký propy chyběj, // tak je neváhej připsat! :) From 794e8c6ffba0a1199e6ae581d842305499bfa7e8 Mon Sep 17 00:00:00 2001 From: Lukas Podmelle <podmellelukas@gmail.com> Date: Tue, 16 Jan 2024 13:41:37 +0100 Subject: [PATCH 5/9] Redesign --- src/scss/botchat-redesign.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/scss/botchat-redesign.scss b/src/scss/botchat-redesign.scss index 9fa2c8becc..66858ae0a5 100644 --- a/src/scss/botchat-redesign.scss +++ b/src/scss/botchat-redesign.scss @@ -1147,6 +1147,9 @@ body .wc-app, max-width: 17em; font-size: 15px; } +.feedbot-wrapper.collapsed .feedbot-persistent-menu-links { + display: none; +} .feedbot-persistent-menu-links a { padding: 10px 20px; display: block; From 27050d2e0d4e1c323a8380b4d657effebbfeaafa Mon Sep 17 00:00:00 2001 From: Lukas Podmelle <podmellelukas@gmail.com> Date: Tue, 16 Jan 2024 15:07:11 +0100 Subject: [PATCH 6/9] Fixes --- .prettierrc | 9 + samples/.DS_Store | Bin 0 -> 6148 bytes samples/feedyou/newdesign.html | 2 +- src/App/App.tsx | 2 +- .../ExpandableTemplate/ExpandableTemplate.tsx | 2 +- .../templates/ExpandableTemplate/Header.tsx | 221 ++++---- .../Signature/SignatureTemplate.tsx | 14 +- src/Shell.tsx | 480 ++++++++---------- src/scss/botchat-redesign.scss | 4 + src/themes/ExpandableBarTheme.tsx | 6 +- src/themes/ExpandableKnobTheme.tsx | 15 +- src/themes/SidebarTheme.tsx | 12 +- src/themes/index.ts | 65 ++- 13 files changed, 413 insertions(+), 419 deletions(-) create mode 100644 .prettierrc create mode 100644 samples/.DS_Store diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..6f7c3e2c99 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "useTabs": true, + "singleQuote": true, + "semi": true, + "printWidth": 100, + "arrowParens": "avoid", + "trailingComma": "none", + "endOfLine": "auto" +} diff --git a/samples/.DS_Store b/samples/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..96e86130b09fd0f4220427c5cdb80dad77bc190a GIT binary patch literal 6148 zcmeHKI|>3p3{6x}u(7n9D|mxJ^aNhO!cQX#f`Z*vp39^8@<Ei<MzE2WC3!R1yjk`Y zn~jL*^t76Yj6`GrH<YV|w%L92mUS|tKse60$U#o$#c{tow5s1HjN6xutZ}WL`TK30 z`kuxt6`%rCfC^9nD)6NO)_Y-tVIU(FpaN9jp@4lK3f!<JHi7=>K=2U&*hAV4Yo8^6 z#S*}p*aRX2)1U%_sySk4(2*}$R}-7Spo`}4q4{LZ2}S+sIKOzgXbohf0#x8tfo^O^ z*8k`55A**ki90Gl1^!9_?adamDPAdiYwP8#*B1B^ZZ$W!8P-lg@OBLJc8ra+<FOY- aU9mOp*Tg2!>Bu`B$e#hzg+>K_t-u|h2^GNr literal 0 HcmV?d00001 diff --git a/samples/feedyou/newdesign.html b/samples/feedyou/newdesign.html index 41bcd8798d..0546921542 100644 --- a/samples/feedyou/newdesign.html +++ b/samples/feedyou/newdesign.html @@ -22,7 +22,7 @@ channel: { id: 'jtuqsq' }, }); }; - s.parentNode.insertBefore(t, s); + s.parentNode.insertBefore(t, s); })(window, document, 'script'); </script> <!-- druhý bot na testing: feedbot-demo-webchat, gvyddu --> diff --git a/src/App/App.tsx b/src/App/App.tsx index d3035ca4d5..c7e28f42f3 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -10,7 +10,7 @@ import { generateUserId } from '../utils/generateUserId' export type AppProps = ChatProps & { theme?: Theme; // option to override theme settings from remote config defaultTheme?: Theme; // option to set default template when no remote config found (on default microsite for example) - header?: { textWhenCollapsed?: string; text: string, extraHtml?: string, image?: string }; + header?: { textWhenCollapsed?: string; text: string, extraHtml?: string}; channel?: { index?: number, id?: string }; autoExpandTimeout?: number; enableScreenshotUpload?: boolean; diff --git a/src/App/templates/ExpandableTemplate/ExpandableTemplate.tsx b/src/App/templates/ExpandableTemplate/ExpandableTemplate.tsx index 1026da6b68..370e605c55 100644 --- a/src/App/templates/ExpandableTemplate/ExpandableTemplate.tsx +++ b/src/App/templates/ExpandableTemplate/ExpandableTemplate.tsx @@ -71,7 +71,7 @@ export class ExpandableTemplate extends React.Component<Props, State> { doesTemplateSupportPopupMsg = () => { const templateType = this.props.theme.template && this.props.theme.template.type - return ["expandable-knob", "sidebar"].includes(templateType) + return ["expandable-knob", "sidebar", "expandable-knob-new", "sidebar-new"].includes(templateType) } diff --git a/src/App/templates/ExpandableTemplate/Header.tsx b/src/App/templates/ExpandableTemplate/Header.tsx index 06c64c171b..d9eea4061c 100644 --- a/src/App/templates/ExpandableTemplate/Header.tsx +++ b/src/App/templates/ExpandableTemplate/Header.tsx @@ -10,26 +10,26 @@ export type Props = { export type State = { isMenuOpen: boolean; -} +}; export class Header extends React.Component<Props, State> { - state: State = {isMenuOpen: false} + state: State = { isMenuOpen: false }; render() { const { - appProps: { - theme: { mainColor, template }, - header: { extraHtml }, - }, - isCollapsed, - onClick, - } = this.props; + appProps: { + theme: { mainColor, template }, + header: { extraHtml } + }, + isCollapsed, + onClick + } = this.props; const handlePersistentMenuToggle = () => { - this.setState((prevState) => ({ + this.setState(prevState => ({ isMenuOpen: !prevState.isMenuOpen - })) - } + })); + }; let backgroundColor; if (template.type !== 'expandable-knob-new' && template.type !== 'sidebar-new') { @@ -37,98 +37,135 @@ export class Header extends React.Component<Props, State> { } const title = getTitle(this.props.appProps, isCollapsed); - + const avatar = template.avatar || "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 36 36' fill='none'%3E%3Cpath d='M36 18c0 9.941-8.059 18-18 18S0 27.941 0 18 8.059 0 18 0s18 8.059 18 18Z' fill='%23F7F9FB'/%3E%3Cpath d='M14.024 18.026h-.003v-.005h.004l.001.002v.001l-.002.002Zm3.978 0v-.001l.001-.002v-.002h-.006v.004l.002.001h.003Zm3.976 0h-.003l-.001-.002v-.002l.001-.001h.004v.003l-.001.002Z' fill='%23385B75'/%3E%3Cpath fillRule='evenodd' clipRule='evenodd' d='M18.718 9.023a8.912 8.912 0 0 1 5.669 2.59 8.91 8.91 0 0 1-10.583 14.12l-3.073 1.182a1.274 1.274 0 0 1-1.646-1.646l1.182-3.073a8.91 8.91 0 0 1 8.45-13.173ZM14.023 17a1.024 1.024 0 1 0 0 2.047 1.024 1.024 0 0 0 0-2.047Zm3.409.172a1.023 1.023 0 1 1 1.136 1.702 1.023 1.023 0 0 1-1.136-1.702ZM21.977 17a1.023 1.023 0 1 0 0 2.047 1.023 1.023 0 0 0 0-2.047Z' fill='%23385B75'/%3E%3C/svg%3E"; - const startOverIcon = - <svg xmlns="http://www.w3.org/2000/svg" width="17" height="15" viewBox="0 0 17 15" fill="none"> - <path d="M4.429 1 1 4.429l3.429 3.428" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /> - <path d="M1 4.429h10.286a4.571 4.571 0 0 1 0 9.142H5.57" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /> + const startOverIcon = ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="17" + height="15" + viewBox="0 0 17 15" + fill="none" + > + <path + d="M4.429 1 1 4.429l3.429 3.428" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M1 4.429h10.286a4.571 4.571 0 0 1 0 9.142H5.57" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> </svg> + ); return ( - <div - className='feedbot-header' - onClick={isCollapsed && onClick} - style={{ backgroundColor }}> - <div className='feedbot-header-name'> + <div className="feedbot-header" onClick={isCollapsed && onClick} style={{ backgroundColor }}> + <div className="feedbot-header-name"> {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( - <div - className='feedbot-avatar' - style={{backgroundImage: `url("${avatar}")`}}> - </div> + <div className="feedbot-avatar" style={{ backgroundImage: `url("${avatar}")` }}></div> ) : null} - <div className='feedbot-name'> - <span className='feedbot-title'>{title}</span> - {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( - <span className='feedbot-supportive-title'> - {template.supportiveTitle} - </span> - ): null} + <div className="feedbot-name"> + <span className="feedbot-title">{title}</span> + {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( + <span className="feedbot-supportive-title">{template.supportiveTitle}</span> + ) : null} </div> </div> - {extraHtml && ( - <span - className='feedbot-extra-html' - dangerouslySetInnerHTML={{ __html: extraHtml }} - /> - )} - - <div className='feedbot-header-actions'> - {(template.type === 'expandable-knob-new' || template.type === 'sidebar-new') && (checkFeedbotTestMode() || template.persistentMenu.length > 0) ? ( - <div className='feedbot-persistent-menu' onClick={handlePersistentMenuToggle}> - <a className='feedbot-persistent-menu-toggle'> - <svg xmlns="http://www.w3.org/2000/svg" width="3" height="17" viewBox="0 0 3 17" fill="none"> - <path d="M3 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm0 7a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm0 7a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z" fill="currentColor"/> - </svg> - </a> - {this.state.isMenuOpen && - <ul className='feedbot-persistent-menu-links'> - - { checkFeedbotTestMode() && - <li className='feedbot-persistent-menu-debug'> - <a onClick={handleStartOver}><span>Start over</span>{startOverIcon}</a> - </li> - } - - {template.persistentMenu.map((menuItem, index) => - <li key={index}> - <a onClick={() => handleTriggerDialog(menuItem.dialog)}> - {menuItem.title} + {extraHtml && ( + <span className="feedbot-extra-html" dangerouslySetInnerHTML={{ __html: extraHtml }} /> + )} + + <div className="feedbot-header-actions"> + {(template.type === 'expandable-knob-new' || template.type === 'sidebar-new') && + (checkFeedbotTestMode() || template.persistentMenu.length > 0) ? ( + <div className="feedbot-persistent-menu" onClick={handlePersistentMenuToggle}> + <a className="feedbot-persistent-menu-toggle"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="3" + height="17" + viewBox="0 0 3 17" + fill="none" + > + <path + d="M3 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm0 7a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm0 7a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z" + fill="currentColor" + /> + </svg> + </a> + {this.state.isMenuOpen && ( + <ul className="feedbot-persistent-menu-links"> + {checkFeedbotTestMode() && ( + <li className="feedbot-persistent-menu-debug"> + <a onClick={handleStartOver}> + <span>Start over</span> + {startOverIcon} </a> </li> )} + + {template.persistentMenu.map((menuItem, index) => ( + <li key={index}> + <a onClick={() => handleTriggerDialog(menuItem.dialog)}>{menuItem.title}</a> + </li> + ))} </ul> - } - </div> - ) : ( - null - )} - <a - onClick={(e) => { - e.preventDefault() - onClick() - }} - className='feedbot-minimize' - href='#'> - {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( - <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26" fill="none"> - <path fillRule="evenodd" clipRule="evenodd" d="M17.5303 8.46967C17.8232 8.76256 17.8232 9.23744 17.5303 9.53033L9.53033 17.5303C9.23744 17.8232 8.76256 17.8232 8.46967 17.5303C8.17678 17.2374 8.17678 16.7626 8.46967 16.4697L16.4697 8.46967C16.7626 8.17678 17.2374 8.17678 17.5303 8.46967Z" fill="currentColor"/> - <path fillRule="evenodd" clipRule="evenodd" d="M8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L17.5303 16.4697C17.8232 16.7626 17.8232 17.2374 17.5303 17.5303C17.2374 17.8232 16.7626 17.8232 16.4697 17.5303L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967Z" fill="currentColor"/> - <path fillRule="evenodd" clipRule="evenodd" d="M13 1.75C6.7868 1.75 1.75 6.7868 1.75 13C1.75 19.2132 6.7868 24.25 13 24.25C19.2132 24.25 24.25 19.2132 24.25 13C24.25 6.7868 19.2132 1.75 13 1.75ZM0.25 13C0.25 5.95837 5.95837 0.25 13 0.25C20.0416 0.25 25.75 5.95837 25.75 13C25.75 20.0416 20.0416 25.75 13 25.75C5.95837 25.75 0.25 20.0416 0.25 13Z" fill="currentColor"/> - </svg> - ) : ( - '_' - )} - </a> + )} + </div> + ) : null} + <a + onClick={e => { + e.preventDefault(); + onClick(); + }} + className="feedbot-minimize" + href="#" + > + {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="26" + height="26" + viewBox="0 0 26 26" + fill="none" + > + <path + fillRule="evenodd" + clipRule="evenodd" + d="M17.5303 8.46967C17.8232 8.76256 17.8232 9.23744 17.5303 9.53033L9.53033 17.5303C9.23744 17.8232 8.76256 17.8232 8.46967 17.5303C8.17678 17.2374 8.17678 16.7626 8.46967 16.4697L16.4697 8.46967C16.7626 8.17678 17.2374 8.17678 17.5303 8.46967Z" + fill="currentColor" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L17.5303 16.4697C17.8232 16.7626 17.8232 17.2374 17.5303 17.5303C17.2374 17.8232 16.7626 17.8232 16.4697 17.5303L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967Z" + fill="currentColor" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M13 1.75C6.7868 1.75 1.75 6.7868 1.75 13C1.75 19.2132 6.7868 24.25 13 24.25C19.2132 24.25 24.25 19.2132 24.25 13C24.25 6.7868 19.2132 1.75 13 1.75ZM0.25 13C0.25 5.95837 5.95837 0.25 13 0.25C20.0416 0.25 25.75 5.95837 25.75 13C25.75 20.0416 20.0416 25.75 13 25.75C5.95837 25.75 0.25 20.0416 0.25 13Z" + fill="currentColor" + /> + </svg> + ) : ( + '_' + )} + </a> + </div> </div> - </div> - ) + ); } - } const getTitle = (props: AppProps, isCollapsed: boolean) => { @@ -146,22 +183,22 @@ const getTitle = (props: AppProps, isCollapsed: boolean) => { * @param dialogId Dialog ID to be triggered on persistent menu item click */ const handleTriggerDialog = (dialogId: string) => { - window.dispatchEvent(new CustomEvent('feedbot:trigger-dialog', { detail: dialogId })) -} + window.dispatchEvent(new CustomEvent('feedbot:trigger-dialog', { detail: dialogId })); +}; /** * Restart conversation via custom event */ const handleStartOver = () => { - window.dispatchEvent(new CustomEvent('feedbot:start-over')) -} + window.dispatchEvent(new CustomEvent('feedbot:start-over')); +}; /** * Check URL if test mode is being used (#feedbot-test-mode) * @returns Boolean */ const checkFeedbotTestMode = (): boolean => { - return window.location.hash === '#feedbot-test-mode' -} + return window.location.hash === '#feedbot-test-mode'; +}; export type HeaderProps = Props; diff --git a/src/App/templates/ExpandableTemplate/Signature/SignatureTemplate.tsx b/src/App/templates/ExpandableTemplate/Signature/SignatureTemplate.tsx index eb508dd803..7f6329f834 100644 --- a/src/App/templates/ExpandableTemplate/Signature/SignatureTemplate.tsx +++ b/src/App/templates/ExpandableTemplate/Signature/SignatureTemplate.tsx @@ -1,16 +1,16 @@ -import * as React from 'react'; +import * as React from 'react' export type Props = { - botId: string; -}; + botId: string +} export const SignatureTemplate: React.StatelessComponent = ({ children }) => { return ( - <div className='feedbot-signature'> - <div className='feedbot-signature-row'> + <div className="feedbot-signature"> + <div className="feedbot-signature-row"> <div style={{ alignSelf: 'center' }}>powered by</div> {children} </div> </div> - ); -}; + ) +} diff --git a/src/Shell.tsx b/src/Shell.tsx index c1b8305d1b..11ce524c91 100644 --- a/src/Shell.tsx +++ b/src/Shell.tsx @@ -4,307 +4,257 @@ import { User } from 'botframework-directlinejs'; import { classList } from './Chat'; import { Dispatch, connect } from 'react-redux'; import { Strings } from './Strings'; -import { Speech } from './SpeechModule'; -import { - ChatActions, - ListeningState, - sendMessage, - sendFiles, - sendScreenshot, -} from './Store'; +import { Speech } from './SpeechModule' +import { ChatActions, ListeningState, sendMessage, sendFiles, sendScreenshot } from './Store'; -import * as html2canvas from 'html2canvas'; +import * as html2canvas from 'html2canvas' interface Props { - inputText: string; - strings: Strings; - listeningState: ListeningState; - showUploadButton: boolean; - uploadCapture: 'image/*' | 'video/*' | 'audio/*' | string; - disableInput: boolean; + inputText: string, + strings: Strings, + listeningState: ListeningState, + showUploadButton: boolean, + uploadCapture: 'image/*' | 'video/*' | 'audio/*' | string, + disableInput: boolean - onChangeText: (inputText: string) => void; + onChangeText: (inputText: string) => void - sendMessage: (inputText: string) => void; - sendFiles: (files: FileList) => void; - sendScreenshot: (screen: string) => void; - stopListening: () => void; - startListening: () => void; + sendMessage: (inputText: string) => void, + sendFiles: (files: FileList) => void, + sendScreenshot: (screen: string) => void, + stopListening: () => void, + startListening: () => void } export interface ShellFunctions { - focus: (appendKey?: string) => void; + focus: (appendKey?: string) => void } class ShellContainer extends React.Component<Props> implements ShellFunctions { - private textInput: HTMLTextAreaElement; - private fileInput: HTMLInputElement; + private textInput: HTMLTextAreaElement; + private fileInput: HTMLInputElement; - componentDidUpdate(prevProps: Props) { - if ( - prevProps.disableInput === true && - this.props.disableInput === false - ) { - this.textInput.focus(); - } - } + componentDidUpdate(prevProps: Props) { + if (prevProps.disableInput === true && this.props.disableInput === false) { + this.textInput.focus(); + } + } - private sendMessage() { - if (this.props.inputText.trim().length > 0) { - this.props.sendMessage(this.props.inputText); - } - } + private sendMessage() { + if (this.props.inputText.trim().length > 0) { + this.props.sendMessage(this.props.inputText); + } + } - private handleSendButtonKeyPress( - evt: React.KeyboardEvent<HTMLButtonElement> - ) { - if (evt.key === 'Enter' || evt.key === ' ') { - evt.preventDefault(); - this.sendMessage(); - this.textInput.focus(); - } - } + private handleSendButtonKeyPress(evt: React.KeyboardEvent<HTMLButtonElement>) { + if (evt.key === 'Enter' || evt.key === ' ') { + evt.preventDefault(); + this.sendMessage(); + this.textInput.focus(); + } + } - private handleUploadButtonKeyPress( - evt: React.KeyboardEvent<HTMLLabelElement> - ) { - if (evt.key === 'Enter' || evt.key === ' ') { - evt.preventDefault(); - this.fileInput.click(); - } - } + private handleUploadButtonKeyPress(evt: React.KeyboardEvent<HTMLLabelElement>) { + if (evt.key === 'Enter' || evt.key === ' ') { + evt.preventDefault(); + this.fileInput.click(); + } + } - private onKeyPress(e: React.KeyboardEvent<HTMLTextAreaElement>) { - if (e.key === 'Enter' && !e.shiftKey) { - this.sendMessage(); - e.stopPropagation(); - e.preventDefault(); - } - } + private onKeyPress(e: React.KeyboardEvent<HTMLTextAreaElement>) { + if (e.key === 'Enter' && !e.shiftKey) { + this.sendMessage(); + e.stopPropagation() + e.preventDefault() + } + } - private onClickSend() { - this.sendMessage(); - } + private onClickSend() { + this.sendMessage(); + } - private onChangeFile() { - this.props.sendFiles(this.fileInput.files); - this.fileInput.value = null; - this.textInput.focus(); - } + private onChangeFile() { + this.props.sendFiles(this.fileInput.files); + this.fileInput.value = null; + this.textInput.focus(); + } - private onTextInputFocus() { - if (this.props.listeningState === ListeningState.STARTED) { - this.props.stopListening(); - } - } + private onTextInputFocus() { + if (this.props.listeningState === ListeningState.STARTED) { + this.props.stopListening(); + } + } - private onClickMic() { - if (this.props.listeningState === ListeningState.STARTED) { - this.props.stopListening(); - } else if (this.props.listeningState === ListeningState.STOPPED) { - this.props.startListening(); - } - } + private onClickMic() { + if (this.props.listeningState === ListeningState.STARTED) { + this.props.stopListening(); + } else if (this.props.listeningState === ListeningState.STOPPED) { + this.props.startListening(); + } + } - public focus(appendKey?: string) { - this.textInput.focus(); + public focus(appendKey?: string) { + this.textInput.focus(); - if (appendKey) { - this.props.onChangeText(this.props.inputText + appendKey); - } - } + if (appendKey) { + this.props.onChangeText(this.props.inputText + appendKey); + } + } - private async takeScreenshot() { - const screen = await html2canvas(document.body, { - allowTaint: true, - useCORS: true, - }).then((canvas) => { - const dataURI = canvas.toDataURL('image/png'); + private async takeScreenshot() { + const screen = await html2canvas(document.body, { allowTaint: true, useCORS: true }).then((canvas) => { + const dataURI = canvas.toDataURL("image/png"); - return dataURI; - }); - this.props.sendScreenshot(screen); - } + return dataURI + }) + this.props.sendScreenshot(screen); + } - render() { - const className = classList( - 'wc-console', - this.props.inputText.length > 0 && 'has-text', - this.props.showUploadButton && 'has-upload-button', - this.props.disableInput && 'disable-input' - ); + render() { + const className = classList( + 'wc-console', + this.props.inputText.length > 0 && 'has-text', + this.props.showUploadButton && 'has-upload-button', + this.props.disableInput && 'disable-input' + ); - const showMicButton = - this.props.listeningState !== ListeningState.STOPPED || - (Speech.SpeechRecognizer.speechIsAvailable() && - !this.props.inputText.length); + const showMicButton = this.props.listeningState !== ListeningState.STOPPED || (Speech.SpeechRecognizer.speechIsAvailable() && !this.props.inputText.length); - const sendButtonClassName = classList( - 'wc-send', - showMicButton && 'hidden' - ); + const sendButtonClassName = classList( + 'wc-send', + showMicButton && 'hidden' + ); - const micButtonClassName = classList( - 'wc-mic', - !showMicButton && 'hidden', - this.props.listeningState === ListeningState.STARTED && 'active', - this.props.listeningState !== ListeningState.STARTED && 'inactive' - ); + const micButtonClassName = classList( + 'wc-mic', + !showMicButton && 'hidden', + this.props.listeningState === ListeningState.STARTED && 'active', + this.props.listeningState !== ListeningState.STARTED && 'inactive' + ); - const placeholder = - this.props.listeningState === ListeningState.STARTED - ? this.props.strings.listeningIndicator - : this.props.strings.consolePlaceholder; + const placeholder = this.props.listeningState === ListeningState.STARTED ? this.props.strings.listeningIndicator : this.props.strings.consolePlaceholder; - return ( - <div className={className}> - {this.props.showUploadButton && ( - <label - className='wc-upload' - htmlFor='wc-upload-input' - onKeyPress={(evt) => - this.handleUploadButtonKeyPress(evt) - } - tabIndex={0}> - <svg> + return ( + <div className={className}> + { + this.props.showUploadButton && + <label + className="wc-upload" + htmlFor="wc-upload-input" + onKeyPress={evt => this.handleUploadButtonKeyPress(evt)} + tabIndex={0} + > + <svg> <path d='M0.850098 3.99961C0.850098 2.25991 2.2604 0.849609 4.0001 0.849609H10.0001C10.199 0.849609 10.3898 0.928627 10.5304 1.06928L16.5304 7.06928C16.6711 7.20993 16.7501 7.4007 16.7501 7.59961V15.9996C16.7501 17.7393 15.3398 19.1496 13.6001 19.1496H4.0001C2.2604 19.1496 0.850098 17.7393 0.850098 15.9996V3.99961ZM4.0001 2.34961C3.08883 2.34961 2.3501 3.08834 2.3501 3.99961V15.9996C2.3501 16.9109 3.08883 17.6496 4.0001 17.6496H13.6001C14.5114 17.6496 15.2501 16.9109 15.2501 15.9996V7.91027L9.68944 2.34961H4.0001Z' /> <path d='M10 0.849609C10.4142 0.849609 10.75 1.1854 10.75 1.59961V5.19961C10.75 6.11088 11.4887 6.84961 12.4 6.84961H16C16.4142 6.84961 16.75 7.1854 16.75 7.59961C16.75 8.01382 16.4142 8.34961 16 8.34961H12.4C10.6603 8.34961 9.25 6.93931 9.25 5.19961V1.59961C9.25 1.1854 9.58579 0.849609 10 0.849609Z' /> </svg> - </label> - )} - {this.props.showUploadButton && ( - <input - id='wc-upload-input' - tabIndex={-1} - type='file' - ref={(input) => (this.fileInput = input)} - // multiple - onChange={() => this.onChangeFile()} - aria-label={this.props.strings.uploadFile} - role='button' - capture={!!this.props.uploadCapture} - accept={this.props.uploadCapture} - /> - )} - {this.props.showUploadButton && ( - <button - className='wc-upload-screenshot' - onClick={() => { - this.takeScreenshot(); - }}> - <svg - aria-hidden='true' - focusable='false' - data-prefix='fas' - data-icon='camera' - role='img' - xmlns='http://www.w3.org/2000/svg' - viewBox='0 0 512 512'> - <path - fill='#8a8a8a' - d='M512 144v288c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V144c0-26.5 21.5-48 48-48h88l12.3-32.9c7-18.7 24.9-31.1 44.9-31.1h125.5c20 0 37.9 12.4 44.9 31.1L376 96h88c26.5 0 48 21.5 48 48zM376 288c0-66.2-53.8-120-120-120s-120 53.8-120 120 53.8 120 120 120 120-53.8 120-120zm-32 0c0 48.5-39.5 88-88 88s-88-39.5-88-88 39.5-88 88-88 88 39.5 88 88z'></path> - </svg> - </button> - )} - <div className='wc-textbox'> - <textarea - className='wc-shellinput' - ref={(input) => (this.textInput = input)} - autoFocus - value={this.props.inputText} - onChange={(_) => - this.props.onChangeText(this.textInput.value) - } - onKeyPress={(e) => this.onKeyPress(e)} - onFocus={() => this.onTextInputFocus()} - placeholder={placeholder} - disabled={this.props.disableInput} - aria-label={this.props.inputText ? null : placeholder} - aria-live='polite'></textarea> - </div> - <button - className={sendButtonClassName} - onClick={() => this.onClickSend()} - aria-label={this.props.strings.send} - role='button' - onKeyPress={(evt) => this.handleSendButtonKeyPress(evt)} - tabIndex={0} - type='button'> - <svg> + </label> + } + { + this.props.showUploadButton && + <input + id="wc-upload-input" + tabIndex={-1} + type="file" + ref={input => this.fileInput = input} + // multiple + onChange={() => this.onChangeFile()} + aria-label={this.props.strings.uploadFile} + role="button" + capture={!!this.props.uploadCapture} + accept={this.props.uploadCapture} + /> + } + { + this.props.showUploadButton && + <button className="wc-upload-screenshot" onClick={() => { this.takeScreenshot() }}><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="camera" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#8a8a8a" d="M512 144v288c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V144c0-26.5 21.5-48 48-48h88l12.3-32.9c7-18.7 24.9-31.1 44.9-31.1h125.5c20 0 37.9 12.4 44.9 31.1L376 96h88c26.5 0 48 21.5 48 48zM376 288c0-66.2-53.8-120-120-120s-120 53.8-120 120 53.8 120 120 120 120-53.8 120-120zm-32 0c0 48.5-39.5 88-88 88s-88-39.5-88-88 39.5-88 88-88 88 39.5 88 88z"></path></svg></button> + } + <div className="wc-textbox"> + <textarea + className="wc-shellinput" + ref={input => this.textInput = input} + autoFocus + value={this.props.inputText} + onChange={_ => this.props.onChangeText(this.textInput.value)} + onKeyPress={e => this.onKeyPress(e)} + onFocus={() => this.onTextInputFocus()} + placeholder={placeholder} + disabled={this.props.disableInput} + aria-label={this.props.inputText ? null : placeholder} + aria-live="polite" + ></textarea> + </div> + <button + className={sendButtonClassName} + onClick={() => this.onClickSend()} + aria-label={this.props.strings.send} + role="button" + onKeyPress={evt => this.handleSendButtonKeyPress(evt)} + tabIndex={0} + type="button" + > + <svg> <path d='M3.03792 2.33702C2.89522 2.26748 2.73411 2.245 2.57777 2.27287C2.42024 2.30095 2.27582 2.37868 2.16561 2.49469C2.05541 2.61071 1.9852 2.75893 1.96524 2.91769C1.9458 3.0723 1.97502 3.22902 2.04866 3.36612L7.09739 11.601C7.24476 11.8414 7.24487 12.1441 7.09769 12.3845L2.04877 20.6337C1.97505 20.7708 1.94579 20.9276 1.96524 21.0823C1.9852 21.2411 2.05541 21.3893 2.16561 21.5053C2.27582 21.6213 2.42024 21.6991 2.57777 21.7271C2.73406 21.755 2.89513 21.7325 3.03779 21.6631L22.047 11.9935L3.03792 2.33702ZM2.31455 0.796147C2.78714 0.711908 3.27429 0.781026 3.7048 0.9934L3.7127 0.997297L22.7267 10.6563C22.9734 10.7818 23.1809 10.9733 23.3257 11.2093C23.4704 11.4453 23.547 11.7167 23.547 11.9935C23.547 12.2703 23.4704 12.5417 23.3257 12.7777C23.1809 13.0137 22.9738 13.205 22.727 13.3305C22.727 13.3305 22.7271 13.3305 22.727 13.3305L3.70482 23.0067C3.27432 23.219 2.78714 23.2881 2.31455 23.2039C1.84196 23.1196 1.40869 22.8865 1.07808 22.5384C0.74746 22.1904 0.536825 21.7457 0.476953 21.2694C0.41708 20.7931 0.51111 20.3102 0.7453 19.8911C0.750131 19.8825 0.755132 19.8739 0.760301 19.8655L5.57846 11.9933L0.760597 4.13502C0.755323 4.12642 0.750223 4.11771 0.7453 4.1089C0.51111 3.68986 0.41708 3.2069 0.476953 2.73061C0.536825 2.25431 0.747461 1.80965 1.07808 1.46161C1.40869 1.11357 1.84196 0.880386 2.31455 0.796147Z' /> <path d='M5.70299 11.993C5.70299 11.5788 6.03877 11.243 6.45299 11.243H22.797C23.2112 11.243 23.547 11.5788 23.547 11.993C23.547 12.4072 23.2112 12.743 22.797 12.743H6.45299C6.03877 12.743 5.70299 12.4072 5.70299 11.993Z' /> </svg> - </button> - <button - className={micButtonClassName} - onClick={() => this.onClickMic()} - aria-label={this.props.strings.speak} - role='button' - tabIndex={0} - type='button'> - <svg width='28' height='22' viewBox='0 0 58 58'> - <path d='M 44 28 C 43.448 28 43 28.447 43 29 L 43 35 C 43 42.72 36.72 49 29 49 C 21.28 49 15 42.72 15 35 L 15 29 C 15 28.447 14.552 28 14 28 C 13.448 28 13 28.447 13 29 L 13 35 C 13 43.485 19.644 50.429 28 50.949 L 28 56 L 23 56 C 22.448 56 22 56.447 22 57 C 22 57.553 22.448 58 23 58 L 35 58 C 35.552 58 36 57.553 36 57 C 36 56.447 35.552 56 35 56 L 30 56 L 30 50.949 C 38.356 50.429 45 43.484 45 35 L 45 29 C 45 28.447 44.552 28 44 28 Z' /> - <path - id='micFilling' - d='M 28.97 44.438 L 28.97 44.438 C 23.773 44.438 19.521 40.033 19.521 34.649 L 19.521 11.156 C 19.521 5.772 23.773 1.368 28.97 1.368 L 28.97 1.368 C 34.166 1.368 38.418 5.772 38.418 11.156 L 38.418 34.649 C 38.418 40.033 34.166 44.438 28.97 44.438 Z' - /> - <path d='M 29 46 C 35.065 46 40 41.065 40 35 L 40 11 C 40 4.935 35.065 0 29 0 C 22.935 0 18 4.935 18 11 L 18 35 C 18 41.065 22.935 46 29 46 Z M 20 11 C 20 6.037 24.038 2 29 2 C 33.962 2 38 6.037 38 11 L 38 35 C 38 39.963 33.962 44 29 44 C 24.038 44 20 39.963 20 35 L 20 11 Z' /> - </svg> - </button> - </div> - ); - } + </button> + <button + className={micButtonClassName} + onClick={() => this.onClickMic()} + aria-label={this.props.strings.speak} + role="button" + tabIndex={0} + type="button" + > + <svg width="28" height="22" viewBox="0 0 58 58" > + <path d="M 44 28 C 43.448 28 43 28.447 43 29 L 43 35 C 43 42.72 36.72 49 29 49 C 21.28 49 15 42.72 15 35 L 15 29 C 15 28.447 14.552 28 14 28 C 13.448 28 13 28.447 13 29 L 13 35 C 13 43.485 19.644 50.429 28 50.949 L 28 56 L 23 56 C 22.448 56 22 56.447 22 57 C 22 57.553 22.448 58 23 58 L 35 58 C 35.552 58 36 57.553 36 57 C 36 56.447 35.552 56 35 56 L 30 56 L 30 50.949 C 38.356 50.429 45 43.484 45 35 L 45 29 C 45 28.447 44.552 28 44 28 Z" /> + <path id="micFilling" d="M 28.97 44.438 L 28.97 44.438 C 23.773 44.438 19.521 40.033 19.521 34.649 L 19.521 11.156 C 19.521 5.772 23.773 1.368 28.97 1.368 L 28.97 1.368 C 34.166 1.368 38.418 5.772 38.418 11.156 L 38.418 34.649 C 38.418 40.033 34.166 44.438 28.97 44.438 Z" /> + <path d="M 29 46 C 35.065 46 40 41.065 40 35 L 40 11 C 40 4.935 35.065 0 29 0 C 22.935 0 18 4.935 18 11 L 18 35 C 18 41.065 22.935 46 29 46 Z M 20 11 C 20 6.037 24.038 2 29 2 C 33.962 2 38 6.037 38 11 L 38 35 C 38 39.963 33.962 44 29 44 C 24.038 44 20 39.963 20 35 L 20 11 Z" /> + </svg> + </button> + </div> + ); + } } export const Shell = connect( - (state: ChatState) => ({ - // passed down to ShellContainer - inputText: state.shell.input, - showUploadButton: state.format.showUploadButton, - uploadCapture: state.format.uploadCapture, - disableInput: state.format.disableInput, - strings: state.format.strings, - // only used to create helper functions below - locale: state.format.locale, - user: state.connection.user, - listeningState: state.shell.listeningState, - }), - { - // passed down to ShellContainer - onChangeText: (input: string) => - ({ type: 'Update_Input', input, source: 'text' } as ChatActions), - stopListening: () => ({ type: 'Listening_Stopping' }), - startListening: () => ({ type: 'Listening_Starting' }), - // only used to create helper functions below - sendMessage, - sendFiles, - sendScreenshot, - }, - (stateProps: any, dispatchProps: any, ownProps: any): Props => ({ - // from stateProps - inputText: stateProps.inputText, - showUploadButton: stateProps.showUploadButton, - uploadCapture: stateProps.uploadCapture, - disableInput: stateProps.disableInput, - strings: stateProps.strings, - listeningState: stateProps.listeningState, - // from dispatchProps - onChangeText: dispatchProps.onChangeText, - // helper functions - sendMessage: (text: string) => - dispatchProps.sendMessage(text, stateProps.user, stateProps.locale), - sendFiles: (files: FileList) => - dispatchProps.sendFiles(files, stateProps.user, stateProps.locale), - sendScreenshot: (screen: string) => - dispatchProps.sendScreenshot( - screen, - stateProps.user, - stateProps.locale - ), - startListening: () => dispatchProps.startListening(), - stopListening: () => dispatchProps.stopListening(), - }), - { - withRef: true, - } -)(ShellContainer); + (state: ChatState) => ({ + // passed down to ShellContainer + inputText: state.shell.input, + showUploadButton: state.format.showUploadButton, + uploadCapture: state.format.uploadCapture, + disableInput: state.format.disableInput, + strings: state.format.strings, + // only used to create helper functions below + locale: state.format.locale, + user: state.connection.user, + listeningState: state.shell.listeningState + }), { + // passed down to ShellContainer + onChangeText: (input: string) => ({ type: 'Update_Input', input, source: "text" } as ChatActions), + stopListening: () => ({ type: 'Listening_Stopping' }), + startListening: () => ({ type: 'Listening_Starting' }), + // only used to create helper functions below + sendMessage, + sendFiles, + sendScreenshot +}, (stateProps: any, dispatchProps: any, ownProps: any): Props => ({ + // from stateProps + inputText: stateProps.inputText, + showUploadButton: stateProps.showUploadButton, + uploadCapture: stateProps.uploadCapture, + disableInput: stateProps.disableInput, + strings: stateProps.strings, + listeningState: stateProps.listeningState, + // from dispatchProps + onChangeText: dispatchProps.onChangeText, + // helper functions + sendMessage: (text: string) => dispatchProps.sendMessage(text, stateProps.user, stateProps.locale), + sendFiles: (files: FileList) => dispatchProps.sendFiles(files, stateProps.user, stateProps.locale), + sendScreenshot: (screen: string) => dispatchProps.sendScreenshot(screen, stateProps.user, stateProps.locale), + startListening: () => dispatchProps.startListening(), + stopListening: () => dispatchProps.stopListening() +}), { + withRef: true +} +)(ShellContainer); \ No newline at end of file diff --git a/src/scss/botchat-redesign.scss b/src/scss/botchat-redesign.scss index 66858ae0a5..f0f31d38bc 100644 --- a/src/scss/botchat-redesign.scss +++ b/src/scss/botchat-redesign.scss @@ -1110,6 +1110,10 @@ body .wc-app, gap: 3px; } .feedbot-supportive-title { + max-width: 20em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; font-size: 13px; color: $grey; } diff --git a/src/themes/ExpandableBarTheme.tsx b/src/themes/ExpandableBarTheme.tsx index 422b08601a..8fdf46acd7 100644 --- a/src/themes/ExpandableBarTheme.tsx +++ b/src/themes/ExpandableBarTheme.tsx @@ -1,5 +1,5 @@ -import { BaseTheme } from './BaseTheme'; -import { Theme } from './index'; +import { BaseTheme } from './BaseTheme' +import { Theme } from './index' export const ExpandableBarTheme = (theme: Theme) => ` .feedbot-reset { @@ -92,4 +92,4 @@ export const ExpandableBarTheme = (theme: Theme) => ` .wc-carousel .wc-hscroll > ul > li > .wc-card > div > .ac-container > .ac-container .ac-image{ border-radius: 5px 5px 0 0; } -`; +` diff --git a/src/themes/ExpandableKnobTheme.tsx b/src/themes/ExpandableKnobTheme.tsx index cff918bc36..8fb762b9ef 100644 --- a/src/themes/ExpandableKnobTheme.tsx +++ b/src/themes/ExpandableKnobTheme.tsx @@ -1,5 +1,5 @@ -import { ExpandableBarTheme } from './ExpandableBarTheme'; -import { Theme } from './index'; +import { ExpandableBarTheme } from './ExpandableBarTheme' +import { Theme } from './index' export const ExpandableKnobTheme = (theme: Theme) => ` .feedbot-reset { @@ -56,11 +56,10 @@ export const ExpandableKnobTheme = (theme: Theme) => ` height: 100%; padding: 0px; - background-image: url(${ - theme.template && theme.template.iconUrl - ? theme.template.iconUrl - : 'https://cdn.feedyou.ai/webchat/message-icon.png' - }); + background-image: url(${(theme.template && theme.template.iconUrl) + ? theme.template.iconUrl + : 'https://cdn.feedyou.ai/webchat/message-icon.png' + }); background-size: 50px 50px; background-position: 12px 12px; background-repeat: no-repeat; @@ -125,4 +124,4 @@ export const ExpandableKnobTheme = (theme: Theme) => ` } ${ExpandableBarTheme(theme)} -`; +` diff --git a/src/themes/SidebarTheme.tsx b/src/themes/SidebarTheme.tsx index a313884be4..3f62582442 100644 --- a/src/themes/SidebarTheme.tsx +++ b/src/themes/SidebarTheme.tsx @@ -1,8 +1,8 @@ -import { ExpandableKnobTheme } from './ExpandableKnobTheme'; -import { Theme } from './index'; +import { ExpandableKnobTheme } from './ExpandableKnobTheme' +import { Theme } from './index' function getSidebarBackgroundColor(theme: Theme) { - return '#e1e1e1'; + return '#e1e1e1' // TODO make background tint configurable in theme /*const color = theme.mainColor @@ -79,9 +79,7 @@ export const SidebarTheme = (theme: Theme) => ` @supports ((-webkit-backdrop-filter: blur(40px)) or (backdrop-filter: blur(40px))) { .feedbot-wrapper { max-height: 100%; - background: linear-gradient(45deg, ${getSidebarBackgroundColor( - theme - )}33, #E1E1E1CE); + background: linear-gradient(45deg, ${getSidebarBackgroundColor(theme)}33, #E1E1E1CE); backdrop-filter: blur(40px); -webkit-backdrop-filter: blur(40px); } @@ -139,4 +137,4 @@ export const SidebarTheme = (theme: Theme) => ` justify-content: center; } -`; +` diff --git a/src/themes/index.ts b/src/themes/index.ts index 81564df8b6..88716d81d6 100644 --- a/src/themes/index.ts +++ b/src/themes/index.ts @@ -1,28 +1,27 @@ -import { ExpandableBarTheme } from './ExpandableBarTheme'; -import { FullScreenTheme } from './FullScreenTheme'; -import { ExpandableKnobTheme } from './ExpandableKnobTheme'; -import { SidebarTheme } from './SidebarTheme'; -import { ExpandableKnobThemeNew } from './ExpandableKnobThemeNew'; -import { SidebarThemeNew } from './SidebarThemeNew'; +import { ExpandableBarTheme } from './ExpandableBarTheme' +import { FullScreenTheme } from './FullScreenTheme' +import { ExpandableKnobTheme } from './ExpandableKnobTheme' +import { SidebarTheme } from './SidebarTheme' +import { ExpandableKnobThemeNew } from './ExpandableKnobThemeNew' +import { SidebarThemeNew } from './SidebarThemeNew' export type Theme = { mainColor: string; - secondaryColor: string; template?: { // Dost možná tu nějaký propy chyběj, // tak je neváhej připsat! :) - autoExpandTimeout?: number; - type?: string; - headerText?: string; - collapsedHeaderText?: string; + autoExpandTimeout?: number, + type?: string, + headerText?: string, + collapsedHeaderText?: string, popupMessage?: { - title: string; - description: string; - timeout: number; - }; - iconUrl?: string; - customScript?: string; - logoUrl?: string; + title: string, + description: string, + timeout: number + }, + iconUrl?: string, + customScript?: string, + logoUrl?: string, avatar?: string; supportiveTitle?: string; persistentMenu?: [{ @@ -31,35 +30,33 @@ export type Theme = { }] }; customCss?: string; - showSignature?: boolean; - enableScreenshotUpload?: boolean; + showSignature?: boolean, + enableScreenshotUpload?: boolean signature?: { - partnerLogoUrl: string; - partnerLogoStyle: string; - partnerLinkUrl: string; - partnerName: string; - mode: string; + partnerLogoUrl: string, + partnerLogoStyle: string, + partnerLinkUrl: string, + partnerName: string, + mode: string, }; }; export function getStyleForTheme(theme: Theme, remoteConfig: boolean): string { switch (theme && theme.template && theme.template.type) { case 'expandable-bar': - return ExpandableBarTheme(theme); + return ExpandableBarTheme(theme) case 'full-screen': - return FullScreenTheme(theme); + return FullScreenTheme(theme) case 'expandable-knob': - return ExpandableKnobTheme(theme); + return ExpandableKnobTheme(theme) case 'sidebar': - return SidebarTheme(theme); + return SidebarTheme(theme) case 'expandable-knob-new': - return ExpandableKnobThemeNew(theme); + return ExpandableKnobThemeNew(theme) case 'sidebar-new': - return SidebarThemeNew(theme); + return SidebarThemeNew(theme) } // backward compatibility - knob is new default for remote config, old default is bar - return remoteConfig - ? ExpandableKnobTheme(theme) - : ExpandableBarTheme(theme); + return remoteConfig ? ExpandableKnobTheme(theme) : ExpandableBarTheme(theme) } From 180a779512914a1f0a0fe56a2a7bf8b8f53b4343 Mon Sep 17 00:00:00 2001 From: Lukas Podmelle <podmellelukas@gmail.com> Date: Tue, 16 Jan 2024 15:27:57 +0100 Subject: [PATCH 7/9] Fixes --- .../templates/ExpandableTemplate/Header.tsx | 26 +++++------ .../Signature/Signature.tsx | 44 +++++++++---------- .../Signature/SignatureLink.tsx | 35 ++++++--------- src/themes/ExpandableKnobTheme.tsx | 6 +-- src/themes/SidebarTheme.tsx | 1 - src/themes/index.ts | 3 +- 6 files changed, 52 insertions(+), 63 deletions(-) diff --git a/src/App/templates/ExpandableTemplate/Header.tsx b/src/App/templates/ExpandableTemplate/Header.tsx index d9eea4061c..2ff00edc41 100644 --- a/src/App/templates/ExpandableTemplate/Header.tsx +++ b/src/App/templates/ExpandableTemplate/Header.tsx @@ -1,12 +1,12 @@ -import * as React from 'react'; -import { AppProps } from '../../App'; -import { Theme } from '../../../themes'; +import * as React from 'react' +import { AppProps } from '../../App' +import { Theme } from '../../../themes' export type Props = { - appProps: AppProps; - onClick(): void; - isCollapsed: boolean; -}; + appProps: AppProps + onClick(): void + isCollapsed: boolean +} export type State = { isMenuOpen: boolean; @@ -169,13 +169,13 @@ export class Header extends React.Component<Props, State> { } const getTitle = (props: AppProps, isCollapsed: boolean) => { - const { text, textWhenCollapsed } = props.header; + const { text, textWhenCollapsed } = props.header - const titleWhenExpanded = text || 'Chatbot'; - const titleWhenCollapsed = textWhenCollapsed || titleWhenExpanded; - const titleToShow = isCollapsed ? titleWhenCollapsed : titleWhenExpanded; + const titleWhenExpanded = text || 'Chatbot' + const titleWhenCollapsed = textWhenCollapsed || titleWhenExpanded + const titleToShow = isCollapsed ? titleWhenCollapsed : titleWhenExpanded - return titleToShow; + return titleToShow }; /** @@ -201,4 +201,4 @@ const checkFeedbotTestMode = (): boolean => { return window.location.hash === '#feedbot-test-mode'; }; -export type HeaderProps = Props; +export type HeaderProps = Props diff --git a/src/App/templates/ExpandableTemplate/Signature/Signature.tsx b/src/App/templates/ExpandableTemplate/Signature/Signature.tsx index c79bf7a509..ada5e3e25b 100644 --- a/src/App/templates/ExpandableTemplate/Signature/Signature.tsx +++ b/src/App/templates/ExpandableTemplate/Signature/Signature.tsx @@ -1,21 +1,19 @@ -import * as React from 'react'; -import { SignatureLink } from './SignatureLink'; -import { Theme } from '../../../../themes'; -import { AppProps } from '../../../App'; +import * as React from 'react' +import { SignatureLink } from './SignatureLink' +import { Theme } from '../../../../themes' +import { AppProps } from '../../../App' -import { SignatureTemplate } from './SignatureTemplate'; +import { SignatureTemplate } from './SignatureTemplate' -type SignatureSchema = Theme['signature']; -const FEEDYOU_LOGO_IMG_SRC = - 'https://cdn.feedyou.ai/webchat/feedyou_logo_red.png'; +type SignatureSchema = Theme['signature'] +const FEEDYOU_LOGO_IMG_SRC = 'https://cdn.feedyou.ai/webchat/feedyou_logo_red.png' export type Props = { - signature: SignatureSchema; - appProps: AppProps; - botId: string; + signature: SignatureSchema + appProps: AppProps + botId: string }; -const getLinkQueryString = (botId: string) => - `?utm_source=webchat&utm_medium=chatbot&utm_campaign=${botId}`; +const getLinkQueryString = (botId: string) => `?utm_source=webchat&utm_medium=chatbot&utm_campaign=${botId}` export const Signature: React.StatelessComponent<Props> = ({ signature, @@ -28,18 +26,18 @@ export const Signature: React.StatelessComponent<Props> = ({ partnerLogoStyle, mode, partnerName, - } = signature; + } = signature const { theme: { template }, - } = appProps; + } = appProps const attachQueryStringToUrl = (url: string) => - `${url}${getLinkQueryString(botId)}`; + `${url}${getLinkQueryString(botId)}` const enhancedFeedyouUrl = attachQueryStringToUrl('https://feedyou.ai'); const enhancedPartnerUrl = partnerLinkUrl ? attachQueryStringToUrl(partnerLinkUrl) - : enhancedFeedyouUrl; + : enhancedFeedyouUrl const feedyouLink = ( <SignatureLink @@ -81,7 +79,7 @@ export const Signature: React.StatelessComponent<Props> = ({ ); } if (mode === 'partner') { - return <SignatureTemplate>{partnerLink}</SignatureTemplate>; + return <SignatureTemplate>{partnerLink}</SignatureTemplate> } return ( <SignatureTemplate> @@ -102,14 +100,14 @@ export const Signature: React.StatelessComponent<Props> = ({ <div style={{ alignSelf: 'center' }}>&</div> {feedyouLink} </SignatureTemplate> - ); + ) } if (partnerLogoUrl && mode === 'partner') { - return <SignatureTemplate>{partnerLink}</SignatureTemplate>; + return <SignatureTemplate>{partnerLink}</SignatureTemplate> } - return <SignatureTemplate>{feedyouLink}</SignatureTemplate>; -}; + return <SignatureTemplate>{feedyouLink}</SignatureTemplate> +} -export type SignatureProps = Props; +export type SignatureProps = Props diff --git a/src/App/templates/ExpandableTemplate/Signature/SignatureLink.tsx b/src/App/templates/ExpandableTemplate/Signature/SignatureLink.tsx index 5b8811d85d..e163895de2 100644 --- a/src/App/templates/ExpandableTemplate/Signature/SignatureLink.tsx +++ b/src/App/templates/ExpandableTemplate/Signature/SignatureLink.tsx @@ -1,13 +1,13 @@ -import * as React from 'react'; -import cx from 'classnames'; -import { CustomStylesForCssClass } from '../../../CustomStylesForCssClass'; +import * as React from 'react' +import cx from 'classnames' +import { CustomStylesForCssClass } from '../../../CustomStylesForCssClass' export type Props = { - href: string; - imgSrc: string; - className?: string; - customStyles?: string; - text?: string; + href: string, + imgSrc: string + className?: string + customStyles?: string + text?: string }; export const SignatureLink: React.StatelessComponent<Props> = ({ @@ -18,19 +18,12 @@ export const SignatureLink: React.StatelessComponent<Props> = ({ text, }) => { return ( - <a - className={cx('signature-link', className)} - target='_blank' - href={href}> + <a className={cx('signature-link', className)} target="_blank" href={href}> {imgSrc ? <img src={imgSrc} alt='Logo' /> : text} - {className && customStyles && ( - <CustomStylesForCssClass - cssClass={className} - styles={customStyles} - /> - )} + {className && customStyles && + <CustomStylesForCssClass cssClass={className} styles={customStyles} />} </a> - ); -}; + ) +} -export type SignatureLinkProps = Props; +export type SignatureLinkProps = Props diff --git a/src/themes/ExpandableKnobTheme.tsx b/src/themes/ExpandableKnobTheme.tsx index 8fb762b9ef..da47b26f9f 100644 --- a/src/themes/ExpandableKnobTheme.tsx +++ b/src/themes/ExpandableKnobTheme.tsx @@ -57,9 +57,9 @@ export const ExpandableKnobTheme = (theme: Theme) => ` padding: 0px; background-image: url(${(theme.template && theme.template.iconUrl) - ? theme.template.iconUrl - : 'https://cdn.feedyou.ai/webchat/message-icon.png' - }); + ? theme.template.iconUrl + : 'https://cdn.feedyou.ai/webchat/message-icon.png' +}); background-size: 50px 50px; background-position: 12px 12px; background-repeat: no-repeat; diff --git a/src/themes/SidebarTheme.tsx b/src/themes/SidebarTheme.tsx index 3f62582442..577446a878 100644 --- a/src/themes/SidebarTheme.tsx +++ b/src/themes/SidebarTheme.tsx @@ -3,7 +3,6 @@ import { Theme } from './index' function getSidebarBackgroundColor(theme: Theme) { return '#e1e1e1' - // TODO make background tint configurable in theme /*const color = theme.mainColor if(color.startsWith("rgb")){ diff --git a/src/themes/index.ts b/src/themes/index.ts index 88716d81d6..707762b907 100644 --- a/src/themes/index.ts +++ b/src/themes/index.ts @@ -36,8 +36,8 @@ export type Theme = { partnerLogoUrl: string, partnerLogoStyle: string, partnerLinkUrl: string, + mode: string partnerName: string, - mode: string, }; }; @@ -56,7 +56,6 @@ export function getStyleForTheme(theme: Theme, remoteConfig: boolean): string { case 'sidebar-new': return SidebarThemeNew(theme) } - // backward compatibility - knob is new default for remote config, old default is bar return remoteConfig ? ExpandableKnobTheme(theme) : ExpandableBarTheme(theme) } From 805c5f81a8c0f828bce93e570f54a165050d9886 Mon Sep 17 00:00:00 2001 From: Lukas Podmelle <podmellelukas@gmail.com> Date: Tue, 16 Jan 2024 15:34:56 +0100 Subject: [PATCH 8/9] Fixes --- src/App/App.tsx | 2 +- .../ExpandableTemplate/Signature/Signature.tsx | 17 +++++++++++++---- src/themes/SidebarTheme.tsx | 1 + src/themes/index.ts | 3 ++- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/App/App.tsx b/src/App/App.tsx index c7e28f42f3..02c743d9d9 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -10,7 +10,7 @@ import { generateUserId } from '../utils/generateUserId' export type AppProps = ChatProps & { theme?: Theme; // option to override theme settings from remote config defaultTheme?: Theme; // option to set default template when no remote config found (on default microsite for example) - header?: { textWhenCollapsed?: string; text: string, extraHtml?: string}; + header?: { textWhenCollapsed?: string; text: string, extraHtml?: string }; channel?: { index?: number, id?: string }; autoExpandTimeout?: number; enableScreenshotUpload?: boolean; diff --git a/src/App/templates/ExpandableTemplate/Signature/Signature.tsx b/src/App/templates/ExpandableTemplate/Signature/Signature.tsx index ada5e3e25b..59b4a414ee 100644 --- a/src/App/templates/ExpandableTemplate/Signature/Signature.tsx +++ b/src/App/templates/ExpandableTemplate/Signature/Signature.tsx @@ -12,7 +12,8 @@ export type Props = { signature: SignatureSchema appProps: AppProps botId: string -}; +} + const getLinkQueryString = (botId: string) => `?utm_source=webchat&utm_medium=chatbot&utm_campaign=${botId}` export const Signature: React.StatelessComponent<Props> = ({ @@ -92,7 +93,7 @@ export const Signature: React.StatelessComponent<Props> = ({ </SignatureTemplate> ); } - + if (partnerLogoUrl && mode === 'both') { return ( <SignatureTemplate> @@ -104,10 +105,18 @@ export const Signature: React.StatelessComponent<Props> = ({ } if (partnerLogoUrl && mode === 'partner') { - return <SignatureTemplate>{partnerLink}</SignatureTemplate> + return ( + <SignatureTemplate> + {partnerLink} + </SignatureTemplate> + ) } - return <SignatureTemplate>{feedyouLink}</SignatureTemplate> + return ( + <SignatureTemplate> + {feedyouLink} + </SignatureTemplate> + ) } export type SignatureProps = Props diff --git a/src/themes/SidebarTheme.tsx b/src/themes/SidebarTheme.tsx index 577446a878..441018c7c7 100644 --- a/src/themes/SidebarTheme.tsx +++ b/src/themes/SidebarTheme.tsx @@ -3,6 +3,7 @@ import { Theme } from './index' function getSidebarBackgroundColor(theme: Theme) { return '#e1e1e1' + // TODO make background tint configurable in theme /*const color = theme.mainColor if(color.startsWith("rgb")){ diff --git a/src/themes/index.ts b/src/themes/index.ts index 707762b907..75b1b70146 100644 --- a/src/themes/index.ts +++ b/src/themes/index.ts @@ -38,7 +38,7 @@ export type Theme = { partnerLinkUrl: string, mode: string partnerName: string, - }; + } }; export function getStyleForTheme(theme: Theme, remoteConfig: boolean): string { @@ -57,5 +57,6 @@ export function getStyleForTheme(theme: Theme, remoteConfig: boolean): string { return SidebarThemeNew(theme) } // backward compatibility - knob is new default for remote config, old default is bar + return remoteConfig ? ExpandableKnobTheme(theme) : ExpandableBarTheme(theme) } From a0b5ea9f732d2340c2cf4852a0e82537b9c558bd Mon Sep 17 00:00:00 2001 From: Lukas Podmelle <podmellelukas@gmail.com> Date: Wed, 17 Jan 2024 10:36:26 +0100 Subject: [PATCH 9/9] Rename to v2 --- .../ExpandableTemplate/ExpandableTemplate.tsx | 2 +- src/App/templates/ExpandableTemplate/Header.tsx | 10 +++++----- .../ExpandableTemplate/Signature/Signature.tsx | 4 ++-- ...bleKnobThemeNew.tsx => ExpandableKnobThemeV2.tsx} | 2 +- .../{SidebarThemeNew.tsx => SidebarThemeV2.tsx} | 2 +- src/themes/index.ts | 12 ++++++------ 6 files changed, 16 insertions(+), 16 deletions(-) rename src/themes/{ExpandableKnobThemeNew.tsx => ExpandableKnobThemeV2.tsx} (94%) rename src/themes/{SidebarThemeNew.tsx => SidebarThemeV2.tsx} (95%) diff --git a/src/App/templates/ExpandableTemplate/ExpandableTemplate.tsx b/src/App/templates/ExpandableTemplate/ExpandableTemplate.tsx index 370e605c55..5d54c4e310 100644 --- a/src/App/templates/ExpandableTemplate/ExpandableTemplate.tsx +++ b/src/App/templates/ExpandableTemplate/ExpandableTemplate.tsx @@ -71,7 +71,7 @@ export class ExpandableTemplate extends React.Component<Props, State> { doesTemplateSupportPopupMsg = () => { const templateType = this.props.theme.template && this.props.theme.template.type - return ["expandable-knob", "sidebar", "expandable-knob-new", "sidebar-new"].includes(templateType) + return ["expandable-knob", "sidebar", "expandable-knob-v2", "sidebar-v2"].includes(templateType) } diff --git a/src/App/templates/ExpandableTemplate/Header.tsx b/src/App/templates/ExpandableTemplate/Header.tsx index 2ff00edc41..9f384fdf12 100644 --- a/src/App/templates/ExpandableTemplate/Header.tsx +++ b/src/App/templates/ExpandableTemplate/Header.tsx @@ -32,7 +32,7 @@ export class Header extends React.Component<Props, State> { }; let backgroundColor; - if (template.type !== 'expandable-knob-new' && template.type !== 'sidebar-new') { + if (template.type !== 'expandable-knob-v2' && template.type !== 'sidebar-v2') { backgroundColor = mainColor || '#fb584e'; } @@ -70,12 +70,12 @@ export class Header extends React.Component<Props, State> { return ( <div className="feedbot-header" onClick={isCollapsed && onClick} style={{ backgroundColor }}> <div className="feedbot-header-name"> - {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( + {template.type === 'expandable-knob-v2' || template.type === 'sidebar-v2' ? ( <div className="feedbot-avatar" style={{ backgroundImage: `url("${avatar}")` }}></div> ) : null} <div className="feedbot-name"> <span className="feedbot-title">{title}</span> - {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( + {template.type === 'expandable-knob-v2' || template.type === 'sidebar-v2' ? ( <span className="feedbot-supportive-title">{template.supportiveTitle}</span> ) : null} </div> @@ -86,7 +86,7 @@ export class Header extends React.Component<Props, State> { )} <div className="feedbot-header-actions"> - {(template.type === 'expandable-knob-new' || template.type === 'sidebar-new') && + {(template.type === 'expandable-knob-v2' || template.type === 'sidebar-v2') && (checkFeedbotTestMode() || template.persistentMenu.length > 0) ? ( <div className="feedbot-persistent-menu" onClick={handlePersistentMenuToggle}> <a className="feedbot-persistent-menu-toggle"> @@ -131,7 +131,7 @@ export class Header extends React.Component<Props, State> { className="feedbot-minimize" href="#" > - {template.type === 'expandable-knob-new' || template.type === 'sidebar-new' ? ( + {template.type === 'expandable-knob-v2' || template.type === 'sidebar-v2' ? ( <svg xmlns="http://www.w3.org/2000/svg" width="26" diff --git a/src/App/templates/ExpandableTemplate/Signature/Signature.tsx b/src/App/templates/ExpandableTemplate/Signature/Signature.tsx index 59b4a414ee..91aa70c5fe 100644 --- a/src/App/templates/ExpandableTemplate/Signature/Signature.tsx +++ b/src/App/templates/ExpandableTemplate/Signature/Signature.tsx @@ -63,8 +63,8 @@ export const Signature: React.StatelessComponent<Props> = ({ /* Webchat Redesign Signature */ if ( - template.type === 'expandable-knob-new' || - template.type === 'sidebar-new' + template.type === 'expandable-knob-v2' || + template.type === 'sidebar-v2' ) { if (mode === 'both') { return ( diff --git a/src/themes/ExpandableKnobThemeNew.tsx b/src/themes/ExpandableKnobThemeV2.tsx similarity index 94% rename from src/themes/ExpandableKnobThemeNew.tsx rename to src/themes/ExpandableKnobThemeV2.tsx index 5af8e64f6b..9c5d50af53 100644 --- a/src/themes/ExpandableKnobThemeNew.tsx +++ b/src/themes/ExpandableKnobThemeV2.tsx @@ -1,6 +1,6 @@ import { Theme } from './index'; -export const ExpandableKnobThemeNew = (theme: Theme) => ` +export const ExpandableKnobThemeV2 = (theme: Theme) => ` /* webchat redesign */ .feedbot-wrapper.collapsed .feedbot-header { background-image: url(${ diff --git a/src/themes/SidebarThemeNew.tsx b/src/themes/SidebarThemeV2.tsx similarity index 95% rename from src/themes/SidebarThemeNew.tsx rename to src/themes/SidebarThemeV2.tsx index 09ba9afcd7..b33c50dee3 100644 --- a/src/themes/SidebarThemeNew.tsx +++ b/src/themes/SidebarThemeV2.tsx @@ -1,6 +1,6 @@ import { Theme } from './index'; -export const SidebarThemeNew = (theme: Theme) => ` +export const SidebarThemeV2 = (theme: Theme) => ` /* webchat redesign */ .feedbot-wrapper.collapsed .feedbot-header { background-image: url(${ diff --git a/src/themes/index.ts b/src/themes/index.ts index 75b1b70146..a6c5e5b746 100644 --- a/src/themes/index.ts +++ b/src/themes/index.ts @@ -2,8 +2,8 @@ import { ExpandableBarTheme } from './ExpandableBarTheme' import { FullScreenTheme } from './FullScreenTheme' import { ExpandableKnobTheme } from './ExpandableKnobTheme' import { SidebarTheme } from './SidebarTheme' -import { ExpandableKnobThemeNew } from './ExpandableKnobThemeNew' -import { SidebarThemeNew } from './SidebarThemeNew' +import { ExpandableKnobThemeV2 } from './ExpandableKnobThemeV2' +import { SidebarThemeV2 } from './SidebarThemeV2' export type Theme = { mainColor: string; @@ -51,10 +51,10 @@ export function getStyleForTheme(theme: Theme, remoteConfig: boolean): string { return ExpandableKnobTheme(theme) case 'sidebar': return SidebarTheme(theme) - case 'expandable-knob-new': - return ExpandableKnobThemeNew(theme) - case 'sidebar-new': - return SidebarThemeNew(theme) + case 'expandable-knob-v2': + return ExpandableKnobThemeV2(theme) + case 'sidebar-v2': + return SidebarThemeV2(theme) } // backward compatibility - knob is new default for remote config, old default is bar