diff --git a/app/javascript/packs/sdk.js b/app/javascript/packs/sdk.js index a9cecebbe..8c7c97dee 100755 --- a/app/javascript/packs/sdk.js +++ b/app/javascript/packs/sdk.js @@ -1,5 +1,6 @@ import Cookies from 'js-cookie'; import { IFrameHelper } from '../sdk/IFrameHelper'; +import { getBubbleView } from '../sdk/bubbleHelpers'; const runSDK = ({ baseUrl, websiteToken }) => { const chatwootSettings = window.chatwootSettings || {}; @@ -11,6 +12,7 @@ const runSDK = ({ baseUrl, websiteToken }) => { position: chatwootSettings.position === 'left' ? 'left' : 'right', websiteToken, locale: chatwootSettings.locale, + type: getBubbleView(chatwootSettings.type), toggle() { IFrameHelper.events.toggleBubble(); diff --git a/app/javascript/sdk/DOMHelpers.js b/app/javascript/sdk/DOMHelpers.js index 196a98476..3e3adb572 100644 --- a/app/javascript/sdk/DOMHelpers.js +++ b/app/javascript/sdk/DOMHelpers.js @@ -1,4 +1,4 @@ -import { SDK_CSS } from '../widget/assets/scss/sdk'; +import { SDK_CSS } from './sdk.js'; export const loadCSS = () => { const css = document.createElement('style'); diff --git a/app/javascript/sdk/IFrameHelper.js b/app/javascript/sdk/IFrameHelper.js index f42afcd64..6858f3a40 100644 --- a/app/javascript/sdk/IFrameHelper.js +++ b/app/javascript/sdk/IFrameHelper.js @@ -1,5 +1,5 @@ import Cookies from 'js-cookie'; -import { wootOn, loadCSS, addClass, removeClass } from './DOMHelpers'; +import { wootOn, addClass, loadCSS, removeClass } from './DOMHelpers'; import { body, widgetHolder, @@ -12,6 +12,7 @@ import { createNotificationBubble, onClickChatBubble, onBubbleClick, + setBubbleText, } from './bubbleHelpers'; import { dispatchWindowEvent } from 'shared/helpers/CustomEventHelper'; @@ -32,8 +33,9 @@ export const IFrameHelper = { iframe.id = 'chatwoot_live_chat_widget'; iframe.style.visibility = 'hidden'; - const HolderclassName = `woot-widget-holder woot--hide woot-elements--${window.$chatwoot.position}`; - addClass(widgetHolder, HolderclassName); + + const holderClassName = `woot-widget-holder woot--hide woot-elements--${window.$chatwoot.position}`; + addClass(widgetHolder, holderClassName); widgetHolder.appendChild(iframe); body.appendChild(widgetHolder); IFrameHelper.initPostMessageCommunication(); @@ -69,9 +71,7 @@ export const IFrameHelper = { }; }, initWindowSizeListener: () => { - wootOn(window, 'resize', () => { - IFrameHelper.toggleCloseButton(); - }); + wootOn(window, 'resize', () => IFrameHelper.toggleCloseButton()); }, preventDefaultScroll: () => { widgetHolder.addEventListener('wheel', event => { @@ -100,7 +100,9 @@ export const IFrameHelper = { position: window.$chatwoot.position, hideMessageBubble: window.$chatwoot.hideMessageBubble, }); - IFrameHelper.onLoad(message.config.channelConfig); + IFrameHelper.onLoad({ + widgetColor: message.config.channelConfig.widgetColor, + }); IFrameHelper.setCurrentUrl(); IFrameHelper.toggleCloseButton(); @@ -110,6 +112,10 @@ export const IFrameHelper = { dispatchWindowEvent(EVENT_NAME); }, + setBubbleLabel(message) { + setBubbleText(message.label); + }, + toggleBubble: () => { onBubbleClick(); }, diff --git a/app/javascript/sdk/bubbleHelpers.js b/app/javascript/sdk/bubbleHelpers.js index 7499d337b..a97739436 100644 --- a/app/javascript/sdk/bubbleHelpers.js +++ b/app/javascript/sdk/bubbleHelpers.js @@ -1,5 +1,6 @@ import { addClass, toggleClass, wootOn } from './DOMHelpers'; import { IFrameHelper } from './IFrameHelper'; +import { BUBBLE_DESIGN } from './constants'; export const bubbleImg = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAAwgJEBk0TVheY2R5eo+ut8jb5OXs8fX2+cjRDTIAAADsSURBVHgBldZbkoMgFIThRgQv8SKKgGf/C51UnJqaRI30/9zfe+NQUQ3TvG7bOk9DVeCmshmj/CuOTYnrdBfkUOg0zlOtl9OWVuEk4+QyZ3DIevmSt/ioTvK1VH/s5bY3YdM9SBZ/mUUyWgx+U06ycgp7D8msxSvtc4HXL9BLdj2elSEfhBJAI0QNgJEBI1BEBsQClVBVGDgwYOLAhJkDM1YOrNg4sLFAsLJgZsHEgoEFFQt0JAFGFjQsKAMJ0LFAexKgZYFyJIDxJIBNJEDNAtSJBLCeBDCOBFAPzwFA94ED+zmhwDO9358r8ANtIsMXi7qVAwAAAABJRU5ErkJggg=='; @@ -10,14 +11,34 @@ export const widgetHolder = document.createElement('div'); export const bubbleHolder = document.createElement('div'); export const chatBubble = document.createElement('div'); export const closeBubble = document.createElement('div'); - export const notificationBubble = document.createElement('span'); +export const getBubbleView = type => + BUBBLE_DESIGN.includes(type) ? type : BUBBLE_DESIGN[0]; +export const isExpandedView = type => getBubbleView(type) === BUBBLE_DESIGN[1]; + +export const setBubbleText = bubbleText => { + if (isExpandedView(window.$chatwoot.type)) { + const textNode = document.getElementById('woot-widget--expanded__text'); + textNode.innerHTML = bubbleText; + } +}; + export const createBubbleIcon = ({ className, src, target }) => { - target.className = `${className} woot-elements--${window.$chatwoot.position}`; + let bubbleClassName = `${className} woot-elements--${window.$chatwoot.position}`; const bubbleIcon = document.createElement('img'); bubbleIcon.src = src; target.appendChild(bubbleIcon); + + if (isExpandedView(window.$chatwoot.type)) { + const textNode = document.createElement('div'); + textNode.id = 'woot-widget--expanded__text'; + textNode.innerHTML = ''; + target.appendChild(textNode); + bubbleClassName += ' woot-widget--expanded'; + } + + target.className = bubbleClassName; return target; }; diff --git a/app/javascript/sdk/constants.js b/app/javascript/sdk/constants.js new file mode 100644 index 000000000..f35378a47 --- /dev/null +++ b/app/javascript/sdk/constants.js @@ -0,0 +1 @@ +export const BUBBLE_DESIGN = ['standard', 'expanded_bubble']; diff --git a/app/javascript/widget/assets/scss/sdk.js b/app/javascript/sdk/sdk.js similarity index 63% rename from app/javascript/widget/assets/scss/sdk.js rename to app/javascript/sdk/sdk.js index 558812c57..e5ea385e5 100644 --- a/app/javascript/widget/assets/scss/sdk.js +++ b/app/javascript/sdk/sdk.js @@ -1,50 +1,60 @@ -export const SDK_CSS = ` .woot-widget-holder { - z-index: 2147483000 !important; - position: fixed !important; - -moz-box-shadow: 0 5px 40px rgba(0, 0, 0, .16) !important; - -o-box-shadow: 0 5px 40px rgba(0, 0, 0, .16) !important; - -webkit-box-shadow: 0 5px 40px rgba(0, 0, 0, .16) !important; +export const SDK_CSS = `.woot-widget-holder { box-shadow: 0 5px 40px rgba(0, 0, 0, .16) !important; - overflow: hidden !important; opacity: 1; - transition-property: opacity, bottom; + overflow: hidden !important; + position: fixed !important; transition-duration: 0.5s, 0.5s; -} - -.woot-widget-holder.has-unread-view { - box-shadow: none !important; - -moz-box-shadow: none !important; - -o-box-shadow: none !important; - -webkit-box-shadow: none !important; - -o-border-radius: 0 !important; - -moz-border-radius: 0 !important; - -webkit-border-radius: 0 !important; - border-radius: 0 !important; - bottom: 94px; + transition-property: opacity, bottom; + z-index: 2147483000 !important; } .woot-widget-holder iframe { - width: 100% !important; - height: 100% !important; border: 0; + height: 100% !important; + width: 100% !important; +} + +.woot-widget-holder.has-unread-view { + border-radius: 0 !important; + bottom: 94px; + box-shadow: none !important; } .woot-widget-bubble { - z-index: 2147483000 !important; - -moz-box-shadow: 0 8px 24px rgba(0, 0, 0, .16) !important; - -o-box-shadow: 0 8px 24px rgba(0, 0, 0, .16) !important; - -webkit-box-shadow: 0 8px 24px rgba(0, 0, 0, .16) !important; - box-shadow: 0 8px 24px rgba(0, 0, 0, .16) !important; - -o-border-radius: 100px !important; - -moz-border-radius: 100px !important; - -webkit-border-radius: 100px !important; - border-radius: 100px !important; background: #1f93ff; - position: fixed; - cursor: pointer; + border-radius: 100px !important; bottom: 20px; - width: 64px !important; + box-shadow: 0 8px 24px rgba(0, 0, 0, .16) !important; + cursor: pointer; height: 64px !important; + position: fixed; + width: 64px !important; + z-index: 2147483000 !important; +} + +.woot-widget-bubble.woot-widget--expanded { + bottom: 24px; + display: flex; + height: 48px !important; + width: auto !important; +} + +.woot-widget-bubble.woot-widget--expanded div { + align-items: center; + color: #fff; + display: flex; + font-family: system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, Arial, sans-serif; + font-size: 16px; + font-weight: 500; + justify-content: center; + padding-right: 20px; + width: auto !important; +} + +.woot-widget-bubble.woot-widget--expanded img { + height: 20px; + margin: 14px 8px 14px 16px; + width: 20px; } .woot-widget-bubble.woot-elements--left { @@ -55,87 +65,78 @@ export const SDK_CSS = ` .woot-widget-holder { right: 20px; } -@media only screen and (min-width: 667px) { - .woot-widget-holder.woot-elements--left { - left: 20px; - } - - .woot-widget-holder.woot-elements--right { - right: 20px; - } -} - .woot-widget-bubble:hover { background: #1f93ff; - -moz-box-shadow: 0 8px 32px rgba(0, 0, 0, .4) !important; - -o-box-shadow: 0 8px 32px rgba(0, 0, 0, .4) !important; - -webkit-box-shadow: 0 8px 32px rgba(0, 0, 0, .4) !important; box-shadow: 0 8px 32px rgba(0, 0, 0, .4) !important; } .woot-widget-bubble img { - width: 24px; height: 24px; margin: 20px; + width: 24px; +} + +@media only screen and (min-width: 667px) { + .woot-widget-holder.woot-elements--left { + left: 20px; + } + .woot-widget-holder.woot-elements--right { + right: 20px; + } } .woot--close:hover { opacity: 1; } -.woot--close:before, .woot--close:after { - position: absolute; - left: 32px; - top: 20px; +.woot--close::before, .woot--close::after { + background-color: #fff; content: ' '; height: 24px; + left: 32px; + position: absolute; + top: 20px; width: 2px; - background-color: white; } -.woot--close:before { +.woot--close::before { transform: rotate(45deg); } -.woot--close:after { +.woot--close::after { transform: rotate(-45deg); } - .woot--hide { + bottom: -20000px; + opacity: 0; visibility: hidden !important; z-index: -1 !important; - opacity: 0; - bottom: -20000px; } @media only screen and (max-width: 667px) { .woot-widget-holder { - top: 0; - right: 0; height: 100%; + right: 0; + top: 0; width: 100%; - } - + } .woot-widget-bubble.woot--close { + bottom: 60px; + opacity: 0; visibility: hidden !important; z-index: -1 !important; - opacity: 0; - bottom: 60px; - } + } } @media only screen and (min-width: 667px) { .woot-widget-holder { + border-radius: 16px !important; bottom: 104px; height: calc(85% - 64px - 20px); - width: 400px !important; - min-height: 250px !important; max-height: 590px !important; - -o-border-radius: 16px !important; - -moz-border-radius: 16px !important; - -webkit-border-radius: 16px !important; - border-radius: 16px !important; - } + min-height: 250px !important; + width: 400px !important; + } } `; diff --git a/app/javascript/sdk/specs/bubbleHelpers.spec.js b/app/javascript/sdk/specs/bubbleHelpers.spec.js new file mode 100644 index 000000000..e67cc4c96 --- /dev/null +++ b/app/javascript/sdk/specs/bubbleHelpers.spec.js @@ -0,0 +1,17 @@ +import { getBubbleView, isExpandedView } from '../bubbleHelpers'; + +describe('#getBubbleView', () => { + it('returns correct view', () => { + expect(getBubbleView('')).toEqual('standard'); + expect(getBubbleView('standard')).toEqual('standard'); + expect(getBubbleView('expanded_bubble')).toEqual('expanded_bubble'); + }); +}); + +describe('#isExpandedView', () => { + it('returns true if it is expanded view', () => { + expect(isExpandedView('')).toEqual(false); + expect(isExpandedView('standard')).toEqual(false); + expect(isExpandedView('expanded_bubble')).toEqual(true); + }); +}); diff --git a/app/javascript/widget/App.vue b/app/javascript/widget/App.vue index bdbd1388c..45b45fb48 100755 --- a/app/javascript/widget/App.vue +++ b/app/javascript/widget/App.vue @@ -77,12 +77,13 @@ export default { const message = JSON.parse(e.data.replace(wootPrefix, '')); if (message.event === 'config-set') { + this.setLocale(message.locale); + this.setBubbleLabel(); + this.setPosition(message.position); this.fetchOldConversations().then(() => { this.setUnreadView(); }); this.fetchAvailableAgents(websiteToken); - this.setLocale(message.locale); - this.setPosition(message.position); this.setHideMessageBubble(message.hideMessageBubble); } else if (message.event === 'widget-visible') { this.scrollConversationToBottom(); @@ -100,6 +101,7 @@ export default { this.$store.dispatch('contacts/update', message); } else if (message.event === 'set-locale') { this.setLocale(message.locale); + this.setBubbleLabel(); } else if (message.event === 'set-unread-view') { this.showUnreadView = true; } else if (message.event === 'unset-unread-view') { @@ -118,6 +120,12 @@ export default { const container = this.$el.querySelector('.conversation-wrap'); container.scrollTop = container.scrollHeight; }, + setBubbleLabel() { + IFrameHelper.sendMessage({ + event: 'setBubbleLabel', + label: this.$t('BUBBLE.LABEL'), + }); + }, setLocale(locale) { const { enabledLanguages } = window.chatwootWebChannel; if (enabledLanguages.some(lang => lang.iso_639_1_code === locale)) { diff --git a/app/javascript/widget/i18n/locale/en.json b/app/javascript/widget/i18n/locale/en.json index 011adeae1..45ffcc28f 100644 --- a/app/javascript/widget/i18n/locale/en.json +++ b/app/javascript/widget/i18n/locale/en.json @@ -18,6 +18,9 @@ "VIEW_MESSAGES_BUTTON": "See new messages", "CLOSE_MESSAGES_BUTTON": "Close" }, + "BUBBLE": { + "LABEL": "Chat with us" + }, "POWERED_BY": "Powered by Chatwoot", "EMAIL_PLACEHOLDER": "Please enter your email", "CHAT_PLACEHOLDER": "Type your message" diff --git a/app/javascript/widget/i18n/locale/fr.json b/app/javascript/widget/i18n/locale/fr.json index ac6663b01..a17322b97 100644 --- a/app/javascript/widget/i18n/locale/fr.json +++ b/app/javascript/widget/i18n/locale/fr.json @@ -14,6 +14,9 @@ "OTHERS_ARE_AVAILABLE": "d'autres sont disponibles", "AND": "et" }, + "BUBBLE": { + "LABEL": "Discute avec nous" + }, "POWERED_BY": "Propulsé par Chatwoot", "EMAIL_PLACEHOLDER": "Veuillez saisir votre adresse de courriel", "CHAT_PLACEHOLDER": "Tapez votre message" diff --git a/app/javascript/widget/i18n/locale/ml.json b/app/javascript/widget/i18n/locale/ml.json index 7b6950e7d..bb0247949 100644 --- a/app/javascript/widget/i18n/locale/ml.json +++ b/app/javascript/widget/i18n/locale/ml.json @@ -14,6 +14,9 @@ "OTHERS_ARE_AVAILABLE": "others are available", "AND": "and" }, + "BUBBLE": { + "LABEL": "ഞങ്ങളുമായി ചാറ്റുചെയ്യുക" + }, "POWERED_BY": "Powered by Chatwoot", "EMAIL_PLACEHOLDER": "ദയവായി നിങ്ങളുടെ ഇമെയിൽ നൽകുക", "CHAT_PLACEHOLDER": "Type your message" diff --git a/app/javascript/widget/i18n/locale/nl.json b/app/javascript/widget/i18n/locale/nl.json index 92bd550b0..f96a3eed9 100644 --- a/app/javascript/widget/i18n/locale/nl.json +++ b/app/javascript/widget/i18n/locale/nl.json @@ -14,6 +14,9 @@ "OTHERS_ARE_AVAILABLE": "others are available", "AND": "and" }, + "BUBBLE": { + "LABEL": "Chat met ons" + }, "POWERED_BY": "Mogelijk gemaakt door Chatwoot", "EMAIL_PLACEHOLDER": "Voer uw e-mailadres in", "CHAT_PLACEHOLDER": "Typ uw bericht" diff --git a/app/views/widget_tests/index.html.erb b/app/views/widget_tests/index.html.erb index 241047e16..6be67a713 100644 --- a/app/views/widget_tests/index.html.erb +++ b/app/views/widget_tests/index.html.erb @@ -6,6 +6,7 @@ window.chatwootSettings = { hideMessageBubble: false, position: 'left', locale: 'en', + type: 'expanded_bubble', }; (function(d,t) { diff --git a/docs/channels/images/sdk/expanded-bubble.gif b/docs/channels/images/sdk/expanded-bubble.gif new file mode 100644 index 000000000..4db6fdf3c Binary files /dev/null and b/docs/channels/images/sdk/expanded-bubble.gif differ diff --git a/docs/channels/images/sdk/standard-bubble.gif b/docs/channels/images/sdk/standard-bubble.gif new file mode 100644 index 000000000..e0541dff7 Binary files /dev/null and b/docs/channels/images/sdk/standard-bubble.gif differ diff --git a/docs/channels/website-sdk.md b/docs/channels/website-sdk.md index f29738c37..b6d0497d2 100644 --- a/docs/channels/website-sdk.md +++ b/docs/channels/website-sdk.md @@ -23,9 +23,20 @@ window.chatwootSettings = { hideMessageBubble: false, position: 'left', // This can be left or right locale: 'en', // Language to be set + type: 'standard', // [standard, expanded_bubble] }; ``` +Chatwoot support 2 designs for for the widget + +1. Standard (default) + +![Standard-bubble](./images/sdk/standard-bubble.gif) + +2. Expanded bubble + +![Expanded-bubble](./images/sdk/expanded-bubble.gif) + ### To trigger widget without displaying bubble ```js