chatbox-nps-chat-widget 0.1.0

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.
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Chat Widget (npm)
2
+
3
+ Plain HTML/CSS/JS embed for the AI chat UI. This package injects a floating button
4
+ and popup iframe that loads your chat app.
5
+
6
+ ## Install
7
+ ```bash
8
+ npm install chatbox-nps-chat-widget
9
+ ```
10
+
11
+ ## Plain HTML usage
12
+ ```html
13
+ <link
14
+ rel="stylesheet"
15
+ href="/node_modules/chatbox-nps-chat-widget/dist/chat-widget.css"
16
+ />
17
+ <script src="/node_modules/chatbox-nps-chat-widget/dist/chat-widget.iife.js"></script>
18
+ <script>
19
+ NpssChatWidget.initChatWidget({
20
+ iframeUrl: "https://app.yourdomain.com/embed?ai=1"
21
+ });
22
+ </script>
23
+ ```
24
+
25
+ ## ESM usage
26
+ ```js
27
+ import { initChatWidget } from "chatbox-nps-chat-widget";
28
+ import "chatbox-nps-chat-widget/dist/chat-widget.css";
29
+
30
+ initChatWidget({
31
+ iframeUrl: "https://app.yourdomain.com/embed?ai=1",
32
+ });
33
+ ```
34
+
35
+ ## Options
36
+ - `iframeUrl` (required)
37
+ - `position` ("bottom-right" | "bottom-left" | "top-right" | "top-left")
38
+ - `primaryColor`
39
+ - `buttonText`
40
+ - `buttonLabel`
41
+ - `buttonSize`
42
+ - `popupWidth`
43
+ - `popupHeight`
44
+ - `offset`
45
+ - `popupOffset`
46
+ - `zIndex`
47
+ - `title`
48
+ - `preload`
49
+ - `injectStyles` (default true; set false if you always load CSS manually)
50
+
51
+ ## Build
52
+ ```bash
53
+ npm run build
54
+ ```
@@ -0,0 +1,356 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/index.js
20
+ var src_exports = {};
21
+ __export(src_exports, {
22
+ getStyles: () => getStyles,
23
+ initChatWidget: () => initChatWidget
24
+ });
25
+ module.exports = __toCommonJS(src_exports);
26
+
27
+ // src/styles.js
28
+ var DEFAULT_STYLES = `
29
+ #npss-chat-widget {
30
+ position: fixed;
31
+ inset: 0;
32
+ z-index: var(--npss-z-index, 9999);
33
+ pointer-events: none;
34
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
35
+ }
36
+
37
+ #npss-chat-widget * {
38
+ box-sizing: border-box;
39
+ }
40
+
41
+ #npss-chat-widget .npss-chat-widget__fab {
42
+ position: absolute;
43
+ width: var(--npss-button-size, 56px);
44
+ height: var(--npss-button-size, 56px);
45
+ border-radius: 50%;
46
+ border: none;
47
+ background: var(--npss-primary, #2d6cdf);
48
+ color: #ffffff;
49
+ font-weight: 700;
50
+ font-size: 16px;
51
+ cursor: pointer;
52
+ pointer-events: auto;
53
+ box-shadow: 0 18px 40px rgba(15, 23, 42, 0.28);
54
+ }
55
+
56
+ #npss-chat-widget .npss-chat-widget__popup {
57
+ position: absolute;
58
+ width: min(var(--npss-popup-width, 420px), 92vw);
59
+ height: min(var(--npss-popup-height, 640px), 80vh);
60
+ background: #ffffff;
61
+ border-radius: 16px;
62
+ box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22);
63
+ display: none;
64
+ flex-direction: column;
65
+ overflow: hidden;
66
+ pointer-events: auto;
67
+ }
68
+
69
+ #npss-chat-widget .npss-chat-widget__popup--open {
70
+ display: flex;
71
+ }
72
+
73
+ #npss-chat-widget .npss-chat-widget__header {
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: space-between;
77
+ padding: 12px 16px;
78
+ border-bottom: 1px solid #e2e8f0;
79
+ background: #f8fafc;
80
+ font-size: 14px;
81
+ font-weight: 600;
82
+ }
83
+
84
+ #npss-chat-widget .npss-chat-widget__close {
85
+ border: none;
86
+ background: transparent;
87
+ font-size: 20px;
88
+ line-height: 1;
89
+ cursor: pointer;
90
+ color: #475569;
91
+ }
92
+
93
+ #npss-chat-widget .npss-chat-widget__frame {
94
+ width: 100%;
95
+ height: 100%;
96
+ border: 0;
97
+ }
98
+
99
+ #npss-chat-widget[data-position="bottom-right"] .npss-chat-widget__fab {
100
+ right: var(--npss-offset, 24px);
101
+ bottom: var(--npss-offset, 24px);
102
+ }
103
+
104
+ #npss-chat-widget[data-position="bottom-left"] .npss-chat-widget__fab {
105
+ left: var(--npss-offset, 24px);
106
+ bottom: var(--npss-offset, 24px);
107
+ }
108
+
109
+ #npss-chat-widget[data-position="top-right"] .npss-chat-widget__fab {
110
+ right: var(--npss-offset, 24px);
111
+ top: var(--npss-offset, 24px);
112
+ }
113
+
114
+ #npss-chat-widget[data-position="top-left"] .npss-chat-widget__fab {
115
+ left: var(--npss-offset, 24px);
116
+ top: var(--npss-offset, 24px);
117
+ }
118
+
119
+ #npss-chat-widget[data-position="bottom-right"] .npss-chat-widget__popup {
120
+ right: var(--npss-offset, 24px);
121
+ bottom: calc(
122
+ var(--npss-offset, 24px) +
123
+ var(--npss-button-size, 56px) +
124
+ var(--npss-popup-offset, 16px)
125
+ );
126
+ }
127
+
128
+ #npss-chat-widget[data-position="bottom-left"] .npss-chat-widget__popup {
129
+ left: var(--npss-offset, 24px);
130
+ bottom: calc(
131
+ var(--npss-offset, 24px) +
132
+ var(--npss-button-size, 56px) +
133
+ var(--npss-popup-offset, 16px)
134
+ );
135
+ }
136
+
137
+ #npss-chat-widget[data-position="top-right"] .npss-chat-widget__popup {
138
+ right: var(--npss-offset, 24px);
139
+ top: calc(
140
+ var(--npss-offset, 24px) +
141
+ var(--npss-button-size, 56px) +
142
+ var(--npss-popup-offset, 16px)
143
+ );
144
+ }
145
+
146
+ #npss-chat-widget[data-position="top-left"] .npss-chat-widget__popup {
147
+ left: var(--npss-offset, 24px);
148
+ top: calc(
149
+ var(--npss-offset, 24px) +
150
+ var(--npss-button-size, 56px) +
151
+ var(--npss-popup-offset, 16px)
152
+ );
153
+ }
154
+
155
+ @media (max-width: 640px) {
156
+ #npss-chat-widget {
157
+ --npss-offset: 16px;
158
+ }
159
+
160
+ #npss-chat-widget .npss-chat-widget__popup {
161
+ left: 16px;
162
+ right: 16px;
163
+ width: auto;
164
+ height: min(70vh, 520px);
165
+ }
166
+ }
167
+ `;
168
+ var getStyles = () => DEFAULT_STYLES.trim();
169
+
170
+ // src/index.js
171
+ var WIDGET_VERSION = "0.1.0";
172
+ var ROOT_ID = "npss-chat-widget";
173
+ var STYLE_ID = "npss-chat-widget-styles";
174
+ var DEFAULTS = {
175
+ position: "bottom-right",
176
+ primaryColor: "#2d6cdf",
177
+ buttonText: "AI",
178
+ buttonLabel: "Open AI chat",
179
+ buttonSize: 56,
180
+ popupWidth: 420,
181
+ popupHeight: 640,
182
+ offset: 24,
183
+ popupOffset: 16,
184
+ zIndex: 9999,
185
+ title: "AI Chat",
186
+ preload: false,
187
+ injectStyles: true
188
+ };
189
+ var toPx = function(value) {
190
+ if (typeof value === "number") return value + "px";
191
+ if (typeof value === "string" && value.trim()) return value.trim();
192
+ return null;
193
+ };
194
+ var mergeSettings = function(options) {
195
+ var result = {};
196
+ var key;
197
+ for (key in DEFAULTS) {
198
+ result[key] = DEFAULTS[key];
199
+ }
200
+ if (options) {
201
+ for (key in options) {
202
+ if (Object.prototype.hasOwnProperty.call(options, key)) {
203
+ result[key] = options[key];
204
+ }
205
+ }
206
+ }
207
+ return result;
208
+ };
209
+ var applyVariables = function(root, settings) {
210
+ root.style.setProperty("--npss-primary", settings.primaryColor);
211
+ root.style.setProperty("--npss-button-size", toPx(settings.buttonSize));
212
+ root.style.setProperty("--npss-popup-width", toPx(settings.popupWidth));
213
+ root.style.setProperty("--npss-popup-height", toPx(settings.popupHeight));
214
+ root.style.setProperty("--npss-offset", toPx(settings.offset));
215
+ root.style.setProperty("--npss-popup-offset", toPx(settings.popupOffset));
216
+ root.style.setProperty("--npss-z-index", String(settings.zIndex));
217
+ };
218
+ var injectStyles = function() {
219
+ if (document.getElementById(STYLE_ID)) return;
220
+ var style = document.createElement("style");
221
+ style.id = STYLE_ID;
222
+ style.textContent = getStyles();
223
+ document.head.appendChild(style);
224
+ };
225
+ var buildDom = function(settings) {
226
+ var root = document.createElement("div");
227
+ root.id = ROOT_ID;
228
+ root.setAttribute("data-version", WIDGET_VERSION);
229
+ root.setAttribute("data-position", settings.position || DEFAULTS.position);
230
+ applyVariables(root, settings);
231
+ var popup = document.createElement("div");
232
+ popup.className = "npss-chat-widget__popup";
233
+ popup.setAttribute("role", "dialog");
234
+ popup.setAttribute("aria-modal", "false");
235
+ popup.setAttribute("aria-hidden", "true");
236
+ var header = document.createElement("div");
237
+ header.className = "npss-chat-widget__header";
238
+ var title = document.createElement("span");
239
+ title.className = "npss-chat-widget__title";
240
+ title.textContent = settings.title || DEFAULTS.title;
241
+ var closeButton = document.createElement("button");
242
+ closeButton.type = "button";
243
+ closeButton.className = "npss-chat-widget__close";
244
+ closeButton.setAttribute("aria-label", "Close AI chat");
245
+ closeButton.textContent = "\xD7";
246
+ header.appendChild(title);
247
+ header.appendChild(closeButton);
248
+ var iframe = document.createElement("iframe");
249
+ iframe.className = "npss-chat-widget__frame";
250
+ iframe.title = settings.title || DEFAULTS.title;
251
+ iframe.allow = "clipboard-read; clipboard-write";
252
+ popup.appendChild(header);
253
+ popup.appendChild(iframe);
254
+ var button = document.createElement("button");
255
+ button.type = "button";
256
+ button.className = "npss-chat-widget__fab";
257
+ button.setAttribute("aria-label", settings.buttonLabel || DEFAULTS.buttonLabel);
258
+ button.setAttribute("aria-expanded", "false");
259
+ button.textContent = settings.buttonText || DEFAULTS.buttonText;
260
+ root.appendChild(popup);
261
+ root.appendChild(button);
262
+ return {
263
+ root,
264
+ popup,
265
+ button,
266
+ closeButton,
267
+ iframe
268
+ };
269
+ };
270
+ var mountWidget = function(settings) {
271
+ var iframeUrl = settings.iframeUrl;
272
+ if (!iframeUrl) {
273
+ throw new Error("initChatWidget requires iframeUrl");
274
+ }
275
+ var existing = window.__NPSS_CHAT_WIDGET__;
276
+ if (existing && existing.root) return existing;
277
+ if (settings.injectStyles) {
278
+ injectStyles();
279
+ }
280
+ var dom = buildDom(settings);
281
+ var root = dom.root;
282
+ var popup = dom.popup;
283
+ var button = dom.button;
284
+ var closeButton = dom.closeButton;
285
+ var iframe = dom.iframe;
286
+ var container = document.body || document.documentElement;
287
+ container.appendChild(root);
288
+ var isOpen = false;
289
+ var isLoaded = false;
290
+ var ensureFrame = function() {
291
+ if (isLoaded) return;
292
+ iframe.src = iframeUrl;
293
+ isLoaded = true;
294
+ };
295
+ var open = function() {
296
+ ensureFrame();
297
+ isOpen = true;
298
+ popup.classList.add("npss-chat-widget__popup--open");
299
+ popup.setAttribute("aria-hidden", "false");
300
+ button.setAttribute("aria-expanded", "true");
301
+ };
302
+ var close = function() {
303
+ isOpen = false;
304
+ popup.classList.remove("npss-chat-widget__popup--open");
305
+ popup.setAttribute("aria-hidden", "true");
306
+ button.setAttribute("aria-expanded", "false");
307
+ };
308
+ var toggle = function() {
309
+ if (isOpen) {
310
+ close();
311
+ } else {
312
+ open();
313
+ }
314
+ };
315
+ var onKeyDown = function(event) {
316
+ if (event.key === "Escape" && isOpen) {
317
+ close();
318
+ }
319
+ };
320
+ button.addEventListener("click", toggle);
321
+ closeButton.addEventListener("click", close);
322
+ document.addEventListener("keydown", onKeyDown);
323
+ if (settings.preload) {
324
+ ensureFrame();
325
+ }
326
+ var instance = {
327
+ root,
328
+ open,
329
+ close,
330
+ toggle,
331
+ destroy: function() {
332
+ document.removeEventListener("keydown", onKeyDown);
333
+ if (root.parentNode) {
334
+ root.parentNode.removeChild(root);
335
+ }
336
+ delete window.__NPSS_CHAT_WIDGET__;
337
+ }
338
+ };
339
+ window.__NPSS_CHAT_WIDGET__ = instance;
340
+ return instance;
341
+ };
342
+ var initChatWidget = function(options) {
343
+ var settings = mergeSettings(options);
344
+ if (document.body) {
345
+ return mountWidget(settings);
346
+ }
347
+ return new Promise(function(resolve) {
348
+ document.addEventListener(
349
+ "DOMContentLoaded",
350
+ function() {
351
+ resolve(mountWidget(settings));
352
+ },
353
+ { once: true }
354
+ );
355
+ });
356
+ };
@@ -0,0 +1,138 @@
1
+ #npss-chat-widget {
2
+ position: fixed;
3
+ inset: 0;
4
+ z-index: var(--npss-z-index, 9999);
5
+ pointer-events: none;
6
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
7
+ }
8
+
9
+ #npss-chat-widget * {
10
+ box-sizing: border-box;
11
+ }
12
+
13
+ #npss-chat-widget .npss-chat-widget__fab {
14
+ position: absolute;
15
+ width: var(--npss-button-size, 56px);
16
+ height: var(--npss-button-size, 56px);
17
+ border-radius: 50%;
18
+ border: none;
19
+ background: var(--npss-primary, #2d6cdf);
20
+ color: #ffffff;
21
+ font-weight: 700;
22
+ font-size: 16px;
23
+ cursor: pointer;
24
+ pointer-events: auto;
25
+ box-shadow: 0 18px 40px rgba(15, 23, 42, 0.28);
26
+ }
27
+
28
+ #npss-chat-widget .npss-chat-widget__popup {
29
+ position: absolute;
30
+ width: min(var(--npss-popup-width, 420px), 92vw);
31
+ height: min(var(--npss-popup-height, 640px), 80vh);
32
+ background: #ffffff;
33
+ border-radius: 16px;
34
+ box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22);
35
+ display: none;
36
+ flex-direction: column;
37
+ overflow: hidden;
38
+ pointer-events: auto;
39
+ }
40
+
41
+ #npss-chat-widget .npss-chat-widget__popup--open {
42
+ display: flex;
43
+ }
44
+
45
+ #npss-chat-widget .npss-chat-widget__header {
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: space-between;
49
+ padding: 12px 16px;
50
+ border-bottom: 1px solid #e2e8f0;
51
+ background: #f8fafc;
52
+ font-size: 14px;
53
+ font-weight: 600;
54
+ }
55
+
56
+ #npss-chat-widget .npss-chat-widget__close {
57
+ border: none;
58
+ background: transparent;
59
+ font-size: 20px;
60
+ line-height: 1;
61
+ cursor: pointer;
62
+ color: #475569;
63
+ }
64
+
65
+ #npss-chat-widget .npss-chat-widget__frame {
66
+ width: 100%;
67
+ height: 100%;
68
+ border: 0;
69
+ }
70
+
71
+ #npss-chat-widget[data-position="bottom-right"] .npss-chat-widget__fab {
72
+ right: var(--npss-offset, 24px);
73
+ bottom: var(--npss-offset, 24px);
74
+ }
75
+
76
+ #npss-chat-widget[data-position="bottom-left"] .npss-chat-widget__fab {
77
+ left: var(--npss-offset, 24px);
78
+ bottom: var(--npss-offset, 24px);
79
+ }
80
+
81
+ #npss-chat-widget[data-position="top-right"] .npss-chat-widget__fab {
82
+ right: var(--npss-offset, 24px);
83
+ top: var(--npss-offset, 24px);
84
+ }
85
+
86
+ #npss-chat-widget[data-position="top-left"] .npss-chat-widget__fab {
87
+ left: var(--npss-offset, 24px);
88
+ top: var(--npss-offset, 24px);
89
+ }
90
+
91
+ #npss-chat-widget[data-position="bottom-right"] .npss-chat-widget__popup {
92
+ right: var(--npss-offset, 24px);
93
+ bottom: calc(
94
+ var(--npss-offset, 24px) +
95
+ var(--npss-button-size, 56px) +
96
+ var(--npss-popup-offset, 16px)
97
+ );
98
+ }
99
+
100
+ #npss-chat-widget[data-position="bottom-left"] .npss-chat-widget__popup {
101
+ left: var(--npss-offset, 24px);
102
+ bottom: calc(
103
+ var(--npss-offset, 24px) +
104
+ var(--npss-button-size, 56px) +
105
+ var(--npss-popup-offset, 16px)
106
+ );
107
+ }
108
+
109
+ #npss-chat-widget[data-position="top-right"] .npss-chat-widget__popup {
110
+ right: var(--npss-offset, 24px);
111
+ top: calc(
112
+ var(--npss-offset, 24px) +
113
+ var(--npss-button-size, 56px) +
114
+ var(--npss-popup-offset, 16px)
115
+ );
116
+ }
117
+
118
+ #npss-chat-widget[data-position="top-left"] .npss-chat-widget__popup {
119
+ left: var(--npss-offset, 24px);
120
+ top: calc(
121
+ var(--npss-offset, 24px) +
122
+ var(--npss-button-size, 56px) +
123
+ var(--npss-popup-offset, 16px)
124
+ );
125
+ }
126
+
127
+ @media (max-width: 640px) {
128
+ #npss-chat-widget {
129
+ --npss-offset: 16px;
130
+ }
131
+
132
+ #npss-chat-widget .npss-chat-widget__popup {
133
+ left: 16px;
134
+ right: 16px;
135
+ width: auto;
136
+ height: min(70vh, 520px);
137
+ }
138
+ }
@@ -0,0 +1,334 @@
1
+ // src/styles.js
2
+ var DEFAULT_STYLES = `
3
+ #npss-chat-widget {
4
+ position: fixed;
5
+ inset: 0;
6
+ z-index: var(--npss-z-index, 9999);
7
+ pointer-events: none;
8
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
9
+ }
10
+
11
+ #npss-chat-widget * {
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ #npss-chat-widget .npss-chat-widget__fab {
16
+ position: absolute;
17
+ width: var(--npss-button-size, 56px);
18
+ height: var(--npss-button-size, 56px);
19
+ border-radius: 50%;
20
+ border: none;
21
+ background: var(--npss-primary, #2d6cdf);
22
+ color: #ffffff;
23
+ font-weight: 700;
24
+ font-size: 16px;
25
+ cursor: pointer;
26
+ pointer-events: auto;
27
+ box-shadow: 0 18px 40px rgba(15, 23, 42, 0.28);
28
+ }
29
+
30
+ #npss-chat-widget .npss-chat-widget__popup {
31
+ position: absolute;
32
+ width: min(var(--npss-popup-width, 420px), 92vw);
33
+ height: min(var(--npss-popup-height, 640px), 80vh);
34
+ background: #ffffff;
35
+ border-radius: 16px;
36
+ box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22);
37
+ display: none;
38
+ flex-direction: column;
39
+ overflow: hidden;
40
+ pointer-events: auto;
41
+ }
42
+
43
+ #npss-chat-widget .npss-chat-widget__popup--open {
44
+ display: flex;
45
+ }
46
+
47
+ #npss-chat-widget .npss-chat-widget__header {
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: space-between;
51
+ padding: 12px 16px;
52
+ border-bottom: 1px solid #e2e8f0;
53
+ background: #f8fafc;
54
+ font-size: 14px;
55
+ font-weight: 600;
56
+ }
57
+
58
+ #npss-chat-widget .npss-chat-widget__close {
59
+ border: none;
60
+ background: transparent;
61
+ font-size: 20px;
62
+ line-height: 1;
63
+ cursor: pointer;
64
+ color: #475569;
65
+ }
66
+
67
+ #npss-chat-widget .npss-chat-widget__frame {
68
+ width: 100%;
69
+ height: 100%;
70
+ border: 0;
71
+ }
72
+
73
+ #npss-chat-widget[data-position="bottom-right"] .npss-chat-widget__fab {
74
+ right: var(--npss-offset, 24px);
75
+ bottom: var(--npss-offset, 24px);
76
+ }
77
+
78
+ #npss-chat-widget[data-position="bottom-left"] .npss-chat-widget__fab {
79
+ left: var(--npss-offset, 24px);
80
+ bottom: var(--npss-offset, 24px);
81
+ }
82
+
83
+ #npss-chat-widget[data-position="top-right"] .npss-chat-widget__fab {
84
+ right: var(--npss-offset, 24px);
85
+ top: var(--npss-offset, 24px);
86
+ }
87
+
88
+ #npss-chat-widget[data-position="top-left"] .npss-chat-widget__fab {
89
+ left: var(--npss-offset, 24px);
90
+ top: var(--npss-offset, 24px);
91
+ }
92
+
93
+ #npss-chat-widget[data-position="bottom-right"] .npss-chat-widget__popup {
94
+ right: var(--npss-offset, 24px);
95
+ bottom: calc(
96
+ var(--npss-offset, 24px) +
97
+ var(--npss-button-size, 56px) +
98
+ var(--npss-popup-offset, 16px)
99
+ );
100
+ }
101
+
102
+ #npss-chat-widget[data-position="bottom-left"] .npss-chat-widget__popup {
103
+ left: var(--npss-offset, 24px);
104
+ bottom: calc(
105
+ var(--npss-offset, 24px) +
106
+ var(--npss-button-size, 56px) +
107
+ var(--npss-popup-offset, 16px)
108
+ );
109
+ }
110
+
111
+ #npss-chat-widget[data-position="top-right"] .npss-chat-widget__popup {
112
+ right: var(--npss-offset, 24px);
113
+ top: calc(
114
+ var(--npss-offset, 24px) +
115
+ var(--npss-button-size, 56px) +
116
+ var(--npss-popup-offset, 16px)
117
+ );
118
+ }
119
+
120
+ #npss-chat-widget[data-position="top-left"] .npss-chat-widget__popup {
121
+ left: var(--npss-offset, 24px);
122
+ top: calc(
123
+ var(--npss-offset, 24px) +
124
+ var(--npss-button-size, 56px) +
125
+ var(--npss-popup-offset, 16px)
126
+ );
127
+ }
128
+
129
+ @media (max-width: 640px) {
130
+ #npss-chat-widget {
131
+ --npss-offset: 16px;
132
+ }
133
+
134
+ #npss-chat-widget .npss-chat-widget__popup {
135
+ left: 16px;
136
+ right: 16px;
137
+ width: auto;
138
+ height: min(70vh, 520px);
139
+ }
140
+ }
141
+ `;
142
+ var getStyles = () => DEFAULT_STYLES.trim();
143
+
144
+ // src/index.js
145
+ var WIDGET_VERSION = "0.1.0";
146
+ var ROOT_ID = "npss-chat-widget";
147
+ var STYLE_ID = "npss-chat-widget-styles";
148
+ var DEFAULTS = {
149
+ position: "bottom-right",
150
+ primaryColor: "#2d6cdf",
151
+ buttonText: "AI",
152
+ buttonLabel: "Open AI chat",
153
+ buttonSize: 56,
154
+ popupWidth: 420,
155
+ popupHeight: 640,
156
+ offset: 24,
157
+ popupOffset: 16,
158
+ zIndex: 9999,
159
+ title: "AI Chat",
160
+ preload: false,
161
+ injectStyles: true
162
+ };
163
+ var toPx = function(value) {
164
+ if (typeof value === "number") return value + "px";
165
+ if (typeof value === "string" && value.trim()) return value.trim();
166
+ return null;
167
+ };
168
+ var mergeSettings = function(options) {
169
+ var result = {};
170
+ var key;
171
+ for (key in DEFAULTS) {
172
+ result[key] = DEFAULTS[key];
173
+ }
174
+ if (options) {
175
+ for (key in options) {
176
+ if (Object.prototype.hasOwnProperty.call(options, key)) {
177
+ result[key] = options[key];
178
+ }
179
+ }
180
+ }
181
+ return result;
182
+ };
183
+ var applyVariables = function(root, settings) {
184
+ root.style.setProperty("--npss-primary", settings.primaryColor);
185
+ root.style.setProperty("--npss-button-size", toPx(settings.buttonSize));
186
+ root.style.setProperty("--npss-popup-width", toPx(settings.popupWidth));
187
+ root.style.setProperty("--npss-popup-height", toPx(settings.popupHeight));
188
+ root.style.setProperty("--npss-offset", toPx(settings.offset));
189
+ root.style.setProperty("--npss-popup-offset", toPx(settings.popupOffset));
190
+ root.style.setProperty("--npss-z-index", String(settings.zIndex));
191
+ };
192
+ var injectStyles = function() {
193
+ if (document.getElementById(STYLE_ID)) return;
194
+ var style = document.createElement("style");
195
+ style.id = STYLE_ID;
196
+ style.textContent = getStyles();
197
+ document.head.appendChild(style);
198
+ };
199
+ var buildDom = function(settings) {
200
+ var root = document.createElement("div");
201
+ root.id = ROOT_ID;
202
+ root.setAttribute("data-version", WIDGET_VERSION);
203
+ root.setAttribute("data-position", settings.position || DEFAULTS.position);
204
+ applyVariables(root, settings);
205
+ var popup = document.createElement("div");
206
+ popup.className = "npss-chat-widget__popup";
207
+ popup.setAttribute("role", "dialog");
208
+ popup.setAttribute("aria-modal", "false");
209
+ popup.setAttribute("aria-hidden", "true");
210
+ var header = document.createElement("div");
211
+ header.className = "npss-chat-widget__header";
212
+ var title = document.createElement("span");
213
+ title.className = "npss-chat-widget__title";
214
+ title.textContent = settings.title || DEFAULTS.title;
215
+ var closeButton = document.createElement("button");
216
+ closeButton.type = "button";
217
+ closeButton.className = "npss-chat-widget__close";
218
+ closeButton.setAttribute("aria-label", "Close AI chat");
219
+ closeButton.textContent = "\xD7";
220
+ header.appendChild(title);
221
+ header.appendChild(closeButton);
222
+ var iframe = document.createElement("iframe");
223
+ iframe.className = "npss-chat-widget__frame";
224
+ iframe.title = settings.title || DEFAULTS.title;
225
+ iframe.allow = "clipboard-read; clipboard-write";
226
+ popup.appendChild(header);
227
+ popup.appendChild(iframe);
228
+ var button = document.createElement("button");
229
+ button.type = "button";
230
+ button.className = "npss-chat-widget__fab";
231
+ button.setAttribute("aria-label", settings.buttonLabel || DEFAULTS.buttonLabel);
232
+ button.setAttribute("aria-expanded", "false");
233
+ button.textContent = settings.buttonText || DEFAULTS.buttonText;
234
+ root.appendChild(popup);
235
+ root.appendChild(button);
236
+ return {
237
+ root,
238
+ popup,
239
+ button,
240
+ closeButton,
241
+ iframe
242
+ };
243
+ };
244
+ var mountWidget = function(settings) {
245
+ var iframeUrl = settings.iframeUrl;
246
+ if (!iframeUrl) {
247
+ throw new Error("initChatWidget requires iframeUrl");
248
+ }
249
+ var existing = window.__NPSS_CHAT_WIDGET__;
250
+ if (existing && existing.root) return existing;
251
+ if (settings.injectStyles) {
252
+ injectStyles();
253
+ }
254
+ var dom = buildDom(settings);
255
+ var root = dom.root;
256
+ var popup = dom.popup;
257
+ var button = dom.button;
258
+ var closeButton = dom.closeButton;
259
+ var iframe = dom.iframe;
260
+ var container = document.body || document.documentElement;
261
+ container.appendChild(root);
262
+ var isOpen = false;
263
+ var isLoaded = false;
264
+ var ensureFrame = function() {
265
+ if (isLoaded) return;
266
+ iframe.src = iframeUrl;
267
+ isLoaded = true;
268
+ };
269
+ var open = function() {
270
+ ensureFrame();
271
+ isOpen = true;
272
+ popup.classList.add("npss-chat-widget__popup--open");
273
+ popup.setAttribute("aria-hidden", "false");
274
+ button.setAttribute("aria-expanded", "true");
275
+ };
276
+ var close = function() {
277
+ isOpen = false;
278
+ popup.classList.remove("npss-chat-widget__popup--open");
279
+ popup.setAttribute("aria-hidden", "true");
280
+ button.setAttribute("aria-expanded", "false");
281
+ };
282
+ var toggle = function() {
283
+ if (isOpen) {
284
+ close();
285
+ } else {
286
+ open();
287
+ }
288
+ };
289
+ var onKeyDown = function(event) {
290
+ if (event.key === "Escape" && isOpen) {
291
+ close();
292
+ }
293
+ };
294
+ button.addEventListener("click", toggle);
295
+ closeButton.addEventListener("click", close);
296
+ document.addEventListener("keydown", onKeyDown);
297
+ if (settings.preload) {
298
+ ensureFrame();
299
+ }
300
+ var instance = {
301
+ root,
302
+ open,
303
+ close,
304
+ toggle,
305
+ destroy: function() {
306
+ document.removeEventListener("keydown", onKeyDown);
307
+ if (root.parentNode) {
308
+ root.parentNode.removeChild(root);
309
+ }
310
+ delete window.__NPSS_CHAT_WIDGET__;
311
+ }
312
+ };
313
+ window.__NPSS_CHAT_WIDGET__ = instance;
314
+ return instance;
315
+ };
316
+ var initChatWidget = function(options) {
317
+ var settings = mergeSettings(options);
318
+ if (document.body) {
319
+ return mountWidget(settings);
320
+ }
321
+ return new Promise(function(resolve) {
322
+ document.addEventListener(
323
+ "DOMContentLoaded",
324
+ function() {
325
+ resolve(mountWidget(settings));
326
+ },
327
+ { once: true }
328
+ );
329
+ });
330
+ };
331
+ export {
332
+ getStyles,
333
+ initChatWidget
334
+ };
@@ -0,0 +1,140 @@
1
+ var NpssChatWidget=(()=>{var u=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var L=(t,e)=>{for(var s in e)u(t,s,{get:e[s],enumerable:!0})},I=(t,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let p of S(e))!z.call(t,p)&&p!==s&&u(t,p,{get:()=>e[p],enumerable:!(n=A(e,p))||n.enumerable});return t};var T=t=>I(u({},"__esModule",{value:!0}),t);var B={};L(B,{getStyles:()=>l,initChatWidget:()=>j});var P=`
2
+ #npss-chat-widget {
3
+ position: fixed;
4
+ inset: 0;
5
+ z-index: var(--npss-z-index, 9999);
6
+ pointer-events: none;
7
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
8
+ }
9
+
10
+ #npss-chat-widget * {
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ #npss-chat-widget .npss-chat-widget__fab {
15
+ position: absolute;
16
+ width: var(--npss-button-size, 56px);
17
+ height: var(--npss-button-size, 56px);
18
+ border-radius: 50%;
19
+ border: none;
20
+ background: var(--npss-primary, #2d6cdf);
21
+ color: #ffffff;
22
+ font-weight: 700;
23
+ font-size: 16px;
24
+ cursor: pointer;
25
+ pointer-events: auto;
26
+ box-shadow: 0 18px 40px rgba(15, 23, 42, 0.28);
27
+ }
28
+
29
+ #npss-chat-widget .npss-chat-widget__popup {
30
+ position: absolute;
31
+ width: min(var(--npss-popup-width, 420px), 92vw);
32
+ height: min(var(--npss-popup-height, 640px), 80vh);
33
+ background: #ffffff;
34
+ border-radius: 16px;
35
+ box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22);
36
+ display: none;
37
+ flex-direction: column;
38
+ overflow: hidden;
39
+ pointer-events: auto;
40
+ }
41
+
42
+ #npss-chat-widget .npss-chat-widget__popup--open {
43
+ display: flex;
44
+ }
45
+
46
+ #npss-chat-widget .npss-chat-widget__header {
47
+ display: flex;
48
+ align-items: center;
49
+ justify-content: space-between;
50
+ padding: 12px 16px;
51
+ border-bottom: 1px solid #e2e8f0;
52
+ background: #f8fafc;
53
+ font-size: 14px;
54
+ font-weight: 600;
55
+ }
56
+
57
+ #npss-chat-widget .npss-chat-widget__close {
58
+ border: none;
59
+ background: transparent;
60
+ font-size: 20px;
61
+ line-height: 1;
62
+ cursor: pointer;
63
+ color: #475569;
64
+ }
65
+
66
+ #npss-chat-widget .npss-chat-widget__frame {
67
+ width: 100%;
68
+ height: 100%;
69
+ border: 0;
70
+ }
71
+
72
+ #npss-chat-widget[data-position="bottom-right"] .npss-chat-widget__fab {
73
+ right: var(--npss-offset, 24px);
74
+ bottom: var(--npss-offset, 24px);
75
+ }
76
+
77
+ #npss-chat-widget[data-position="bottom-left"] .npss-chat-widget__fab {
78
+ left: var(--npss-offset, 24px);
79
+ bottom: var(--npss-offset, 24px);
80
+ }
81
+
82
+ #npss-chat-widget[data-position="top-right"] .npss-chat-widget__fab {
83
+ right: var(--npss-offset, 24px);
84
+ top: var(--npss-offset, 24px);
85
+ }
86
+
87
+ #npss-chat-widget[data-position="top-left"] .npss-chat-widget__fab {
88
+ left: var(--npss-offset, 24px);
89
+ top: var(--npss-offset, 24px);
90
+ }
91
+
92
+ #npss-chat-widget[data-position="bottom-right"] .npss-chat-widget__popup {
93
+ right: var(--npss-offset, 24px);
94
+ bottom: calc(
95
+ var(--npss-offset, 24px) +
96
+ var(--npss-button-size, 56px) +
97
+ var(--npss-popup-offset, 16px)
98
+ );
99
+ }
100
+
101
+ #npss-chat-widget[data-position="bottom-left"] .npss-chat-widget__popup {
102
+ left: var(--npss-offset, 24px);
103
+ bottom: calc(
104
+ var(--npss-offset, 24px) +
105
+ var(--npss-button-size, 56px) +
106
+ var(--npss-popup-offset, 16px)
107
+ );
108
+ }
109
+
110
+ #npss-chat-widget[data-position="top-right"] .npss-chat-widget__popup {
111
+ right: var(--npss-offset, 24px);
112
+ top: calc(
113
+ var(--npss-offset, 24px) +
114
+ var(--npss-button-size, 56px) +
115
+ var(--npss-popup-offset, 16px)
116
+ );
117
+ }
118
+
119
+ #npss-chat-widget[data-position="top-left"] .npss-chat-widget__popup {
120
+ left: var(--npss-offset, 24px);
121
+ top: calc(
122
+ var(--npss-offset, 24px) +
123
+ var(--npss-button-size, 56px) +
124
+ var(--npss-popup-offset, 16px)
125
+ );
126
+ }
127
+
128
+ @media (max-width: 640px) {
129
+ #npss-chat-widget {
130
+ --npss-offset: 16px;
131
+ }
132
+
133
+ #npss-chat-widget .npss-chat-widget__popup {
134
+ left: 16px;
135
+ right: 16px;
136
+ width: auto;
137
+ height: min(70vh, 520px);
138
+ }
139
+ }
140
+ `,l=()=>P.trim();var N="0.1.0",D="npss-chat-widget",w="npss-chat-widget-styles",i={position:"bottom-right",primaryColor:"#2d6cdf",buttonText:"AI",buttonLabel:"Open AI chat",buttonSize:56,popupWidth:420,popupHeight:640,offset:24,popupOffset:16,zIndex:9999,title:"AI Chat",preload:!1,injectStyles:!0},d=function(t){return typeof t=="number"?t+"px":typeof t=="string"&&t.trim()?t.trim():null},k=function(t){var e={},s;for(s in i)e[s]=i[s];if(t)for(s in t)Object.prototype.hasOwnProperty.call(t,s)&&(e[s]=t[s]);return e},O=function(t,e){t.style.setProperty("--npss-primary",e.primaryColor),t.style.setProperty("--npss-button-size",d(e.buttonSize)),t.style.setProperty("--npss-popup-width",d(e.popupWidth)),t.style.setProperty("--npss-popup-height",d(e.popupHeight)),t.style.setProperty("--npss-offset",d(e.offset)),t.style.setProperty("--npss-popup-offset",d(e.popupOffset)),t.style.setProperty("--npss-z-index",String(e.zIndex))},W=function(){if(!document.getElementById(w)){var t=document.createElement("style");t.id=w,t.textContent=l(),document.head.appendChild(t)}},U=function(t){var e=document.createElement("div");e.id=D,e.setAttribute("data-version",N),e.setAttribute("data-position",t.position||i.position),O(e,t);var s=document.createElement("div");s.className="npss-chat-widget__popup",s.setAttribute("role","dialog"),s.setAttribute("aria-modal","false"),s.setAttribute("aria-hidden","true");var n=document.createElement("div");n.className="npss-chat-widget__header";var p=document.createElement("span");p.className="npss-chat-widget__title",p.textContent=t.title||i.title;var o=document.createElement("button");o.type="button",o.className="npss-chat-widget__close",o.setAttribute("aria-label","Close AI chat"),o.textContent="\xD7",n.appendChild(p),n.appendChild(o);var a=document.createElement("iframe");a.className="npss-chat-widget__frame",a.title=t.title||i.title,a.allow="clipboard-read; clipboard-write",s.appendChild(n),s.appendChild(a);var r=document.createElement("button");return r.type="button",r.className="npss-chat-widget__fab",r.setAttribute("aria-label",t.buttonLabel||i.buttonLabel),r.setAttribute("aria-expanded","false"),r.textContent=t.buttonText||i.buttonText,e.appendChild(s),e.appendChild(r),{root:e,popup:s,button:r,closeButton:o,iframe:a}},_=function(t){var e=t.iframeUrl;if(!e)throw new Error("initChatWidget requires iframeUrl");var s=window.__NPSS_CHAT_WIDGET__;if(s&&s.root)return s;t.injectStyles&&W();var n=U(t),p=n.root,o=n.popup,a=n.button,r=n.closeButton,y=n.iframe,E=document.body||document.documentElement;E.appendChild(p);var f=!1,h=!1,b=function(){h||(y.src=e,h=!0)},v=function(){b(),f=!0,o.classList.add("npss-chat-widget__popup--open"),o.setAttribute("aria-hidden","false"),a.setAttribute("aria-expanded","true")},c=function(){f=!1,o.classList.remove("npss-chat-widget__popup--open"),o.setAttribute("aria-hidden","true"),a.setAttribute("aria-expanded","false")},m=function(){f?c():v()},g=function(C){C.key==="Escape"&&f&&c()};a.addEventListener("click",m),r.addEventListener("click",c),document.addEventListener("keydown",g),t.preload&&b();var x={root:p,open:v,close:c,toggle:m,destroy:function(){document.removeEventListener("keydown",g),p.parentNode&&p.parentNode.removeChild(p),delete window.__NPSS_CHAT_WIDGET__}};return window.__NPSS_CHAT_WIDGET__=x,x},j=function(t){var e=k(t);return document.body?_(e):new Promise(function(s){document.addEventListener("DOMContentLoaded",function(){s(_(e))},{once:!0})})};return T(B);})();
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "chatbox-nps-chat-widget",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "./dist/chat-widget.cjs.js",
7
+ "module": "./dist/chat-widget.esm.js",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/chat-widget.esm.js",
11
+ "require": "./dist/chat-widget.cjs.js"
12
+ },
13
+ "./styles.css": "./dist/chat-widget.css"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "scripts": {
22
+ "build": "node scripts/build.js"
23
+ },
24
+ "devDependencies": {
25
+ "esbuild": "^0.21.5"
26
+ }
27
+ }