xref: /plugin/aichat/script/AIChatButton.js (revision 689088446f64ff8d9dfdae9ae0666b45de449da7)
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 class="toggle start">
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                        <button class="fs" title="Fullscreen">
17                            <svg viewBox="0 0 24 24"><path d="M12 5.5L10 8H14L12 5.5M18 10V14L20.5 12L18 10M6 10L3.5 12L6 14V10M14 16H10L12 18.5L14 16M21 3H3C1.9 3 1 3.9 1 5V19C1 20.1 1.9 21 3 21H21C22.1 21 23 20.1 23 19V5C23 3.9 22.1 3 21 3M21 19H3V5H21V19Z" /></svg>
18                        </button>
19                        <h1>AI Chat</h1>
20                        <button class="toggle" title="Close">
21                            <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>
22                        </button>
23                    </header>
24                    <main>
25                        <slot></slot>
26                    </main>
27                </div>
28            </dialog>
29        `;
30
31        this.#root.appendChild(this.getStyle());
32        this.#dialog = this.#root.querySelector('dialog');
33
34        const toggleButtons = this.#root.querySelectorAll('button.toggle');
35        toggleButtons.forEach(function (button) {
36            button.addEventListener('click', this.toggleDialog.bind(this))
37        }.bind(this));
38
39        this.#dialog.querySelector('button.fs').addEventListener('click', function() {
40            this.#dialog.classList.toggle('fullscreen');
41        }.bind(this));
42    }
43
44    /**
45     * Called when the DOM has been connected
46     *
47     * We initialize the attribute based states here
48     */
49    connectedCallback() {
50        this.#root.querySelector('button.start').title = this.getAttribute('label') || 'AI Chat';
51        this.#dialog.querySelector('header h1').textContent = this.getAttribute('label') || 'AI Chat';
52    }
53
54    /**
55     * Define the web component's internal styles
56     *
57     * @returns {HTMLStyleElement}
58     */
59    getStyle() {
60        const style = document.createElement('style');
61        style.textContent = `
62            :host {
63                --color-chat-icon: #4881bf;
64                --icon-size: 2em;
65            }
66            button {
67                background: none;
68                border: none;
69                cursor: pointer;
70            }
71            :host > button svg {
72                fill: var(--color-chat-icon);
73            }
74            svg {
75                width: 2em;
76                height: 2em;
77            }
78            button.start svg {
79                width: var(--icon-size);
80                height: var(--icon-size);
81            }
82            dialog {
83                width: 500px;
84                max-width: 90vw;
85                height: 800px;
86                max-height: 90vh;
87
88                position: fixed;
89                top: 1em;
90                right: 1em;
91                left: auto;
92                z-index: 9999;
93
94                padding: 0.25em;
95
96                box-shadow: 0 4px 5px rgb(0 0 0 / 30%);
97                border-radius: 8px;
98                border: 1px solid #fff;
99            }
100            dialog.fullscreen {
101                width: 100%;
102                height: 100%;
103                left: 1em;
104                right: 1em;
105
106            }
107            dialog > div {
108                display: flex;
109                flex-direction: column;
110                height: 100%;
111            }
112            dialog header {
113                display: flex;
114                justify-content: space-between;
115                align-items: flex-start;
116            }
117            dialog main {
118                overflow: auto;
119                flex-grow: 1;
120            }
121        `;
122        return style;
123    }
124
125    toggleDialog() {
126        if (this.#dialog.open) {
127            this.#dialog.close();
128        } else {
129            this.#dialog.show();
130        }
131    }
132}
133
134window.customElements.define('aichat-button', AIChatButton);
135