livechat 0.3.2 → 0.3.3

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: 5c30e19e783526543e297b77114a6e1d823799c22aca38a2048fc4bb476feb3f
4
- data.tar.gz: 6746d78ed02867bd663d2d34c636f68530cc3b818678c1b4b5cd79cb70781eb6
3
+ metadata.gz: 3263710888d695851123f5afeffc0dd73985901e0ad559f5d04562a59c411537
4
+ data.tar.gz: 9db6f6b5df671b05b594dd174180cc54c2988695aac907ab3f7f3e38c32df732
5
5
  SHA512:
6
- metadata.gz: 651eedbae0aabd2570b1690cb3f06e961abadbe3922670ed254b4c108f2553341e6a27e43e35b565c61004dd7ee6df751b38d62b6bd996aa1c97f50f377b71ef
7
- data.tar.gz: ec6e305609121daf2182095c44bc2bccc098bee0a6d199ba54c46d3b8665c8de4c749eb6adfa34c5884992318701d157a8146a7155c7d9039a782456b7afd446
6
+ metadata.gz: c5ae2806f1b7f1c8d08b14bc61043c8fe23159873c260a75c3ed6a5b263bc2ada95630af19a323a64c3de774d107e17aef8094454535460bba76cb10c91d5846
7
+ data.tar.gz: cda4c08755760e0a1a1996be4aa5165e8d7ddf10ca86cd0362905b0d02c5ee1dd8e73f3a2f8dcec3cbb9b3b68a23418d3898f9bca48146365799544ca029d332
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.3
4
+
5
+ - Accessibility: on phones (where the panel is a full-screen modal) focus is
6
+ trapped inside it and page scroll is locked behind it; closing restores
7
+ focus to whatever opened the chat. Errors and the email-saved note are now
8
+ announced to screen readers. The desktop popover deliberately stays
9
+ non-modal — you can read the page while chatting.
10
+
3
11
  ## 0.3.2
4
12
 
5
13
  - The chat panel is full-screen on phones (≤480px) — a conversation is an
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Livechat
4
- VERSION = '0.3.2'
4
+ VERSION = '0.3.3'
5
5
  end
@@ -33,6 +33,11 @@
33
33
  var emailRowEl = null;
34
34
  var errorEl = null;
35
35
  var isOpen = false;
36
+ var lastFocused = null;
37
+ // On phones the panel is a full-screen modal (focus trapped, page scroll
38
+ // locked); on desktop it stays a non-modal popover you can chat alongside.
39
+ var mobileModal = window.matchMedia ? window.matchMedia("(max-width: 480px)") : null;
40
+ var savedOverflow = null;
36
41
  // The id of the newest message actually RENDERED into the list — never
37
42
  // advanced by background polls, so reopening the panel refetches exactly
38
43
  // the messages it hasn't shown yet.
@@ -112,6 +117,9 @@
112
117
  isOpen = false;
113
118
  lastRenderedId = 0;
114
119
  lastAuthorKey = null;
120
+ // A Turbo body swap removes the panel without closePanel() running; the
121
+ // documentElement (and any scroll lock on it) survives the swap.
122
+ unlockScroll();
115
123
 
116
124
  root = document.createElement("div");
117
125
  root.id = "lvc-root";
@@ -173,6 +181,7 @@
173
181
  panel.id = "lvc-panel";
174
182
  panel.setAttribute("role", "dialog");
175
183
  panel.setAttribute("aria-label", config.appName);
184
+ panel.addEventListener("keydown", trapFocus);
176
185
 
177
186
  var header = document.createElement("div");
178
187
  header.id = "lvc-header";
@@ -206,6 +215,9 @@
206
215
 
207
216
  errorEl = document.createElement("div");
208
217
  errorEl.id = "lvc-error";
218
+ // The alert region exists before any text lands in it, so screen
219
+ // readers announce the message instead of a silent visual update.
220
+ errorEl.setAttribute("role", "alert");
209
221
  errorEl.hidden = true;
210
222
  panel.appendChild(errorEl);
211
223
 
@@ -252,6 +264,7 @@
252
264
  function buildEmailRow() {
253
265
  var row = document.createElement("form");
254
266
  row.id = "lvc-email";
267
+ row.setAttribute("aria-live", "polite"); // "we'll also reply by email" is a status
255
268
  row.hidden = true;
256
269
 
257
270
  var label = document.createElement("span");
@@ -297,6 +310,7 @@
297
310
  mount();
298
311
  if (!isOpen) {
299
312
  isOpen = true;
313
+ lastFocused = document.activeElement;
300
314
  sessionSet("livechat_open", "1");
301
315
 
302
316
  // The panel is built once and then hidden/shown — closing must never
@@ -307,6 +321,8 @@
307
321
  root.appendChild(panel);
308
322
  }
309
323
  panel.hidden = false;
324
+ panel.setAttribute("aria-modal", isMobileModal() ? "true" : "false");
325
+ if (isMobileModal()) lockScroll();
310
326
  setUnread(0);
311
327
  poll();
312
328
  schedulePoll();
@@ -325,9 +341,45 @@
325
341
  sessionSet("livechat_open", "");
326
342
  var panel = document.getElementById("lvc-panel");
327
343
  if (panel) panel.hidden = true;
344
+ unlockScroll();
345
+ if (lastFocused && document.contains(lastFocused)) lastFocused.focus();
328
346
  schedulePoll();
329
347
  }
330
348
 
349
+ function isMobileModal() {
350
+ return !!(mobileModal && mobileModal.matches);
351
+ }
352
+
353
+ // Tab cycles inside the panel only while it is the full-screen mobile
354
+ // modal; the desktop popover deliberately lets focus reach the page.
355
+ function trapFocus(event) {
356
+ if (event.key !== "Tab" || !isMobileModal()) return;
357
+ var panel = document.getElementById("lvc-panel");
358
+ var focusable = panel.querySelectorAll("button, [href], input, textarea, select");
359
+ if (!focusable.length) return;
360
+ var first = focusable[0];
361
+ var last = focusable[focusable.length - 1];
362
+ if (event.shiftKey && document.activeElement === first) {
363
+ event.preventDefault();
364
+ last.focus();
365
+ } else if (!event.shiftKey && document.activeElement === last) {
366
+ event.preventDefault();
367
+ first.focus();
368
+ }
369
+ }
370
+
371
+ function lockScroll() {
372
+ if (savedOverflow !== null) return;
373
+ savedOverflow = document.documentElement.style.overflow;
374
+ document.documentElement.style.overflow = "hidden";
375
+ }
376
+
377
+ function unlockScroll() {
378
+ if (savedOverflow === null) return;
379
+ document.documentElement.style.overflow = savedOverflow;
380
+ savedOverflow = null;
381
+ }
382
+
331
383
  // --- polling ----------------------------------------------------------------
332
384
 
333
385
  function schedulePoll() {
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.3.2
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov