1class AIChatButton extends HTMLElement { 2 #root = null; 3 #dialog = null; 4 5 6 constructor() { 7 super(); 8 this.#root = this.attachShadow({mode: 'open'}); 9 this.#root.innerHTML = ` 10 <button> 11 <svg viewBox="0 0 24 24"><path d="M12,3C17.5,3 22,6.58 22,11C22,15.42 17.5,19 12,19C10.76,19 9.57,18.82 8.47,18.5C5.55,21 2,21 2,21C4.33,18.67 4.7,17.1 4.75,16.5C3.05,15.07 2,13.13 2,11C2,6.58 6.5,3 12,3Z" /></svg> 12 </button> 13 <dialog> 14 <div> 15 <header> 16 <h1>AI Chat</h1> 17 <button> 18 <svg viewBox="0 0 24 24"><path d="M13.46,12L19,17.54V19H17.54L12,13.46L6.46,19H5V17.54L10.54,12L5,6.46V5H6.46L12,10.54L17.54,5H19V6.46L13.46,12Z" /></svg> 19 </button> 20 </header> 21 <main> 22 <slot></slot> 23 </main> 24 </div> 25 </dialog> 26 `; 27 28 this.#root.appendChild(this.getStyle()); 29 this.#dialog = this.#root.querySelector('dialog'); 30 31 const buttons = this.#root.querySelectorAll('button'); 32 buttons.forEach(function (button) { 33 button.addEventListener('click', this.toggleDialog.bind(this)) 34 }.bind(this)); 35 } 36 37 /** 38 * Called when the DOM has been connected 39 * 40 * We initialize the attribute based states here 41 */ 42 connectedCallback() { 43 this.#dialog.querySelector('header h1').textContent = this.getAttribute('title') || 'AI Chat'; 44 } 45 46 /** 47 * Define the web component's internal styles 48 * 49 * @returns {HTMLStyleElement} 50 */ 51 getStyle() { 52 const style = document.createElement('style'); 53 style.textContent = ` 54 :host { 55 --color-chat-icon: #4881bf; 56 } 57 button { 58 background: none; 59 border: none; 60 cursor: pointer; 61 } 62 :host > button svg { 63 fill: var(--color-chat-icon); 64 } 65 svg { 66 width: 2em; 67 height: 2em; 68 69 } 70 dialog { 71 width: 500px; 72 max-width: 90vw; 73 height: 800px; 74 max-height: 90vh; 75 76 position: fixed; 77 top: 1em; 78 right: 1em; 79 left: auto; 80 81 padding: 0.25em; 82 83 box-shadow: 0 4px 5px rgb(0 0 0 / 30%); 84 border-radius: 8px; 85 border: 1px solid #fff; 86 } 87 dialog > div { 88 display: flex; 89 flex-direction: column; 90 height: 100%; 91 } 92 dialog header { 93 display: flex; 94 justify-content: space-between; 95 align-items: flex-start; 96 } 97 dialog main { 98 overflow: auto; 99 flex-grow: 1; 100 } 101 `; 102 return style; 103 } 104 105 toggleDialog() { 106 if (this.#dialog.open) { 107 this.#dialog.close(); 108 } else { 109 this.#dialog.show(); 110 } 111 } 112} 113 114window.customElements.define('aichat-button', AIChatButton); 115