@tp3/chat-widget 0.1.10 → 0.1.12
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/dist/ChatWidget.js +11 -29
- package/dist/ChatWidget.mjs +11 -29
- package/package.json +1 -1
- package/src/ChatWidget.tsx +16 -39
package/dist/ChatWidget.js
CHANGED
|
@@ -64,45 +64,30 @@ function ChatWidget({
|
|
|
64
64
|
const wsRef = (0, import_react.useRef)(null);
|
|
65
65
|
const messagesEnd = (0, import_react.useRef)(null);
|
|
66
66
|
const inputRef = (0, import_react.useRef)(null);
|
|
67
|
-
const chatWindowRef = (0, import_react.useRef)(null);
|
|
68
67
|
const typewriterQueue = (0, import_react.useRef)([]);
|
|
69
68
|
const typewriterTimer = (0, import_react.useRef)(null);
|
|
69
|
+
const savedScrollY = (0, import_react.useRef)(0);
|
|
70
70
|
(0, import_react.useEffect)(() => {
|
|
71
71
|
if (open && !closing) {
|
|
72
|
-
|
|
72
|
+
savedScrollY.current = window.scrollY;
|
|
73
73
|
document.body.style.position = "fixed";
|
|
74
|
-
document.body.style.top = `-${
|
|
75
|
-
document.body.style.
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
document.body.style.top = `-${savedScrollY.current}px`;
|
|
75
|
+
document.body.style.left = "0";
|
|
76
|
+
document.body.style.right = "0";
|
|
77
|
+
} else {
|
|
78
78
|
document.body.style.position = "";
|
|
79
79
|
document.body.style.top = "";
|
|
80
|
-
document.body.style.
|
|
81
|
-
|
|
80
|
+
document.body.style.left = "";
|
|
81
|
+
document.body.style.right = "";
|
|
82
|
+
window.scrollTo(0, savedScrollY.current);
|
|
82
83
|
}
|
|
83
84
|
return () => {
|
|
84
|
-
const top = Math.abs(parseInt(document.body.style.top || "0", 10));
|
|
85
85
|
document.body.style.position = "";
|
|
86
86
|
document.body.style.top = "";
|
|
87
|
-
document.body.style.
|
|
88
|
-
|
|
87
|
+
document.body.style.left = "";
|
|
88
|
+
document.body.style.right = "";
|
|
89
89
|
};
|
|
90
90
|
}, [open, closing]);
|
|
91
|
-
(0, import_react.useEffect)(() => {
|
|
92
|
-
const el = chatWindowRef.current;
|
|
93
|
-
if (!el || !open) return;
|
|
94
|
-
function blockIfOutsideMessages(e) {
|
|
95
|
-
const target = e.target;
|
|
96
|
-
if (target.closest("[data-chat-messages]")) return;
|
|
97
|
-
e.preventDefault();
|
|
98
|
-
}
|
|
99
|
-
el.addEventListener("wheel", blockIfOutsideMessages, { passive: false });
|
|
100
|
-
el.addEventListener("touchmove", blockIfOutsideMessages, { passive: false });
|
|
101
|
-
return () => {
|
|
102
|
-
el.removeEventListener("wheel", blockIfOutsideMessages);
|
|
103
|
-
el.removeEventListener("touchmove", blockIfOutsideMessages);
|
|
104
|
-
};
|
|
105
|
-
}, [open]);
|
|
106
91
|
(0, import_react.useEffect)(() => {
|
|
107
92
|
if (messages.length > 1) {
|
|
108
93
|
messagesEnd.current?.scrollIntoView({ behavior: "smooth" });
|
|
@@ -298,7 +283,6 @@ function ChatWidget({
|
|
|
298
283
|
open && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
299
284
|
"div",
|
|
300
285
|
{
|
|
301
|
-
ref: chatWindowRef,
|
|
302
286
|
className: closing ? "chat-window-out" : "chat-window",
|
|
303
287
|
style: {
|
|
304
288
|
position: "fixed",
|
|
@@ -309,7 +293,6 @@ function ChatWidget({
|
|
|
309
293
|
maxWidth: "calc(100vw - 48px)",
|
|
310
294
|
height: chatHeight,
|
|
311
295
|
maxHeight: "calc(100dvh - 48px)",
|
|
312
|
-
overscrollBehavior: "contain",
|
|
313
296
|
background: "var(--chat-primary-fg, #fff)",
|
|
314
297
|
display: "flex",
|
|
315
298
|
flexDirection: "column",
|
|
@@ -374,7 +357,6 @@ function ChatWidget({
|
|
|
374
357
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
375
358
|
"div",
|
|
376
359
|
{
|
|
377
|
-
"data-chat-messages": true,
|
|
378
360
|
style: {
|
|
379
361
|
flex: 1,
|
|
380
362
|
overflowY: "auto",
|
package/dist/ChatWidget.mjs
CHANGED
|
@@ -40,45 +40,30 @@ function ChatWidget({
|
|
|
40
40
|
const wsRef = useRef(null);
|
|
41
41
|
const messagesEnd = useRef(null);
|
|
42
42
|
const inputRef = useRef(null);
|
|
43
|
-
const chatWindowRef = useRef(null);
|
|
44
43
|
const typewriterQueue = useRef([]);
|
|
45
44
|
const typewriterTimer = useRef(null);
|
|
45
|
+
const savedScrollY = useRef(0);
|
|
46
46
|
useEffect(() => {
|
|
47
47
|
if (open && !closing) {
|
|
48
|
-
|
|
48
|
+
savedScrollY.current = window.scrollY;
|
|
49
49
|
document.body.style.position = "fixed";
|
|
50
|
-
document.body.style.top = `-${
|
|
51
|
-
document.body.style.
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
document.body.style.top = `-${savedScrollY.current}px`;
|
|
51
|
+
document.body.style.left = "0";
|
|
52
|
+
document.body.style.right = "0";
|
|
53
|
+
} else {
|
|
54
54
|
document.body.style.position = "";
|
|
55
55
|
document.body.style.top = "";
|
|
56
|
-
document.body.style.
|
|
57
|
-
|
|
56
|
+
document.body.style.left = "";
|
|
57
|
+
document.body.style.right = "";
|
|
58
|
+
window.scrollTo(0, savedScrollY.current);
|
|
58
59
|
}
|
|
59
60
|
return () => {
|
|
60
|
-
const top = Math.abs(parseInt(document.body.style.top || "0", 10));
|
|
61
61
|
document.body.style.position = "";
|
|
62
62
|
document.body.style.top = "";
|
|
63
|
-
document.body.style.
|
|
64
|
-
|
|
63
|
+
document.body.style.left = "";
|
|
64
|
+
document.body.style.right = "";
|
|
65
65
|
};
|
|
66
66
|
}, [open, closing]);
|
|
67
|
-
useEffect(() => {
|
|
68
|
-
const el = chatWindowRef.current;
|
|
69
|
-
if (!el || !open) return;
|
|
70
|
-
function blockIfOutsideMessages(e) {
|
|
71
|
-
const target = e.target;
|
|
72
|
-
if (target.closest("[data-chat-messages]")) return;
|
|
73
|
-
e.preventDefault();
|
|
74
|
-
}
|
|
75
|
-
el.addEventListener("wheel", blockIfOutsideMessages, { passive: false });
|
|
76
|
-
el.addEventListener("touchmove", blockIfOutsideMessages, { passive: false });
|
|
77
|
-
return () => {
|
|
78
|
-
el.removeEventListener("wheel", blockIfOutsideMessages);
|
|
79
|
-
el.removeEventListener("touchmove", blockIfOutsideMessages);
|
|
80
|
-
};
|
|
81
|
-
}, [open]);
|
|
82
67
|
useEffect(() => {
|
|
83
68
|
if (messages.length > 1) {
|
|
84
69
|
messagesEnd.current?.scrollIntoView({ behavior: "smooth" });
|
|
@@ -274,7 +259,6 @@ function ChatWidget({
|
|
|
274
259
|
open && /* @__PURE__ */ jsxs(
|
|
275
260
|
"div",
|
|
276
261
|
{
|
|
277
|
-
ref: chatWindowRef,
|
|
278
262
|
className: closing ? "chat-window-out" : "chat-window",
|
|
279
263
|
style: {
|
|
280
264
|
position: "fixed",
|
|
@@ -285,7 +269,6 @@ function ChatWidget({
|
|
|
285
269
|
maxWidth: "calc(100vw - 48px)",
|
|
286
270
|
height: chatHeight,
|
|
287
271
|
maxHeight: "calc(100dvh - 48px)",
|
|
288
|
-
overscrollBehavior: "contain",
|
|
289
272
|
background: "var(--chat-primary-fg, #fff)",
|
|
290
273
|
display: "flex",
|
|
291
274
|
flexDirection: "column",
|
|
@@ -350,7 +333,6 @@ function ChatWidget({
|
|
|
350
333
|
/* @__PURE__ */ jsxs(
|
|
351
334
|
"div",
|
|
352
335
|
{
|
|
353
|
-
"data-chat-messages": true,
|
|
354
336
|
style: {
|
|
355
337
|
flex: 1,
|
|
356
338
|
overflowY: "auto",
|
package/package.json
CHANGED
package/src/ChatWidget.tsx
CHANGED
|
@@ -64,57 +64,37 @@ export default function ChatWidget({
|
|
|
64
64
|
const wsRef = useRef<WebSocket | null>(null);
|
|
65
65
|
const messagesEnd = useRef<HTMLDivElement>(null);
|
|
66
66
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
67
|
-
const chatWindowRef = useRef<HTMLDivElement>(null);
|
|
68
67
|
const typewriterQueue = useRef<string[]>([]);
|
|
69
68
|
const typewriterTimer = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
70
69
|
|
|
71
|
-
// Lock
|
|
72
|
-
//
|
|
73
|
-
//
|
|
70
|
+
// Lock scroll on the page behind the chat without blocking scroll
|
|
71
|
+
// inside the chat messages area. Uses position:fixed on body — the
|
|
72
|
+
// chat window is also fixed, so it stays visible and its internal
|
|
73
|
+
// overflow-y:auto container scrolls independently.
|
|
74
|
+
const savedScrollY = useRef(0);
|
|
75
|
+
|
|
74
76
|
useEffect(() => {
|
|
75
77
|
if (open && !closing) {
|
|
76
|
-
|
|
78
|
+
savedScrollY.current = window.scrollY;
|
|
77
79
|
document.body.style.position = "fixed";
|
|
78
|
-
document.body.style.top = `-${
|
|
79
|
-
document.body.style.
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
document.body.style.top = `-${savedScrollY.current}px`;
|
|
81
|
+
document.body.style.left = "0";
|
|
82
|
+
document.body.style.right = "0";
|
|
83
|
+
} else {
|
|
82
84
|
document.body.style.position = "";
|
|
83
85
|
document.body.style.top = "";
|
|
84
|
-
document.body.style.
|
|
85
|
-
|
|
86
|
+
document.body.style.left = "";
|
|
87
|
+
document.body.style.right = "";
|
|
88
|
+
window.scrollTo(0, savedScrollY.current);
|
|
86
89
|
}
|
|
87
90
|
return () => {
|
|
88
|
-
const top = Math.abs(parseInt(document.body.style.top || "0", 10));
|
|
89
91
|
document.body.style.position = "";
|
|
90
92
|
document.body.style.top = "";
|
|
91
|
-
document.body.style.
|
|
92
|
-
|
|
93
|
+
document.body.style.left = "";
|
|
94
|
+
document.body.style.right = "";
|
|
93
95
|
};
|
|
94
96
|
}, [open, closing]);
|
|
95
97
|
|
|
96
|
-
// Prevent events on the chat window from scrolling the page behind.
|
|
97
|
-
// Only blocks events targeting the window itself, not the scrollable messages area.
|
|
98
|
-
useEffect(() => {
|
|
99
|
-
const el = chatWindowRef.current;
|
|
100
|
-
if (!el || !open) return;
|
|
101
|
-
|
|
102
|
-
function blockIfOutsideMessages(e: WheelEvent | TouchEvent) {
|
|
103
|
-
const target = e.target as HTMLElement;
|
|
104
|
-
// Allow scroll inside the messages container
|
|
105
|
-
if (target.closest('[data-chat-messages]')) return;
|
|
106
|
-
e.preventDefault();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
el.addEventListener("wheel", blockIfOutsideMessages, { passive: false });
|
|
110
|
-
el.addEventListener("touchmove", blockIfOutsideMessages, { passive: false });
|
|
111
|
-
|
|
112
|
-
return () => {
|
|
113
|
-
el.removeEventListener("wheel", blockIfOutsideMessages);
|
|
114
|
-
el.removeEventListener("touchmove", blockIfOutsideMessages);
|
|
115
|
-
};
|
|
116
|
-
}, [open]);
|
|
117
|
-
|
|
118
98
|
useEffect(() => {
|
|
119
99
|
// Don't scroll on first render — wait for the open animation
|
|
120
100
|
if (messages.length > 1) {
|
|
@@ -330,7 +310,6 @@ export default function ChatWidget({
|
|
|
330
310
|
|
|
331
311
|
{open && (
|
|
332
312
|
<div
|
|
333
|
-
ref={chatWindowRef}
|
|
334
313
|
className={closing ? "chat-window-out" : "chat-window"}
|
|
335
314
|
style={{
|
|
336
315
|
position: "fixed",
|
|
@@ -341,7 +320,6 @@ export default function ChatWidget({
|
|
|
341
320
|
maxWidth: "calc(100vw - 48px)",
|
|
342
321
|
height: chatHeight,
|
|
343
322
|
maxHeight: "calc(100dvh - 48px)",
|
|
344
|
-
overscrollBehavior: "contain",
|
|
345
323
|
background: "var(--chat-primary-fg, #fff)",
|
|
346
324
|
display: "flex",
|
|
347
325
|
flexDirection: "column",
|
|
@@ -401,7 +379,6 @@ export default function ChatWidget({
|
|
|
401
379
|
|
|
402
380
|
{/* Messages */}
|
|
403
381
|
<div
|
|
404
|
-
data-chat-messages
|
|
405
382
|
style={{
|
|
406
383
|
flex: 1,
|
|
407
384
|
overflowY: "auto",
|