@pocketping/widget 1.11.0 → 1.12.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.
package/dist/index.cjs CHANGED
@@ -218,24 +218,28 @@ function styles(options) {
218
218
  left: 20px;
219
219
  }
220
220
 
221
+ /* Mobile: fullscreen widget to prevent scroll issues */
221
222
  @media (max-width: 480px) {
222
223
  .pp-window {
223
- width: calc(100vw - 20px);
224
- height: auto;
225
- min-height: 300px;
226
- max-height: calc(100vh - 40px);
227
- max-height: calc(100svh - 40px); /* svh = small viewport, excludes keyboard */
228
- bottom: 10px;
229
- right: 10px;
230
- left: 10px;
231
- border-radius: 12px;
224
+ position: fixed;
225
+ top: 0;
226
+ left: 0;
227
+ right: 0;
228
+ bottom: 0;
229
+ width: 100%;
230
+ height: 100%;
231
+ max-height: 100%;
232
+ max-height: 100dvh;
233
+ min-height: 100%;
234
+ min-height: 100dvh;
235
+ border-radius: 0;
236
+ overflow: hidden;
237
+ touch-action: none; /* Prevent page scroll behind widget */
232
238
  }
233
239
 
234
- /* When keyboard is likely open (input focused), reduce height */
235
- .pp-window:has(.pp-input:focus) {
236
- max-height: calc(50vh - 20px);
237
- max-height: calc(50svh - 20px);
238
- bottom: 10px;
240
+ /* Prevent any horizontal scroll */
241
+ .pp-window * {
242
+ max-width: 100%;
239
243
  }
240
244
  }
241
245
 
@@ -319,6 +323,7 @@ function styles(options) {
319
323
  .pp-messages {
320
324
  flex: 1;
321
325
  overflow-y: auto;
326
+ overflow-x: hidden;
322
327
  padding: 16px 12px;
323
328
  display: flex;
324
329
  flex-direction: column;
@@ -330,6 +335,7 @@ function styles(options) {
330
335
  background-image: ${chatBgImage};
331
336
  background-size: ${chatBgSize};
332
337
  background-position: center;
338
+ touch-action: pan-y; /* Only allow vertical scrolling */
333
339
  }
334
340
 
335
341
  .pp-welcome {
@@ -368,8 +374,9 @@ function styles(options) {
368
374
  position: relative;
369
375
  display: flex;
370
376
  align-items: stretch;
371
- overflow: visible;
372
- touch-action: pan-y;
377
+ overflow: hidden; /* Prevent horizontal overflow */
378
+ touch-action: pan-y; /* Only vertical scroll */
379
+ max-width: 100%;
373
380
  }
374
381
 
375
382
  .pp-swipe-left {
@@ -504,11 +511,14 @@ function styles(options) {
504
511
  }
505
512
 
506
513
  /* Add spacing between different senders */
507
- .pp-message-visitor + .pp-message-operator,
508
- .pp-message-visitor + .pp-message-ai,
509
- .pp-message-operator + .pp-message-visitor,
510
- .pp-message-ai + .pp-message-visitor {
511
- margin-top: 8px;
514
+ .pp-message-swipe-container + .pp-message-swipe-container {
515
+ margin-top: 4px;
516
+ }
517
+
518
+ /* More spacing when sender changes (visitor <-> operator/ai) */
519
+ .pp-swipe-left + .pp-swipe-right,
520
+ .pp-swipe-right + .pp-swipe-left {
521
+ margin-top: 16px;
512
522
  }
513
523
 
514
524
  .pp-message-content {
@@ -628,23 +638,27 @@ function styles(options) {
628
638
  padding: 8px 10px;
629
639
  gap: 8px;
630
640
  background: ${resolvedFooterColor};
631
- align-items: center;
641
+ align-items: flex-end;
632
642
  }
633
643
 
634
644
  .pp-input {
635
645
  flex: 1;
636
646
  min-width: 0;
637
- height: 42px;
638
- line-height: 42px;
639
- padding: 0 14px;
647
+ min-height: 42px;
648
+ max-height: 120px;
649
+ padding: 10px 14px;
640
650
  border: none;
641
651
  border-radius: 8px;
642
652
  background: ${isDark ? "#2a3942" : "#ffffff"};
643
653
  color: ${isDark ? "#e9edef" : "#111b21"};
644
654
  font-size: 15px;
655
+ line-height: 1.4;
645
656
  outline: none;
646
657
  box-sizing: border-box;
647
658
  margin: 0;
659
+ resize: none;
660
+ font-family: inherit;
661
+ overflow-y: auto;
648
662
  }
649
663
 
650
664
  .pp-input:focus {
@@ -2047,6 +2061,35 @@ function ChatWidget({ client: client2, config: initialConfig }) {
2047
2061
  setUnreadCount(0);
2048
2062
  }
2049
2063
  }, [isOpen]);
2064
+ (0, import_hooks2.useEffect)(() => {
2065
+ const isMobile = window.innerWidth <= 480;
2066
+ if (isOpen && isMobile) {
2067
+ const scrollY = window.scrollY;
2068
+ const originalStyles = {
2069
+ overflow: document.body.style.overflow,
2070
+ position: document.body.style.position,
2071
+ top: document.body.style.top,
2072
+ left: document.body.style.left,
2073
+ right: document.body.style.right,
2074
+ width: document.body.style.width
2075
+ };
2076
+ document.body.style.overflow = "hidden";
2077
+ document.body.style.position = "fixed";
2078
+ document.body.style.top = `-${scrollY}px`;
2079
+ document.body.style.left = "0";
2080
+ document.body.style.right = "0";
2081
+ document.body.style.width = "100%";
2082
+ return () => {
2083
+ document.body.style.overflow = originalStyles.overflow;
2084
+ document.body.style.position = originalStyles.position;
2085
+ document.body.style.top = originalStyles.top;
2086
+ document.body.style.left = originalStyles.left;
2087
+ document.body.style.right = originalStyles.right;
2088
+ document.body.style.width = originalStyles.width;
2089
+ window.scrollTo(0, scrollY);
2090
+ };
2091
+ }
2092
+ }, [isOpen]);
2050
2093
  (0, import_hooks2.useEffect)(() => {
2051
2094
  if (!isOpen && messages.length > 0) {
2052
2095
  const unread = messages.filter(
@@ -2103,6 +2146,9 @@ function ChatWidget({ client: client2, config: initialConfig }) {
2103
2146
  setInputValue("");
2104
2147
  setPendingAttachments([]);
2105
2148
  setReplyingTo(null);
2149
+ if (inputRef.current) {
2150
+ inputRef.current.style.height = "auto";
2151
+ }
2106
2152
  try {
2107
2153
  await client2.sendMessage(content, attachmentIds, replyToId);
2108
2154
  } catch (err) {
@@ -2112,8 +2158,17 @@ function ChatWidget({ client: client2, config: initialConfig }) {
2112
2158
  const handleInputChange = (e) => {
2113
2159
  const target = e.target;
2114
2160
  setInputValue(target.value);
2161
+ target.style.height = "auto";
2162
+ target.style.height = Math.min(target.scrollHeight, 120) + "px";
2115
2163
  client2.sendTyping(true);
2116
2164
  };
2165
+ const handleKeyDown = (e) => {
2166
+ if (e.key === "Enter" && !e.shiftKey) {
2167
+ e.preventDefault();
2168
+ const form = e.target.closest("form");
2169
+ if (form) form.requestSubmit();
2170
+ }
2171
+ };
2117
2172
  const handleFileSelect = async (e) => {
2118
2173
  const target = e.target;
2119
2174
  const files = target.files;
@@ -2233,9 +2288,16 @@ function ChatWidget({ client: client2, config: initialConfig }) {
2233
2288
  const touch = e.touches[0];
2234
2289
  const deltaX = touch.clientX - touchStartRef.current.x;
2235
2290
  const deltaY = touch.clientY - touchStartRef.current.y;
2236
- if (Math.abs(deltaY) > Math.abs(deltaX)) return;
2291
+ if (Math.abs(deltaY) > 10 || Math.abs(deltaX) < 15) {
2292
+ if (swipedMessageId === message.id && swipeOffset !== 0) {
2293
+ setSwipeOffset(0);
2294
+ setSwipedMessageId(null);
2295
+ }
2296
+ return;
2297
+ }
2298
+ e.preventDefault();
2237
2299
  if (deltaX < 0) {
2238
- const offset = Math.max(deltaX, -100);
2300
+ const offset = Math.max(deltaX, -80);
2239
2301
  setSwipeOffset(offset);
2240
2302
  setSwipedMessageId(message.id);
2241
2303
  }
@@ -2658,15 +2720,16 @@ function ChatWidget({ client: client2, config: initialConfig }) {
2658
2720
  }
2659
2721
  ),
2660
2722
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2661
- "input",
2723
+ "textarea",
2662
2724
  {
2663
2725
  ref: inputRef,
2664
- type: "text",
2665
2726
  class: "pp-input",
2666
2727
  placeholder: config.placeholder ?? "Type a message...",
2667
2728
  value: inputValue,
2668
2729
  onInput: handleInputChange,
2669
- disabled: !isConnected
2730
+ onKeyDown: handleKeyDown,
2731
+ disabled: !isConnected,
2732
+ rows: 1
2670
2733
  }
2671
2734
  ),
2672
2735
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
package/dist/index.js CHANGED
@@ -177,24 +177,28 @@ function styles(options) {
177
177
  left: 20px;
178
178
  }
179
179
 
180
+ /* Mobile: fullscreen widget to prevent scroll issues */
180
181
  @media (max-width: 480px) {
181
182
  .pp-window {
182
- width: calc(100vw - 20px);
183
- height: auto;
184
- min-height: 300px;
185
- max-height: calc(100vh - 40px);
186
- max-height: calc(100svh - 40px); /* svh = small viewport, excludes keyboard */
187
- bottom: 10px;
188
- right: 10px;
189
- left: 10px;
190
- border-radius: 12px;
183
+ position: fixed;
184
+ top: 0;
185
+ left: 0;
186
+ right: 0;
187
+ bottom: 0;
188
+ width: 100%;
189
+ height: 100%;
190
+ max-height: 100%;
191
+ max-height: 100dvh;
192
+ min-height: 100%;
193
+ min-height: 100dvh;
194
+ border-radius: 0;
195
+ overflow: hidden;
196
+ touch-action: none; /* Prevent page scroll behind widget */
191
197
  }
192
198
 
193
- /* When keyboard is likely open (input focused), reduce height */
194
- .pp-window:has(.pp-input:focus) {
195
- max-height: calc(50vh - 20px);
196
- max-height: calc(50svh - 20px);
197
- bottom: 10px;
199
+ /* Prevent any horizontal scroll */
200
+ .pp-window * {
201
+ max-width: 100%;
198
202
  }
199
203
  }
200
204
 
@@ -278,6 +282,7 @@ function styles(options) {
278
282
  .pp-messages {
279
283
  flex: 1;
280
284
  overflow-y: auto;
285
+ overflow-x: hidden;
281
286
  padding: 16px 12px;
282
287
  display: flex;
283
288
  flex-direction: column;
@@ -289,6 +294,7 @@ function styles(options) {
289
294
  background-image: ${chatBgImage};
290
295
  background-size: ${chatBgSize};
291
296
  background-position: center;
297
+ touch-action: pan-y; /* Only allow vertical scrolling */
292
298
  }
293
299
 
294
300
  .pp-welcome {
@@ -327,8 +333,9 @@ function styles(options) {
327
333
  position: relative;
328
334
  display: flex;
329
335
  align-items: stretch;
330
- overflow: visible;
331
- touch-action: pan-y;
336
+ overflow: hidden; /* Prevent horizontal overflow */
337
+ touch-action: pan-y; /* Only vertical scroll */
338
+ max-width: 100%;
332
339
  }
333
340
 
334
341
  .pp-swipe-left {
@@ -463,11 +470,14 @@ function styles(options) {
463
470
  }
464
471
 
465
472
  /* Add spacing between different senders */
466
- .pp-message-visitor + .pp-message-operator,
467
- .pp-message-visitor + .pp-message-ai,
468
- .pp-message-operator + .pp-message-visitor,
469
- .pp-message-ai + .pp-message-visitor {
470
- margin-top: 8px;
473
+ .pp-message-swipe-container + .pp-message-swipe-container {
474
+ margin-top: 4px;
475
+ }
476
+
477
+ /* More spacing when sender changes (visitor <-> operator/ai) */
478
+ .pp-swipe-left + .pp-swipe-right,
479
+ .pp-swipe-right + .pp-swipe-left {
480
+ margin-top: 16px;
471
481
  }
472
482
 
473
483
  .pp-message-content {
@@ -587,23 +597,27 @@ function styles(options) {
587
597
  padding: 8px 10px;
588
598
  gap: 8px;
589
599
  background: ${resolvedFooterColor};
590
- align-items: center;
600
+ align-items: flex-end;
591
601
  }
592
602
 
593
603
  .pp-input {
594
604
  flex: 1;
595
605
  min-width: 0;
596
- height: 42px;
597
- line-height: 42px;
598
- padding: 0 14px;
606
+ min-height: 42px;
607
+ max-height: 120px;
608
+ padding: 10px 14px;
599
609
  border: none;
600
610
  border-radius: 8px;
601
611
  background: ${isDark ? "#2a3942" : "#ffffff"};
602
612
  color: ${isDark ? "#e9edef" : "#111b21"};
603
613
  font-size: 15px;
614
+ line-height: 1.4;
604
615
  outline: none;
605
616
  box-sizing: border-box;
606
617
  margin: 0;
618
+ resize: none;
619
+ font-family: inherit;
620
+ overflow-y: auto;
607
621
  }
608
622
 
609
623
  .pp-input:focus {
@@ -2006,6 +2020,35 @@ function ChatWidget({ client: client2, config: initialConfig }) {
2006
2020
  setUnreadCount(0);
2007
2021
  }
2008
2022
  }, [isOpen]);
2023
+ useEffect2(() => {
2024
+ const isMobile = window.innerWidth <= 480;
2025
+ if (isOpen && isMobile) {
2026
+ const scrollY = window.scrollY;
2027
+ const originalStyles = {
2028
+ overflow: document.body.style.overflow,
2029
+ position: document.body.style.position,
2030
+ top: document.body.style.top,
2031
+ left: document.body.style.left,
2032
+ right: document.body.style.right,
2033
+ width: document.body.style.width
2034
+ };
2035
+ document.body.style.overflow = "hidden";
2036
+ document.body.style.position = "fixed";
2037
+ document.body.style.top = `-${scrollY}px`;
2038
+ document.body.style.left = "0";
2039
+ document.body.style.right = "0";
2040
+ document.body.style.width = "100%";
2041
+ return () => {
2042
+ document.body.style.overflow = originalStyles.overflow;
2043
+ document.body.style.position = originalStyles.position;
2044
+ document.body.style.top = originalStyles.top;
2045
+ document.body.style.left = originalStyles.left;
2046
+ document.body.style.right = originalStyles.right;
2047
+ document.body.style.width = originalStyles.width;
2048
+ window.scrollTo(0, scrollY);
2049
+ };
2050
+ }
2051
+ }, [isOpen]);
2009
2052
  useEffect2(() => {
2010
2053
  if (!isOpen && messages.length > 0) {
2011
2054
  const unread = messages.filter(
@@ -2062,6 +2105,9 @@ function ChatWidget({ client: client2, config: initialConfig }) {
2062
2105
  setInputValue("");
2063
2106
  setPendingAttachments([]);
2064
2107
  setReplyingTo(null);
2108
+ if (inputRef.current) {
2109
+ inputRef.current.style.height = "auto";
2110
+ }
2065
2111
  try {
2066
2112
  await client2.sendMessage(content, attachmentIds, replyToId);
2067
2113
  } catch (err) {
@@ -2071,8 +2117,17 @@ function ChatWidget({ client: client2, config: initialConfig }) {
2071
2117
  const handleInputChange = (e) => {
2072
2118
  const target = e.target;
2073
2119
  setInputValue(target.value);
2120
+ target.style.height = "auto";
2121
+ target.style.height = Math.min(target.scrollHeight, 120) + "px";
2074
2122
  client2.sendTyping(true);
2075
2123
  };
2124
+ const handleKeyDown = (e) => {
2125
+ if (e.key === "Enter" && !e.shiftKey) {
2126
+ e.preventDefault();
2127
+ const form = e.target.closest("form");
2128
+ if (form) form.requestSubmit();
2129
+ }
2130
+ };
2076
2131
  const handleFileSelect = async (e) => {
2077
2132
  const target = e.target;
2078
2133
  const files = target.files;
@@ -2192,9 +2247,16 @@ function ChatWidget({ client: client2, config: initialConfig }) {
2192
2247
  const touch = e.touches[0];
2193
2248
  const deltaX = touch.clientX - touchStartRef.current.x;
2194
2249
  const deltaY = touch.clientY - touchStartRef.current.y;
2195
- if (Math.abs(deltaY) > Math.abs(deltaX)) return;
2250
+ if (Math.abs(deltaY) > 10 || Math.abs(deltaX) < 15) {
2251
+ if (swipedMessageId === message.id && swipeOffset !== 0) {
2252
+ setSwipeOffset(0);
2253
+ setSwipedMessageId(null);
2254
+ }
2255
+ return;
2256
+ }
2257
+ e.preventDefault();
2196
2258
  if (deltaX < 0) {
2197
- const offset = Math.max(deltaX, -100);
2259
+ const offset = Math.max(deltaX, -80);
2198
2260
  setSwipeOffset(offset);
2199
2261
  setSwipedMessageId(message.id);
2200
2262
  }
@@ -2617,15 +2679,16 @@ function ChatWidget({ client: client2, config: initialConfig }) {
2617
2679
  }
2618
2680
  ),
2619
2681
  /* @__PURE__ */ jsx2(
2620
- "input",
2682
+ "textarea",
2621
2683
  {
2622
2684
  ref: inputRef,
2623
- type: "text",
2624
2685
  class: "pp-input",
2625
2686
  placeholder: config.placeholder ?? "Type a message...",
2626
2687
  value: inputValue,
2627
2688
  onInput: handleInputChange,
2628
- disabled: !isConnected
2689
+ onKeyDown: handleKeyDown,
2690
+ disabled: !isConnected,
2691
+ rows: 1
2629
2692
  }
2630
2693
  ),
2631
2694
  /* @__PURE__ */ jsx2(