UNPKG

@supportai.it/chat-widget

Version:

Chat widget web component for supportAI.it

213 lines (194 loc) 17.3 kB
(function(o,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],n):(o=typeof globalThis<"u"?globalThis:o||self,n(o.ChatWidgetVue={},o.Vue))})(this,function(o,n){"use strict";const l='<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 535.62 369.81"><defs><linearGradient id="b" x1="218.11" y1="-120.74" x2="475.29" y2="-123.48" gradientTransform="translate(0 145.8) scale(1 -1)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ededed"/><stop offset="1" stop-color="#fafafa"/></linearGradient></defs><path d="M473.62,329.58c-.42,4.74-1.75,13.63-8.33,20.69-6.33,6.82-15.33,8.39-24.9,10.31-3.91.75-10.66,1.83-23.57,1.66-7.5-.08-18.99-.33-34.48-3.16-33.06-6.07-61.55-20.11-61.55-20.11-48.06-20.11-81.79-48.87-102.69-70.48,4.25-4.16,8.58-8.31,12.83-12.38.5-.42.92-.91,1.5-1.41.67-.67,1.42-1.33,2.17-2.08,14.41-13.63,28.73-26.51,42.98-36.9,16.24-10.89,32.4-21.78,48.56-32.66,4.33-2.83,8.66-5.9,13.08-8.81,3.25,4.74,6.75,9.22,10.66,13.55,32.65,35.9,85.45,48.2,103.61,50.78,2.58.42,9.41,1.25,14.58,6.4,6.41,6.32,6.83,15.71,6.91,21.19.67,26.26-.75,52.61-1.33,63.41Z" fill="url(#b)"/><path d="M525.99,19.32c-5.16,13.8-25.99,76.05-45.64,105.63-1.5,2.33-4.25,6.23-8.75,10.47-2.17,2.08-6.83,6.57-13.58,9.56-11.66,5.24-22.57,3.74-25.57,3.24-18.82-2.99-29.9-17.04-35.98-24.68-16.49-20.69-23.32-48.87-16.74-54.27,2.66-2.16,8.08-2.99,20.9,3.66,1.33.75,2.58,1.33,4,2.16,5.41,2.91,9.58,5.15,13.66,7.56.92.5,2.08,1.16,3.25,1.83.5-.33.92-.66,1.33-1,15.33-11.22,30.48-22.52,45.64-33.74,9.24-6.82,18.41-13.63,27.57-20.45,2.5-1.83,14.33-11.22,19.49-15.04,9.75-7.15,11.66-7.31,12.24-7.06h.08c2.17,1.41-.83,9.14-1.92,12.22v-.08Z" fill="#fff"/><path d="M339.2,174.25c-4.41,2.91-8.75,5.98-13.08,8.81-16.16,10.89-32.32,21.78-48.56,32.66-14.24,10.39-28.57,23.27-42.98,36.9-.75.75-1.5,1.41-2.17,2.08-.58.5-1,1-1.5,1.41-4.25,4.07-8.58,8.23-12.83,12.38-5.75,5.57-13.08,12.55-20.74,19.61-14.16,13.21-31.15,28.92-49.89,41.39-9.83,6.48-32.73,21.36-66.05,28.76-29.65,6.65-55.72,4.65-74.13,1.33,0-53.27-.17-106.55-.25-159.82.75.08,1.5.08,2.42.08,38.9,1.75,67.21-19.78,93.28-42.14,4.33-3.82,8.66-7.56,12.91-11.22,21.4-18.45,41.06-38.31,61.47-57.6,18.07-17.04,36.65-39.14,64.05-43.63.42-.08.75-.17,1.17-.08,1.58-.33,3.25-.5,4.83-.58,12.41-1.16,26.15.5,36.81,5.82,10.66,5.32,16.41,15.87,20.24,27.68,2.42,7.48,4,15.54,5.66,22.94,1.83,8.06,3.17,13.8,3.33,14.54,2.08,9.31,8.16,26.01,25.99,58.59v.08Z" fill="#fff"/></svg>',g=typeof window<"u"?window.HTMLElement:class{};let u=class extends g{constructor(){super(),this._isOpen=!1,this._isMobile=!1,this._fullChatUrl="",this._baseUrl="https://www.supportai.it",this._isFirstLoad=!0,this._firstChatOpen=!1,this._messageBubbleVisible=!0,this._cookieName="chat-widget-opened",this._cookieExpDays=7,this._chatModal=null,this._chatButton=null,this._messageContainer=null,this._connected=!1,this._lastRenderedState={isOpen:!1,isFirstLoad:!0,firstChatOpen:!1,isMobile:!1,chatUrl:""},this._config={chatId:"",apiKey:"",buttonColor:"#582975",buttonHoverColor:"#7b2ba6",buttonSize:"64px",messageBubble:"Ciao, sono l'assistente virtuale di <b>supportAI</b>, se hai bisogno di assistenza apri la chat!",chatAlign:"right",svgIcon:l,chatWidth:"400px",chatHeight:"650px",style:""},this.attachShadow({mode:"open"}),this._firstChatOpen=this.getCookie(this._cookieName)==="true"}setCookie(e,s,t){const a=new Date;a.setTime(a.getTime()+t*24*60*60*1e3);const h=`expires=${a.toUTCString()}`;document.cookie=`${e}=${s};${h};path=/`}getCookie(e){if(console.log(`Getting cookie: ${e}`),typeof document>"u")return"";const s=document.cookie.split(";");for(let t=0;t<s.length;t++){const a=s[t].trim();if(a.startsWith(e+"="))return a.substring(e.length+1)}return""}updateChatUrl(){const e=new URL(`${this._baseUrl}/chat/${this._config.chatId}`);e.searchParams.set("apiKey",this._config.apiKey),e.searchParams.set("mobile",this._isMobile.toString()),this._fullChatUrl=e.toString()}static get observedAttributes(){return["base-url","chat-id","api-key","button-color","button-hover-color","button-size","message-bubble","chat-align","svg-icon","chat-width","chat-height","style"]}attributeChangedCallback(e,s,t){if(s!==t){switch(e){case"base-url":this._baseUrl=t,this.updateChatUrl();break;case"chat-id":this._config.chatId=t,this.updateChatUrl();break;case"api-key":this._config.apiKey=t,this.updateChatUrl();break;case"button-color":this._config.buttonColor=t;break;case"button-hover-color":this._config.buttonHoverColor=t;break;case"button-size":this._config.buttonSize=t;break;case"message-bubble":this._config.messageBubble=t==="false"?!1:t||"Ciao, sono l'assistente virtuale di supportAI, se hai bisogno di assistenza apri la chat!";break;case"chat-align":this._config.chatAlign=t==="left"?"left":"right";break;case"svg-icon":this._config.svgIcon=t||l;break;case"chat-width":this._config.chatWidth=t;break;case"chat-height":this._config.chatHeight=t;break;case"style":this._config.style=t;break}this._connected&&(this._chatModal=null,this.render())}}connectedCallback(){this._connected=!0,this._isMobile=window.innerWidth<=768,this.updateChatUrl(),this.render(),this.setupEventListeners()}disconnectedCallback(){this.removeEventListeners()}setupEventListeners(){this.handleResize=this.handleResize.bind(this),this.updateContext=this.updateContext.bind(this),this.handlePostMessage=this.handlePostMessage.bind(this),window.addEventListener("resize",this.handleResize),window.addEventListener("chat-widget/updateContext",this.updateContext),window.addEventListener("message",this.handlePostMessage)}removeEventListeners(){window.removeEventListener("resize",this.handleResize),window.removeEventListener("chat-widget/updateContext",this.updateContext),window.removeEventListener("message",this.handlePostMessage)}handleResize(){const e=this._isMobile;this._isMobile=window.innerWidth<=768,e!==this._isMobile&&(this.updateChatUrl(),this._chatModal&&(this._isMobile?this._chatModal.classList.add("mobile-fullscreen"):this._chatModal.classList.remove("mobile-fullscreen")),this.updateMessageBubble(),this._lastRenderedState.isMobile=this._isMobile,this._lastRenderedState.chatUrl=this._fullChatUrl)}toggleChat(){this._firstChatOpen||(this._firstChatOpen=!0,this.setBubbleCookie()),this._isOpen=!this._isOpen,this.updateChatState()}closeBubble(){this.setBubbleCookie(),this._messageBubbleVisible=!1,this.updateMessageBubble()}setBubbleCookie(){this.setCookie(this._cookieName,"true",this._cookieExpDays)}updateChatState(){this.shadowRoot&&(this._chatModal&&(this._chatModal.classList.toggle("chat-hidden",!this._isOpen),this._isMobile?this._chatModal.classList.add("mobile-fullscreen"):this._chatModal.classList.remove("mobile-fullscreen")),this._chatButton&&(this._chatButton.innerHTML=this._isOpen?'<svg viewBox="0 0 24 24" fill="white"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>':this._config.svgIcon,this._chatButton.setAttribute("aria-label",this._isOpen?"Close Chat":"Open Chat")),this.updateMessageBubble())}updateMessageBubble(){if(!this._messageContainer)return;if(!(this._config.messageBubble&&typeof this._config.messageBubble=="string"&&!this._isMobile&&!this._isOpen&&!this._firstChatOpen&&this._messageBubbleVisible)){this._messageContainer.innerHTML="";return}if(this._isFirstLoad)this._messageContainer.innerHTML=` <div class="typing-indicator"> <div class="typing-dot"></div> <div class="typing-dot"></div> <div class="typing-dot"></div> </div> `,setTimeout(()=>{this._isFirstLoad=!1,this.updateMessageBubble()},2500);else{this._messageContainer.innerHTML=` <div class="message-bubble"> <button class="bubble-close" aria-label="Close message"> <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"> <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/> </svg> </button> <div class="bubble-content">${this._config.messageBubble}</div> </div> `;const s=this._messageContainer.querySelector(".bubble-close");s&&s.addEventListener("click",t=>{t.stopPropagation(),this.closeBubble()})}}updateContext(e){const s=this.shadowRoot?.getElementById("chat-iframe");s&&s.contentWindow?.postMessage({type:"updateContext",context:e.detail,apiKey:this._config.apiKey},new URL(this._baseUrl).origin)}handlePostMessage(e){const s=new URL(this._baseUrl).origin;e.origin===s&&e.data?.namespace==="chat-widget/close"&&this.closeChat()}closeChat(){this._isOpen&&(this._isOpen=!1,this.updateChatState())}render(){const e=!this._chatModal||!this._chatButton||!this._messageContainer||this._lastRenderedState.isMobile!==this._isMobile||this._lastRenderedState.chatUrl!==this._fullChatUrl;if(!e&&this._lastRenderedState.isOpen!==this._isOpen){this.updateChatState(),this._lastRenderedState.isOpen=this._isOpen;return}if(!e&&(this._lastRenderedState.isFirstLoad!==this._isFirstLoad||this._lastRenderedState.firstChatOpen!==this._firstChatOpen)){this.updateMessageBubble(),this._lastRenderedState.isFirstLoad=this._isFirstLoad,this._lastRenderedState.firstChatOpen=this._firstChatOpen;return}if(!e)return;const s=` <style> .chat-widget { position: fixed; bottom: 20px; ${this._config.chatAlign}: 20px; z-index: 9999; font-family: Arial, sans-serif; } .chat-button-container { position: relative; display: flex; flex-direction: column; align-items: ${this._config.chatAlign==="right"?"flex-end":"flex-start"}; } .message-bubble, .typing-indicator { background-color: white; border: 1px solid #e0e0e0; border-radius: ${this._config.chatAlign==="right"?"15px 15px 0 15px":"15px 15px 15px 0"}; padding: 10px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); margin-${this._config.chatAlign}: 40px; text-align: left; } .message-bubble { max-width: 250px; font-size: 14px; line-height: 1.4; padding-right: 20px; margin-right: 5px; } .bubble-close { position: absolute; top: 5px; right: 8px; background: none; border: none; cursor: pointer; padding: 2px; color: #666; border-radius: 50%; display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; transition: background-color 0.2s ease, color 0.2s ease; } .bubble-close:hover { background-color: #f0f0f0; color: #333; } .typing-indicator { display: flex; align-items: center; justify-content: center; } .typing-dot { background-color: #888; border-radius: 50%; width: 8px; height: 8px; margin: 0 4px; animation: typing 1.4s infinite ease-in-out; } .typing-dot:nth-child(1) { animation-delay: -0.32s; } .typing-dot:nth-child(2) { animation-delay: -0.16s; } @keyframes typing { 0%, 80%, 100% { opacity: 0.5; transform: scale(0.8); } 40% { opacity: 1; transform: scale(1); } } .chat-button { border: none; border-radius: 50%; cursor: pointer; margin-top: 5px; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); transition: background-color 0.3s ease; background-color: ${this._config.buttonColor}; width: ${this._config.buttonSize}; height: ${this._config.buttonSize}; } .chat-button svg { width: 75%; height: 75%; } .chat-button:hover { background-color: ${this._config.buttonHoverColor}; } .chat-modal { position: fixed; bottom: 100px; ${this._config.chatAlign}: 20px; width: ${this._config.chatWidth}; height: ${this._config.chatHeight}; background: white; border-radius: 10px; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); overflow: hidden; z-index: 10000; } .chat-modal.mobile-fullscreen { position: fixed; top: 0; left: 0; width: 100%; height: 100%; bottom: auto; right: auto; border-radius: 0; } .close-button { background: none; border: none; color: #333; font-size: 1.2rem; cursor: pointer; } #chat-iframe { width: 100%; height: 100%; border: none; } .chat-hidden { display: none; } </style> `,a=` <div class="chat-button-container"> <div class="message-container">${this._config.messageBubble&&typeof this._config.messageBubble=="string"&&!this._isMobile&&!this._isOpen&&!this._firstChatOpen&&this._messageBubbleVisible?this._isFirstLoad?` <div class="typing-indicator"> <div class="typing-dot"></div> <div class="typing-dot"></div> <div class="typing-dot"></div> </div> `:` <div class="message-bubble"> <button class="bubble-close" aria-label="Close message"> <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"> <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/> </svg> </button> <div class="bubble-content">${this._config.messageBubble}</div> </div> `:""}</div> <button class="chat-button" aria-label="${this._isOpen?"Close Chat":"Open Chat"}"> ${this._isOpen?'<svg viewBox="0 0 24 24" fill="white"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>':this._config.svgIcon} </button> </div> `,h=` <div class="chat-modal ${this._isMobile?"mobile-fullscreen":""} ${this._isOpen?"":"chat-hidden"}"> <iframe id="chat-iframe" src="${this._fullChatUrl}" frameborder="0" allow="microphone" > </iframe> </div> `;if(this.shadowRoot){this.shadowRoot.innerHTML=` ${s} <div class="chat-widget" style="${this._config.style}"> ${a} ${h} </div> `,this._chatButton=this.shadowRoot.querySelector(".chat-button"),this._chatModal=this.shadowRoot.querySelector(".chat-modal"),this._messageContainer=this.shadowRoot.querySelector(".message-container"),this._chatButton&&this._chatButton.addEventListener("click",()=>this.toggleChat());const d=this.shadowRoot.querySelector(".close-button");d&&d.addEventListener("click",()=>this.toggleChat());const c=this.shadowRoot.querySelector(".bubble-close");c&&c.addEventListener("click",b=>{b.stopPropagation(),this.closeBubble()}),this._lastRenderedState={isOpen:this._isOpen,isFirstLoad:this._isFirstLoad,firstChatOpen:this._firstChatOpen,isMobile:this._isMobile,chatUrl:this._fullChatUrl},this._isFirstLoad&&!this._isOpen&&!this._isMobile&&this.shadowRoot.querySelector(".typing-indicator")&&setTimeout(()=>{this._isFirstLoad=!1,this.updateMessageBubble(),this._lastRenderedState.isFirstLoad=!1},2500)}}};typeof window<"u"&&window.customElements&&customElements.define("chat-widget",u);const r=n.defineComponent({name:"ChatWidget",props:{chatId:{type:String,required:!0},apiKey:{type:String,required:!0},buttonColor:{type:String,default:"#582975"},buttonHoverColor:{type:String,default:"#7b2ba6"},buttonSize:{type:String,default:"64px"},messageBubble:{type:[String,Boolean],default:"Ciao, sono l'assistente virtuale di supportAI, se hai bisogno di assistenza apri la chat!"},chatAlign:{type:String,default:"right",validator:i=>["left","right"].includes(i)},svgIcon:{type:String},chatWidth:{type:String,default:"400px"},chatHeight:{type:String,default:"600px"},baseUrl:{type:String}},setup(i,{expose:e}){const s=n.ref(null);return e({$el:s}),()=>{const t={"chat-id":i.chatId,"api-key":i.apiKey};return i.buttonColor&&(t["button-color"]=i.buttonColor),i.buttonHoverColor&&(t["button-hover-color"]=i.buttonHoverColor),i.buttonSize&&(t["button-size"]=i.buttonSize),i.messageBubble!==void 0&&(t["message-bubble"]=i.messageBubble.toString()),i.chatAlign&&(t["chat-align"]=i.chatAlign),i.svgIcon&&(t["svg-icon"]=i.svgIcon),i.chatWidth&&(t["chat-width"]=i.chatWidth),i.chatHeight&&(t["chat-height"]=i.chatHeight),i.baseUrl&&(t["base-url"]=i.baseUrl),n.h("chat-widget",{...t,ref:s})}}}),f=()=>({updateContext:e=>{window.dispatchEvent(new CustomEvent("chat-widget/updateContext",{detail:e}))}});o.ChatWidget=r,o.default=r,o.useChatContext=f,Object.defineProperties(o,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});