sara.mesquita commited on
Commit
5e6a147
Β·
1 Parent(s): f61af68

feat: 3-dot menu with download option on gallery photos

Browse files
Files changed (2) hide show
  1. index.html +56 -2
  2. static/style.css +9 -0
index.html CHANGED
@@ -367,6 +367,14 @@
367
  </div>
368
  </div>
369
 
 
 
 
 
 
 
 
 
370
  <!-- ══ BOTTOM NAV ══ -->
371
  <nav id="bottom-nav">
372
  <button class="nav-btn active" data-screen="map"><span class="nav-icon"><i data-lucide="map"></i></span><span>Map</span></button>
@@ -723,8 +731,17 @@
723
  const locStr = (s.latitude && s.longitude) ? `${s.latitude.toFixed(3)}, ${s.longitude.toFixed(3)}` : 'Location not recorded';
724
  return `
725
  <div class="gallery-card">
726
- <img class="gallery-card-img" src="${s.photo_url}" alt="sighting"
727
- onerror="this.style.background='#eee';this.src=''"/>
 
 
 
 
 
 
 
 
 
728
  <div class="gallery-card-info">
729
  <div class="gallery-card-date">
730
  <svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
@@ -1060,6 +1077,43 @@
1060
  updateSubmitBtn();
1061
  }
1062
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1063
  // ── HELP SHEET ────────────────────────────────────────────────────────────
1064
  let helpAnimal = null;
1065
 
 
367
  </div>
368
  </div>
369
 
370
+ <!-- ══ PHOTO CONTEXT MENU ══ -->
371
+ <div id="photo-menu" class="photo-menu hidden">
372
+ <button id="photo-menu-download">
373
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
374
+ Download photo
375
+ </button>
376
+ </div>
377
+
378
  <!-- ══ BOTTOM NAV ══ -->
379
  <nav id="bottom-nav">
380
  <button class="nav-btn active" data-screen="map"><span class="nav-icon"><i data-lucide="map"></i></span><span>Map</span></button>
 
731
  const locStr = (s.latitude && s.longitude) ? `${s.latitude.toFixed(3)}, ${s.longitude.toFixed(3)}` : 'Location not recorded';
732
  return `
733
  <div class="gallery-card">
734
+ <div class="gallery-card-img-wrap">
735
+ <img class="gallery-card-img" src="${s.photo_url}" alt="sighting"
736
+ onerror="this.style.background='#eee';this.src=''"/>
737
+ <button class="gallery-card-menu-btn" data-url="${s.photo_url}" data-date="${dateStr}" aria-label="Options">
738
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
739
+ <circle cx="12" cy="5" r="1.2" fill="currentColor" stroke="none"/>
740
+ <circle cx="12" cy="12" r="1.2" fill="currentColor" stroke="none"/>
741
+ <circle cx="12" cy="19" r="1.2" fill="currentColor" stroke="none"/>
742
+ </svg>
743
+ </button>
744
+ </div>
745
  <div class="gallery-card-info">
746
  <div class="gallery-card-date">
747
  <svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
 
1077
  updateSubmitBtn();
1078
  }
1079
 
1080
+ // ── PHOTO CONTEXT MENU ────────────────────────────────────────────────────
1081
+ let photoMenuUrl = null;
1082
+ const photoMenu = document.getElementById('photo-menu');
1083
+
1084
+ function openPhotoMenu(btn) {
1085
+ photoMenuUrl = btn.dataset.url;
1086
+ const rect = btn.getBoundingClientRect();
1087
+ const appRect = document.getElementById('app').getBoundingClientRect();
1088
+ photoMenu.classList.remove('hidden');
1089
+ // Position below the button, clamped inside app
1090
+ let top = rect.bottom - appRect.top + 4;
1091
+ let left = rect.right - appRect.left - photoMenu.offsetWidth;
1092
+ if (left < 8) left = 8;
1093
+ photoMenu.style.top = top + 'px';
1094
+ photoMenu.style.left = left + 'px';
1095
+ }
1096
+
1097
+ function closePhotoMenu() { photoMenu.classList.add('hidden'); photoMenuUrl = null; }
1098
+
1099
+ document.getElementById('app').addEventListener('click', e => {
1100
+ const btn = e.target.closest('.gallery-card-menu-btn');
1101
+ if (btn) { e.stopPropagation(); openPhotoMenu(btn); return; }
1102
+ if (!photoMenu.contains(e.target)) closePhotoMenu();
1103
+ });
1104
+
1105
+ document.getElementById('photo-menu-download').addEventListener('click', () => {
1106
+ if (!photoMenuUrl) return;
1107
+ const a = document.createElement('a');
1108
+ a.href = photoMenuUrl;
1109
+ a.download = 'pawmap-photo.jpg';
1110
+ a.target = '_blank';
1111
+ document.body.appendChild(a);
1112
+ a.click();
1113
+ document.body.removeChild(a);
1114
+ closePhotoMenu();
1115
+ });
1116
+
1117
  // ── HELP SHEET ────────────────────────────────────────────────────────────
1118
  let helpAnimal = null;
1119
 
static/style.css CHANGED
@@ -304,6 +304,15 @@
304
  /* Adopt button */
305
  .btn-adopt svg { width: 18px; height: 18px; stroke: #fff; fill: #fff; }
306
 
 
 
 
 
 
 
 
 
 
307
  /* ── Helped banner & events ── */
308
  #profile-helped-banner { display:flex; align-items:center; gap:8px; background:var(--green-soft); border:1px solid var(--green-pale); border-radius:10px; padding:10px 14px; margin:10px 0 4px; font-size:13px; color:var(--green-dark); font-weight:500; }
309
  #profile-helped-banner i { flex-shrink:0; }
 
304
  /* Adopt button */
305
  .btn-adopt svg { width: 18px; height: 18px; stroke: #fff; fill: #fff; }
306
 
307
+ /* ── Gallery card menu ── */
308
+ .gallery-card-img-wrap { position:relative; }
309
+ .gallery-card-menu-btn { position:absolute; top:6px; right:6px; width:28px; height:28px; background:rgba(0,0,0,.45); border:none; border-radius:50%; display:flex; align-items:center; justify-content:center; cursor:pointer; color:#fff; padding:0; }
310
+ .gallery-card-menu-btn svg { width:14px; height:14px; }
311
+ .photo-menu { position:absolute; z-index:800; background:#fff; border-radius:10px; box-shadow:0 4px 20px rgba(0,0,0,.15); overflow:hidden; min-width:160px; }
312
+ #photo-menu-download { display:flex; align-items:center; gap:10px; padding:13px 16px; font-size:14px; font-weight:500; color:var(--text); background:none; border:none; cursor:pointer; width:100%; }
313
+ #photo-menu-download:active { background:var(--bg); }
314
+ #photo-menu-download svg { width:16px; height:16px; flex-shrink:0; color:var(--text-muted); }
315
+
316
  /* ── Helped banner & events ── */
317
  #profile-helped-banner { display:flex; align-items:center; gap:8px; background:var(--green-soft); border:1px solid var(--green-pale); border-radius:10px; padding:10px 14px; margin:10px 0 4px; font-size:13px; color:var(--green-dark); font-weight:500; }
318
  #profile-helped-banner i { flex-shrink:0; }