livechat 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f475396740f88ad5b62ea7f30889dd7e3b36699834f061d2a7d09cdf341b88a
4
- data.tar.gz: cb58318b0d8ac30a316d59bca48e8eca3bbe63b697c0d744a2404820d9327344
3
+ metadata.gz: ab3b69e1c61320ea9df88571fc2fcad40c09c44e0f14f961b59a87d23ffc161e
4
+ data.tar.gz: b6750a5aef1f99e7a6d2a49ed2fbcd6bb9cb1fb992cefd176e552f7d281f42e8
5
5
  SHA512:
6
- metadata.gz: e261a7ad6751071cafb5546af26cc27fe2191ff7425c8aa001d52dc7ce996d0a7176cce69aecae2b7f2e09dccf38b6536dabc1c71af06f790f80b536c45de37a
7
- data.tar.gz: 443fe470e3d66f164a4f970ea4d0e3fdfdd41e42d98d5a05674ac9783e97f4c128ea5dfe97866ad034e4a71eb3cc412e4ec8cabfaf3449c7a727e903925328c2
6
+ metadata.gz: 12421cafc2a629e4d1621dea03a984bd1e88d6ae9418390bd9767533208827da379f3d858cfd1c1eb1a6b47d2bb1aebaecedce8ff810f7f4fdc1197473c276e3
7
+ data.tar.gz: 53b24b739628a3fdf69082ff652a1770d1acfa18c385152e61a2f9985faa82eac968f0c0be4ff36561957d9d8a3fa2bd53c7c671d2df02d0564ac10a96bc8eda
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1
4
+
5
+ - With `show_launcher = false` the panel now sits in the corner instead of
6
+ hovering 66px above a launcher bubble that isn't there.
7
+
8
+ ## 0.2.0
9
+
10
+ - Unread count badges on `data-livechat-open` elements, so launcher-less
11
+ setups (`show_launcher = false`) still surface waiting replies.
12
+ - Message prefill: `data-livechat-message="…"` on any opener, or
13
+ `window.Livechat.open("…")` — seeds the message box for contextual asks,
14
+ never overwriting a visitor's draft.
15
+
3
16
  ## 0.1.0
4
17
 
