Tristan Leduc Claude Opus 4.8 (1M context) commited on
Commit
d34ffe8
·
1 Parent(s): c4b5726

Mobile UI: bound the autocomplete dropdown + denser drawer + keyboard handling

Browse files

The "typing narrows the menu" issue: the start/destination autocomplete list
had max-height:none, so a 9-item dropdown grew unbounded and buried the rest of
the menu (Destination/Mode/sliders), with its tail clipped by the CTA. Cap it at
min(46vh,300px) with internal scroll so it stays compact.

Also for mobile readability:
- Tighten drawer density (panel padding, control margins, chip gap) so more of
the menu is visible at once — the brand + vibe header no longer clip at the top.
- Add a visualViewport handler: when the on-screen keyboard opens, size the
bottom drawer to the visible area above it and scroll the focused field into
view (dvh alone gets crushed on Android / overlaid on iOS).

Verified in the mobile preview (375px): dropdown now 300px + scrollable; full
menu (vibe → Plan button) scrolls cleanly; no console errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. src/discoverroute/ui/shell.py +37 -1
src/discoverroute/ui/shell.py CHANGED
@@ -88,7 +88,9 @@ body{ font-family:'DM Sans',ui-sans-serif,system-ui,sans-serif; color:var(--dr-i
88
  .combo{ position:relative; }
89
  .combo-list{ position:absolute; z-index:60; left:0; right:0; top:calc(100% + 5px);
90
  background:var(--dr-paper); border:1.5px solid var(--dr-line); border-radius:16px;
91
- box-shadow:0 18px 38px -16px rgba(43,38,32,.45); overflow:hidden; display:none; padding:5px; }
 
 
92
  .combo-list.open{ display:block; animation:drPop .16s var(--dr-spring); }
93
  @keyframes drPop{ from{ transform:translateY(-4px); opacity:.4; } to{ transform:none; opacity:1; } }
94
  .combo-list div{ padding:9px 11px; font-size:13.5px; cursor:pointer; border-radius:11px;
@@ -270,6 +272,11 @@ details.dr-collapse .collapse-body{ padding:13px 15px; }
270
  transform:translateY(110%); transition:transform .32s var(--dr-spring);
271
  padding-top:8px; }
272
  .left-panel.open{ transform:translateY(0); }
 
 
 
 
 
273
  .fab{ display:grid; place-items:center; position:fixed; right:18px; bottom:18px; z-index:210;
274
  width:62px; height:62px; border-radius:50%; border:none; cursor:pointer;
275
  background:var(--dr-coral); color:#fff; font-size:15px; font-family:'Fredoka',sans-serif;
@@ -804,6 +811,35 @@ const closeDrawer = () => $("left-panel").classList.remove("open");
804
  $("fab").addEventListener("click", openDrawer);
805
  $("drawer-close").addEventListener("click", closeDrawer);
806
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
807
  /* ---------- floating results sheet (collapsible overlay) ---------- */
808
  function showSheet() { const s = $("results-sheet"); if (s) { s.hidden = false; s.classList.remove("collapsed"); } }
809
  function hideSheet() { const s = $("results-sheet"); if (s) s.hidden = true; }
 
88
  .combo{ position:relative; }
89
  .combo-list{ position:absolute; z-index:60; left:0; right:0; top:calc(100% + 5px);
90
  background:var(--dr-paper); border:1.5px solid var(--dr-line); border-radius:16px;
91
+ box-shadow:0 18px 38px -16px rgba(43,38,32,.45); display:none; padding:5px;
92
+ max-height:min(46vh,300px); overflow-y:auto; overflow-x:hidden;
93
+ overscroll-behavior:contain; }
94
  .combo-list.open{ display:block; animation:drPop .16s var(--dr-spring); }
95
  @keyframes drPop{ from{ transform:translateY(-4px); opacity:.4; } to{ transform:none; opacity:1; } }
96
  .combo-list div{ padding:9px 11px; font-size:13.5px; cursor:pointer; border-radius:11px;
 
272
  transform:translateY(110%); transition:transform .32s var(--dr-spring);
273
  padding-top:8px; }
274
  .left-panel.open{ transform:translateY(0); }
275
+ /* denser drawer so more of the menu is visible in the cramped mobile height */
276
+ .panel-scroll{ padding:12px 16px 8px; }
277
+ .dr-control{ margin-bottom:12px; }
278
+ .brand{ margin-bottom:10px; }
279
+ .vibe-chips{ gap:7px; }
280
  .fab{ display:grid; place-items:center; position:fixed; right:18px; bottom:18px; z-index:210;
281
  width:62px; height:62px; border-radius:50%; border:none; cursor:pointer;
282
  background:var(--dr-coral); color:#fff; font-size:15px; font-family:'Fredoka',sans-serif;
 
811
  $("fab").addEventListener("click", openDrawer);
812
  $("drawer-close").addEventListener("click", closeDrawer);
813
 
814
+ /* Keep the bottom drawer usable when the on-screen keyboard opens. On phones the
815
+ keyboard shrinks the *visual* viewport (and on iOS overlays a fixed element)
816
+ so a dvh-sized drawer gets crushed or hidden. We size the drawer to the
817
+ visible area above the keyboard and scroll the focused field into view. */
818
+ (function () {
819
+ const vv = window.visualViewport;
820
+ if (!vv) return;
821
+ const panel = $("left-panel");
822
+ const fit = () => {
823
+ if (window.innerWidth > 768 || !panel.classList.contains("open")) {
824
+ panel.style.maxHeight = ""; return; // desktop / closed: CSS rules
825
+ }
826
+ panel.style.maxHeight = Math.round(vv.height * 0.94) + "px";
827
+ };
828
+ vv.addEventListener("resize", fit);
829
+ vv.addEventListener("scroll", fit);
830
+ document.addEventListener("focusin", (e) => {
831
+ if (window.innerWidth > 768) return;
832
+ const field = e.target.closest && e.target.closest(".left-panel input, .left-panel textarea");
833
+ if (!field) return;
834
+ openDrawer(); fit();
835
+ // let the keyboard animate in, then center the field within the scroll area
836
+ setTimeout(() => field.scrollIntoView({ block: "center", behavior: "smooth" }), 280);
837
+ });
838
+ document.addEventListener("focusout", () => {
839
+ if (window.innerWidth <= 768) setTimeout(fit, 100);
840
+ });
841
+ })();
842
+
843
  /* ---------- floating results sheet (collapsible overlay) ---------- */
844
  function showSheet() { const s = $("results-sheet"); if (s) { s.hidden = false; s.classList.remove("collapsed"); } }
845
  function hideSheet() { const s = $("results-sheet"); if (s) s.hidden = true; }