From 56ab1368b1582b83b2f9eaac994a71aecd69a10e Mon Sep 17 00:00:00 2001 From: Tudor Sitaru Date: Tue, 19 May 2026 09:40:23 +0100 Subject: [PATCH] feat(mobile): compare table scroll fade and native share sheet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MOB-14: PerformanceChart and ComparisonChart both already configure legend position 'top' — no change needed. MOB-15: Compare's detailedTable is 774px wide inside an overflow-x: auto wrapper. Add a right-edge mask-image fade at ≤640px so phone users see the table extends past the viewport. MOB-16: ComparisonView's Share button previously did clipboard-only. Prefer navigator.share when available (iOS/Android native sheet) so users can send straight to Messages / WhatsApp / Mail / etc. Fall back to clipboard with the existing "Copied!" toast otherwise. User cancellations swallow silently; only real errors trigger fallback. Co-Authored-By: Claude Opus 4.7 --- .../components/ComparisonView.module.css | 9 ++++++++ nextjs-app/components/ComparisonView.tsx | 23 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/nextjs-app/components/ComparisonView.module.css b/nextjs-app/components/ComparisonView.module.css index b820ad3..3629942 100644 --- a/nextjs-app/components/ComparisonView.module.css +++ b/nextjs-app/components/ComparisonView.module.css @@ -306,6 +306,15 @@ -webkit-overflow-scrolling: touch; } +/* Right-edge fade so phone users see the comparison table scrolls. + Otherwise the wider-than-viewport table silently clips. */ +@media (max-width: 640px) { + .tableWrapper { + -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); + mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); + } +} + .comparisonTable { width: 100%; border-collapse: separate; diff --git a/nextjs-app/components/ComparisonView.tsx b/nextjs-app/components/ComparisonView.tsx index abacf69..f97e679 100644 --- a/nextjs-app/components/ComparisonView.tsx +++ b/nextjs-app/components/ComparisonView.tsx @@ -157,8 +157,29 @@ export function ComparisonView({ }; const handleShare = async () => { + const url = window.location.href; + const count = selectedSchools.length; + const shareData = { + title: 'School comparison · SchoolCompare', + text: count > 0 + ? `Comparing ${count} school${count === 1 ? '' : 's'} on SchoolCompare` + : 'SchoolCompare', + url, + }; + // Prefer the native share sheet on platforms that support it (iOS / Android). + // canShare is feature-detected because Safari iOS exposes share() but + // some configurations refuse the payload. + if (typeof navigator !== 'undefined' && navigator.share && (!navigator.canShare || navigator.canShare(shareData))) { + try { + await navigator.share(shareData); + return; + } catch (err) { + // User cancelled — bail silently. Any other error falls through to clipboard. + if ((err as DOMException)?.name === 'AbortError') return; + } + } try { - await navigator.clipboard.writeText(window.location.href); + await navigator.clipboard.writeText(url); setShareConfirm(true); setTimeout(() => setShareConfirm(false), 2000); } catch { /* fallback: do nothing */ }