5
18
  - Initial release: floating chat widget (guests + signed-in visitors, one
data/README.md CHANGED
@@ -134,8 +134,14 @@ reply. Gated by `config.authorize_agent` (development-only until you set it).
134
134
 
135
135
  ## Widget API
136
136
 
137
- - `window.Livechat.open()` / `window.Livechat.close()`
138
- - Any element with `data-livechat-open` opens the panel on click
137
+ - `window.Livechat.open()` / `window.Livechat.close()`
138
+ `open("Hi, I need help with…")` prefills the message box (never over a
139
+ visitor's draft)
140
+ - Any element with `data-livechat-open` opens the panel on click; add
141
+ `data-livechat-message="…"` to prefill — great for contextual buttons
142
+ ("Request verification", "Ask about billing")
143
+ - While replies are unread, every `data-livechat-open` element carries a
144
+ small count badge — so hiding the launcher doesn't hide the answer
139
145
  - `<%= livechat_button %>` renders a plain, unstyled opener button
140
146
  - `config.show_launcher = false` hides the bubble entirely — bring your own
141
147
  entry point
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Livechat
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.1'
5
5
  end
@@ -83,6 +83,7 @@
83
83
  injectStyles();
84
84
  mount();
85
85
  applyBrandColor();
86
+ syncOpenerBadges(); // a Turbo visit brings fresh openers — restamp them
86
87
 
87
88
  if (sessionGet("livechat_open") === "1") {
88
89
  openPanel();
@@ -100,7 +101,7 @@
100
101
  if (!opener) return;
101
102
  event.preventDefault();
102
103
  event.stopPropagation();
103
- openPanel();
104
+ openPanel(opener.getAttribute("data-livechat-message"));
104
105
  }
105
106
 
106
107
  // --- mounting ---------------------------------------------------------------
@@ -115,6 +116,9 @@
115
116
  root = document.createElement("div");
116
117
  root.id = "lvc-root";
117
118
  if (config.rtl) root.setAttribute("dir", "rtl");
119
+ // No launcher bubble to clear — the panel sits in the corner instead of
120
+ // hovering 66px above nothing.
121
+ if (!config.launcher) root.className = "lvc-no-launcher";
118
122
 
119
123
  if (config.launcher) root.appendChild(buildLauncher());
120
124
  document.body.appendChild(root);
@@ -287,24 +291,33 @@
287
291
 
288
292
  // --- open / close -----------------------------------------------------------
289
293
 
290
- function openPanel() {
294
+ // prefill (optional string, from data-livechat-message or
295
+ // Livechat.open("…")) seeds the input — but never over a visitor's draft.
296
+ function openPanel(prefill) {
291
297
  mount();
292
- if (isOpen) return;
293
- isOpen = true;
294
- sessionSet("livechat_open", "1");
295
-
296
- // The panel is built once and then hidden/shown — closing must never
297
- // throw away the rendered thread or a half-written message.
298
- var panel = document.getElementById("lvc-panel");
299
- if (!panel) {
300
- panel = buildPanel();
301
- root.appendChild(panel);
298
+ if (!isOpen) {
299
+ isOpen = true;
300
+ sessionSet("livechat_open", "1");
301
+
302
+ // The panel is built once and then hidden/shown — closing must never
303
+ // throw away the rendered thread or a half-written message.
304
+ var panel = document.getElementById("lvc-panel");
305
+ if (!panel) {
306
+ panel = buildPanel();
307
+ root.appendChild(panel);
308
+ }
309
+ panel.hidden = false;
310
+ setUnread(0);
311
+ poll();
312
+ schedulePoll();
313
+ }
314
+ if (inputEl) {
315
+ if (typeof prefill === "string" && prefill && !inputEl.value.trim()) {
316
+ inputEl.value = prefill;
317
+ autogrow();
318
+ }
319
+ inputEl.focus();
302
320
  }
303
- panel.hidden = false;
304
- setUnread(0);
305
- poll();
306
- schedulePoll();
307
- if (inputEl) inputEl.focus();
308
321
  }
309
322
 
310
323
  function closePanel() {
@@ -356,10 +369,35 @@
356
369
  .catch(function () { fetching = false; });
357
370
  }
358
371
 
372
+ var unreadCount = 0;
373
+
359
374
  function setUnread(count) {
360
- if (!badgeEl) return;
361
- badgeEl.hidden = !(count > 0);
362
- badgeEl.textContent = count > 9 ? "9+" : String(count);
375
+ unreadCount = count;
376
+ if (badgeEl) {
377
+ badgeEl.hidden = !(count > 0);
378
+ badgeEl.textContent = count > 9 ? "9+" : String(count);
379
+ }
380
+ syncOpenerBadges();
381
+ }
382
+
383
+ // Hosts that hide the launcher still get an unread indicator: every
384
+ // data-livechat-open element carries a small count badge while replies
385
+ // are waiting, and loses it the moment the panel opens.
386
+ function syncOpenerBadges() {
387
+ var openers = document.querySelectorAll("[data-livechat-open]");
388
+ for (var i = 0; i < openers.length; i++) {
389
+ var badge = openers[i].querySelector(".lvc-opener-badge");
390
+ if (unreadCount > 0) {
391
+ if (!badge) {
392
+ badge = document.createElement("span");
393
+ badge.className = "lvc-opener-badge";
394
+ openers[i].appendChild(badge);
395
+ }
396
+ badge.textContent = unreadCount > 9 ? "9+" : String(unreadCount);
397
+ } else if (badge) {
398
+ badge.remove();
399
+ }
400
+ }
363
401
  }
364
402
 
365
403
  function syncEmailRow() {
@@ -509,7 +547,9 @@
509
547
  "border-radius:14px;box-shadow:0 12px 40px rgba(0,0,0,.28);display:flex;flex-direction:column;overflow:hidden}" +
510
548
  "#lvc-panel[hidden]{display:none}" +
511
549
  "#lvc-root[dir=rtl] #lvc-panel{right:auto;left:20px}" +
512
- "@media(max-width:480px){#lvc-panel{right:12px;bottom:80px}}" +
550
+ "#lvc-root.lvc-no-launcher #lvc-panel{bottom:20px;max-height:calc(100dvh - 40px)}" +
551
+ "@media(max-width:480px){#lvc-panel{right:12px;bottom:80px}" +
552
+ "#lvc-root.lvc-no-launcher #lvc-panel{right:12px;bottom:12px;max-height:calc(100dvh - 24px)}}" +
513
553
  "#lvc-header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;" +
514
554
  "padding:14px 16px;background:var(--lvc-accent);color:var(--lvc-accent-text)}" +
515
555
  "#lvc-header strong{display:block;font-size:15px}" +
@@ -553,7 +593,12 @@
553
593
  "color:var(--lvc-accent-text);width:40px;min-width:40px;height:40px;align-self:flex-end;" +
554
594
  "display:flex;align-items:center;justify-content:center;cursor:pointer}" +
555
595
  "#lvc-form button:disabled{opacity:.6;cursor:default}" +
556
- "#lvc-root[dir=rtl] #lvc-form button svg{transform:scaleX(-1)}";
596
+ "#lvc-root[dir=rtl] #lvc-form button svg{transform:scaleX(-1)}" +
597
+ // Lives in host DOM (on data-livechat-open elements), so no #lvc-root prefix.
598
+ ".lvc-opener-badge{display:inline-flex;min-width:18px;height:18px;padding:0 5px;" +
599
+ "margin-inline-start:6px;border-radius:999px;background:#dc2626;color:#fff;" +
600
+ "font-size:11px;font-weight:700;line-height:18px;align-items:center;" +
601
+ "justify-content:center;vertical-align:middle}";
557
602
 
558
603
  var style = document.createElement("style");
559
604
  style.id = "lvc-styles";
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: livechat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov
@@ -118,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
118
  - !ruby/object:Gem::Version
119
119
  version: '0'
120
120
  requirements: []
121
- rubygems_version: 4.0.6
121
+ rubygems_version: 3.6.9
122
122
  specification_version: 4
123
123
  summary: 'Open-source live chat for Rails: a drop-in support widget plus a team inbox,
124
124
  in your own database.'