embeddedaichatux 1.3.2 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/EmbeddedChat.js +96 -48
  2. package/package.json +1 -1
package/EmbeddedChat.js CHANGED
@@ -1,5 +1,4 @@
1
- // embeddedChat.js
2
- class EmbeddedChat {
1
+ class EmbeddedChat {
3
2
  constructor(containerDiv, chatId, options) {
4
3
  if (!containerDiv) {
5
4
  containerDiv = this.createChatContainer();
@@ -11,12 +10,24 @@ class EmbeddedChat {
11
10
  this.locale = options.locale || 'en';
12
11
  this.previewParam = this.isPreview ? "?isPreview=true" : "";
13
12
  this.serverUrl = this.options.serverUrl || 'https://embedgpt.chat/';
14
- this.positionStyle = this.containerDiv.dataset.position === 'in-place' ? 'width:100%; height:600px' : 'position: fixed; right: 0; bottom: 0; width: 300px; height: 600px; z-index: 100000';
13
+ this.height = options.height || 600; // Default height
14
+ this.width = options.width || 300; // Default width
15
+ this.enableAnimation = options.enableAnimation !== false; // Default to true if not provided
16
+ this.minimizedHeight = `${this.height * 0.3}px`; // 30% of the full height
17
+ this.mouseInsideChat = false;
18
+
19
+ // Determine transition styles
20
+ const transitionStyle = this.enableAnimation ? 'transition: height 0.3s ease, opacity 0.3s ease;' : '';
21
+
22
+ this.positionStyle = this.containerDiv.dataset.position === 'in-place' ?
23
+ `width:100%; height:${this.height}px; ${transitionStyle}` :
24
+ `position: fixed; right: 0; bottom: 0; width: ${this.width}px; height: ${this.height}px; z-index: 100000; ${transitionStyle}`;
15
25
  this.mode = options.mode || 'Chat'; // default to 'chat' if mode isn't provided
26
+ this.minimizeOnScroll = false; // Default to false
16
27
  if (this.mode === 'ContactForm') {
17
28
  // Adjust position style for contact form if mode is 'ContactForm'
18
- this.positionStyle = 'width:100%; height:600px';
19
- this.containerDiv.style.height = '600px';
29
+ this.positionStyle = `width:100%; height:${this.height}px; ${transitionStyle}`;
30
+ this.containerDiv.style.height = `${this.height}px`;
20
31
  }
21
32
  this.sessionInfo = options.sessionInfo || null;
22
33
  this.init();
@@ -25,74 +36,113 @@ class EmbeddedChat {
25
36
  init() {
26
37
  const cleanedServerUrl = this.serverUrl.endsWith('/') ? this.serverUrl.slice(0, -1) : this.serverUrl;
27
38
  const baseIframeUrl = `${cleanedServerUrl}/ChatUX/${this.chatId}`;
28
- const minimizedPositionStyle = 'position: fixed; right: 0; bottom: 0; width: 300px; height: 70px; z-index: 100000';
39
+ const minimizedPositionStyle = `position: fixed; right: 0; bottom: 0; width: ${this.width}px; height: ${this.minimizedHeight}; z-index: 100000; ${this.enableAnimation ? 'transition: height 0.3s ease, opacity 0.3s ease;' : ''}`;
29
40
 
30
41
  const iframeHtml = `
31
- <iframe id="embedded-chat" style="${this.positionStyle}; display: none; border:none; overflow:hidden;" frameborder="0" sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="${this.buildIframeUrl(baseIframeUrl, { isPreview: this.isPreview, mode: this.mode, locale: this.locale })}"></iframe>
32
- <iframe id="embedded-chat-minimized" style="${minimizedPositionStyle}; display: none; border:none; overflow:hidden;" frameborder="0" sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="${this.buildIframeUrl(baseIframeUrl, { isPreview: this.isPreview, isMinimized: true, locale: this.locale })}"></iframe>
33
- `;
42
+ <iframe id="embedded-chat" style="${this.positionStyle}; display: none; border:none; overflow:hidden;" frameborder="0" sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="${this.buildIframeUrl(baseIframeUrl, { isPreview: this.isPreview, mode: this.mode, locale: this.locale })}"></iframe>
43
+ <iframe id="embedded-chat-minimized" style="${minimizedPositionStyle}; display: none; border:none; overflow:hidden;" frameborder="0" sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="${this.buildIframeUrl(baseIframeUrl, { isPreview: this.isPreview, isMinimized: true, locale: this.locale })}"></iframe>
44
+ `;
34
45
 
35
46
  this.containerDiv.innerHTML = iframeHtml;
36
47
  this.iframe = this.containerDiv.querySelector("#embedded-chat");
37
48
  this.minimizedIframe = this.containerDiv.querySelector("#embedded-chat-minimized");
38
49
 
50
+ // Add event listeners to track mouse position
51
+ this.iframe.addEventListener("mouseenter", () => { this.mouseInsideChat = true; });
52
+ this.iframe.addEventListener("mouseleave", () => { this.mouseInsideChat = false; });
53
+ this.minimizedIframe.addEventListener("mouseenter", () => { this.mouseInsideChat = true; });
54
+ this.minimizedIframe.addEventListener("mouseleave", () => { this.mouseInsideChat = false; });
55
+
39
56
  window.addEventListener("message", (e) => {
40
- // Check if the data is an object and has a message property
41
- if (typeof e.data === "object" && e.data.message && (!e.data.chatId || e.data.chatId === this.chatId)) {
57
+ this.handleMessage(e);
58
+ });
59
+
60
+ window.addEventListener("scroll", () => {
61
+ if (this.minimizeOnScroll && !this.mouseInsideChat) {
62
+ this.minimizeOnScrollAction();
63
+ }
64
+ });
65
+ }
66
+
67
+ handleMessage(e) {
68
+ if (typeof e.data === "object" && (!e.data.chatId || e.data.chatId === this.chatId)) {
69
+ if (e.data.type === "setMinimizeOnScroll") {
70
+ this.minimizeOnScroll = e.data.value === "true";
71
+ }
72
+ if (e.data.message) {
42
73
  if (e.data.message === "minimize") {
43
74
  if (this.mode !== 'ContactForm') {
44
- // No minimize support for ContactForm
45
- this.iframe.style.display = "none";
46
- this.minimizedIframe.style.display = "block";
75
+ this.animateMinimize();
47
76
  }
48
77
  } else if (e.data.message === "show" || e.data.message === "maximize") {
49
- this.iframe.style.display = "block";
50
- this.minimizedIframe.style.display = "none";
51
- // Check if the data has a scale property
52
- if (e.data.scale) {
53
- if (this.mode !== 'ContactForm') {
54
- // No scaling support for ContactForm
55
-
56
- // Resize the iframe according to the scale factor
57
- this.iframe.style.transform = `scale(${e.data.scale})`;
58
- this.iframe.style.transformOrigin = "bottom right";
59
- }
60
- }
61
- // Send sessionInfo if available
62
- if (this.sessionInfo !== null) {
63
- this.setSessionInfo(this.sessionInfo);
64
- }
78
+ this.showMaximized();
65
79
  }
66
- }
67
- // For backward compatibility, handle the case when the data is a string
68
- else if (typeof e.data === "string") {
69
- if (e.data === "minimize") {
70
- this.iframe.style.display = "none";
71
- this.minimizedIframe.style.display = "block";
72
- } else if (e.data === "show" || e.data === "maximize") {
73
- this.iframe.style.display = "block";
74
- this.minimizedIframe.style.display = "none";
80
+ if (e.data.scale) {
81
+ this.applyScale(e.data.scale);
82
+ }
83
+ if (this.sessionInfo !== null) {
84
+ this.setSessionInfo(this.sessionInfo);
75
85
  }
76
86
  }
77
- });
87
+ } else if (typeof e.data === "string") {
88
+ if (e.data === "minimize") {
89
+ this.animateMinimize();
90
+ } else if (e.data === "show" || e.data === "maximize") {
91
+ this.showMaximized();
92
+ }
93
+ }
94
+ }
95
+
96
+ animateMinimize() {
97
+ if (this.mode !== 'ContactForm') {
98
+ this.iframe.style.height = this.minimizedHeight;
99
+ this.iframe.style.opacity = '0'; // Full transparency at the end
100
+ setTimeout(() => {
101
+ this.iframe.style.display = "none";
102
+ this.iframe.style.opacity = '1'; // Reset opacity to 1 for next time
103
+ this.minimizedIframe.style.display = "block";
104
+ this.minimizedIframe.style.height = this.minimizedHeight;
105
+ this.minimizedIframe.style.opacity = '1'; // Reset opacity to 1
106
+ }, this.enableAnimation ? 300 : 0); // Match the transition duration
107
+ }
108
+ }
109
+
110
+ showMaximized() {
111
+ this.minimizedIframe.style.display = "none";
112
+ this.minimizedIframe.style.height = `${this.height}px`;
113
+ this.minimizedIframe.style.opacity = ''; // Reset opacity to unset
114
+ this.iframe.style.display = "block";
115
+ this.iframe.style.height = `${this.height}px`;
116
+ this.iframe.style.opacity = '1';
117
+ }
118
+
119
+ applyScale(scale) {
120
+ if (this.mode !== 'ContactForm') {
121
+ this.iframe.style.transform = `scale(${scale})`;
122
+ this.iframe.style.transformOrigin = "bottom right";
123
+ this.minimizedIframe.style.transform = `scale(${scale})`;
124
+ this.minimizedIframe.style.transformOrigin = "bottom right";
125
+ }
126
+ }
127
+
128
+ minimizeOnScrollAction() {
129
+ if (this.mode !== 'ContactForm' && this.iframe.style.display !== "none") {
130
+ this.animateMinimize();
131
+ }
78
132
  }
79
133
 
80
134
  setSessionInfo(sessionInfo) {
81
135
  console.log('setSessionInfo called with sessionInfo:', sessionInfo);
82
136
 
83
- // Store the new sessionInfo
84
137
  this.sessionInfo = sessionInfo;
85
138
 
86
- // Obtain the iframe element
87
139
  const iframe = this.containerDiv.querySelector("#embedded-chat");
88
140
 
89
- // Function to post the message with a structured object
90
141
  const postMessage = () => {
91
142
  console.log('Posting message to iframe:', { type: 'setSessionInfo', sessionInfo: sessionInfo });
92
143
  iframe.contentWindow.postMessage({ type: 'setSessionInfo', sessionInfo: sessionInfo }, "*");
93
144
  };
94
145
 
95
- // If the iframe is visible, post the message
96
146
  if (iframe.style.display !== "none") {
97
147
  postMessage();
98
148
  }
@@ -109,7 +159,6 @@ class EmbeddedChat {
109
159
  buildIframeUrl(baseIframeUrl, params = {}) {
110
160
  const urlParams = new URLSearchParams(params);
111
161
 
112
- // Add parameters as needed, based on values in the params object
113
162
  if (params.isPreview) {
114
163
  urlParams.set('isPreview', 'true');
115
164
  }
@@ -122,9 +171,8 @@ class EmbeddedChat {
122
171
  urlParams.set('locale', params.locale);
123
172
  }
124
173
 
125
- // Handle other parameters directly from the params object
126
174
  for (const [key, value] of Object.entries(params)) {
127
- if (key !== 'mode' && key !== 'isPreview') { // Avoid duplicates
175
+ if (key !== 'mode' && key !== 'isPreview') {
128
176
  urlParams.set(key, value);
129
177
  }
130
178
  }
@@ -138,6 +186,6 @@ export function initEmbeddedChat(containerDiv, chatId, options) {
138
186
  }
139
187
 
140
188
  export function initContactForm(containerDiv, chatId, options) {
141
- const clonedOptions = { ...options, mode: 'ContactForm' }; // Clone with spread syntax
189
+ const clonedOptions = { ...options, mode: 'ContactForm' };
142
190
  return new EmbeddedChat(containerDiv, chatId, clonedOptions);
143
191
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "embeddedaichatux",
3
- "version": "1.3.2",
3
+ "version": "1.4.1",
4
4
  "description": "A lightweight and customizable embedded AI chat UI component that seamlessly integrates into web applications, offering minimized and expanded views, with iframe-based content rendering.",
5
5
  "main": "EmbeddedChat.js",
6
6
  "scripts": {