PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.3
Yatra – Travel Booking & Tour Operator Software v3.0.3
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
yatra / templates / partials / single-trip / sticky-sidebar.php

sticky-sidebar.php in Yatra – Travel Booking & Tour Operator Software 3.0.3, at templates/partials/single-trip/sticky-sidebar.php

594 lines 25.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Mobile Sticky Sidebar Template
4 *
5 * Displays booking card in sticky bottom position on mobile devices
6 * SEO-friendly approach - separate template, no content duplication
7 *
8 * @package Yatra
9 */
10
11 if (!defined('ABSPATH')) {
12 exit;
13 }
14
15 // Expected variables: $trip, $has_availability, $has_traveler_pricing, $base_price, $pricing_type
16
17 $booking_mode = $trip->getBookingMode();
18
19 // Calculate pricing - use pre-computed values from SingleTripController
20 $pricing = [
21 'has_price' => false,
22 'current_price' => '',
23 'original_price' => '',
24 'price_prefix' => '',
25 'has_discount' => false,
26 'raw_current_price' => 0,
27 'raw_original_price' => 0,
28 'is_traveler_based' => $has_traveler_pricing
29 ];
30
31 $discount = [
32 'has_discount' => false,
33 'discount_text' => '',
34 'discount_percentage' => 0
35 ];
36
37 // Use effective_price_min computed in SingleTripController
38 $effective_min = (float) ($trip->effective_price_min ?? 0);
39 $original_min = (float) ($trip->min_category_original_price ?? 0);
40 $max_discount_pct = (int) ($trip->max_discount_percentage ?? 0);
41
42 if ($effective_min > 0) {
43 $pricing['has_price'] = true;
44 $pricing['raw_current_price'] = $effective_min;
45 $pricing['current_price'] = yatra_format_price($effective_min);
46
47 if ($has_traveler_pricing || $has_availability) {
48 $pricing['price_prefix'] = __('From ', 'yatra');
49 }
50
51 if ($max_discount_pct > 0 && $original_min > $effective_min) {
52 $pricing['has_discount'] = true;
53 $pricing['raw_original_price'] = $original_min;
54 $pricing['original_price'] = yatra_format_price($original_min);
55
56 $discount['has_discount'] = true;
57 $discount['discount_percentage'] = $max_discount_pct;
58 $discount['discount_text'] = sprintf(__('Up to %d%%', 'yatra'), $max_discount_pct);
59 }
60 } else {
61 $original = (float) ($trip->getOriginalPrice() ?? 0);
62 $discounted = (float) ($trip->discounted_price ?? 0);
63
64 if ($discounted > 0) {
65 $current = $discounted;
66 } elseif ($original > 0) {
67 $current = $original;
68 } elseif (!empty($trip->getSalePrice()) && (float)$trip->getSalePrice() > 0) {
69 $current = (float) $trip->getSalePrice();
70 } else {
71 $current = 0;
72 }
73
74 if ($current > 0) {
75 $pricing['has_price'] = true;
76 $pricing['raw_current_price'] = $current;
77 $pricing['current_price'] = yatra_format_price($current);
78
79 if ($current < $original && $original > 0) {
80 $pct = round((($original - $current) / $original) * 100);
81
82 // Only show discount if percentage is greater than 0
83 if ($pct > 0) {
84 $pricing['has_discount'] = true;
85 $pricing['raw_original_price'] = $original;
86 $pricing['original_price'] = yatra_format_price($original);
87
88 $discount['has_discount'] = true;
89 $discount['discount_percentage'] = $pct;
90 $discount['discount_text'] = sprintf(__('%d%%', 'yatra'), $pct);
91 }
92 }
93 }
94 }
95 // Pro: check whether this trip has booking disabled (enquiry-only mode)
96 $booking_disabled = method_exists($trip, 'isBookingDisabled') && $trip->isBookingDisabled();
97 ?>
98 <?php if ($booking_disabled): ?>
99 <div class="yatra-mobile-sticky-sidebar yatra-enquiry-only-sticky" id="yatra-mobile-sticky-sidebar">
100 <div class="yatra-mobile-sticky-content">
101 <div class="yatra-mobile-row-1" style="justify-content:space-between;align-items:center;">
102 <div style="font-size:0.82rem;color:#1a5fa8;padding:0 4px;">
103 <?php esc_html_e('Enquiry only — no direct booking', 'yatra'); ?>
104 </div>
105 <button type="button"
106 class="yatra-mobile-book-btn"
107 style="background:var(--yatra-primary,#3b82f6);"
108 onclick="window.YatraEnquiry?.open?.(); return false;">
109 <?php esc_html_e('Send Enquiry', 'yatra'); ?>
110 </button>
111 </div>
112 </div>
113 </div>
114 <?php else: ?>
115 <div class="yatra-mobile-sticky-sidebar" id="yatra-mobile-sticky-sidebar"
116 data-booking-mode="<?php echo esc_attr($booking_mode); ?>">
117 <!-- Two-Row Layout -->
118 <div class="yatra-mobile-sticky-content">
119
120 <!-- Row 1: Compact fields + close (clean grid layout) -->
121 <div class="yatra-mobile-row-1">
122
123 <div class="yatra-mobile-date-section">
124 <div class="yatra-mobile-field-btn yatra-mobile-date-btn"
125 role="button"
126 tabindex="0"
127 aria-label="<?php echo esc_attr__('Select date', 'yatra'); ?>">
128 <span class="yatra-mobile-field-icon" aria-hidden="true">
129 <?php echo yatra_svg_icon('calendar', 'yatra-mobile-icon'); ?>
130 </span>
131 <input type="text"
132 id="mobile_travel_date"
133 name="travel_date"
134 class="yatra-mobile-datepicker-input yatra-mobile-datepicker"
135 placeholder="<?php esc_attr_e('Date', 'yatra'); ?>"
136 data-min-date="<?php echo esc_attr($trip->getAvailableFrom() ?: date('Y-m-d')); ?>"
137 data-max-date="<?php echo esc_attr($trip->getAvailableTo() ?: ''); ?>"
138 readonly
139 required>
140 <svg class="yatra-mobile-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
141 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
142 </svg>
143 </div>
144 </div>
145
146 <div class="yatra-mobile-travelers-section">
147 <?php if ($has_traveler_pricing): ?>
148 <?php
149 // Reuse the same traveler selector component as desktop, but with mobile-specific IDs
150 $traveler_data = \Yatra\Controllers\SingleTripController::prepareTravelerSelectorData($trip, 'sidebar');
151 $rows = $traveler_data['traveler_rows'] ?? [];
152
153 // Prefix IDs so they don't collide with the desktop sidebar.
154 foreach ($rows as &$row) {
155 if (!empty($row['minus_attrs']['data-target'])) {
156 $row['minus_attrs']['data-target'] = 'mobile_' . $row['minus_attrs']['data-target'];
157 }
158 if (!empty($row['plus_attrs']['data-target'])) {
159 $row['plus_attrs']['data-target'] = 'mobile_' . $row['plus_attrs']['data-target'];
160 }
161 if (!empty($row['input_attrs']['id'])) {
162 $row['input_attrs']['id'] = 'mobile_' . $row['input_attrs']['id'];
163 }
164 if (!empty($row['input_attrs']['name'])) {
165 $row['input_attrs']['name'] = 'mobile_' . $row['input_attrs']['name'];
166 }
167 }
168 unset($row);
169
170 // Setup variables for traveler-selector.php
171 $root_id = 'mobile-participants';
172 $root_class = 'yatra-booking-field-select yatra-participants-select yatra-mobile-participants';
173 $container_attrs = [];
174
175 $display_id = 'mobile-participants-display';
176 $display_class = 'yatra-participants-display yatra-mobile-participants-display';
177 $display_attrs = [];
178
179 $dropdown_id = 'mobile-quantity-selector';
180 $dropdown_class = 'yatra-booking-quantity-selector yatra-mobile-quantity-selector';
181 $dropdown_attrs = [];
182
183 $display_text = $traveler_data['traveler_display_text'] ?? __('Select travelers', 'yatra');
184 $icon_html = yatra_svg_icon('users', 'yatra-mobile-icon');
185
186 include YATRA_PLUGIN_PATH . 'templates/partials/traveler-selector.php';
187 ?>
188 <?php else: ?>
189 <!-- Regular Pricing -->
190 <div class="yatra-mobile-booking-field">
191 <div class="yatra-mobile-field-icon">
192 <?php echo yatra_svg_icon('users', 'yatra-mobile-icon'); ?>
193 </div>
194 <div class="yatra-mobile-quantity-controls">
195 <button type="button" class="yatra-mobile-quantity-btn yatra-mobile-quantity-minus" data-target="mobile_num_travelers">
196 <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
197 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12H4"/>
198 </svg>
199 </button>
200 <input type="number"
201 id="mobile_num_travelers"
202 name="num_travelers"
203 class="yatra-mobile-quantity-input"
204 value="1"
205 min="<?php echo esc_attr($trip->getMinTravelers() ?: 1); ?>"
206 max="<?php echo esc_attr($trip->getMaxTravelers() ?: 20); ?>"
207 readonly
208 data-price="<?php echo esc_attr($base_price); ?>">
209 <button type="button" class="yatra-mobile-quantity-btn yatra-mobile-quantity-plus" data-target="mobile_num_travelers">
210 <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
211 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
212 </svg>
213 </button>
214 </div>
215 </div>
216 <?php endif; ?>
217 </div>
218 </div>
219
220 <!-- Close Button (fixed in top-right of sticky container) -->
221 <div class="yatra-mobile-action-buttons">
222 <button type="button"
223 class="yatra-mobile-close-btn"
224 aria-label="<?php echo esc_attr__('Close', 'yatra'); ?>"
225 onclick="yatraCloseMobileSidebar()">
226 <span class="yatra-mobile-close-fallback" aria-hidden="true">&times;</span>
227 </button>
228 </div>
229
230 <!-- Row 2: Primary Actions -->
231 <div class="yatra-mobile-row-2">
232 <button type="button" class="yatra-mobile-check-btn" id="mobile-check-availability-btn" data-trip-id="<?php echo esc_attr($trip->getId()); ?>">
233 <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
234 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
235 </svg>
236 <span><?php echo esc_html__('Check', 'yatra'); ?></span>
237 </button>
238 <button type="button" class="yatra-mobile-enquire-btn" id="mobile-open-enquiry-modal" onclick="if (window.enquiryModal) { window.enquiryModal.open(); } else if (window.YatraEnquiry) { window.YatraEnquiry.open(); } return false;">
239 <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
240 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"/>
241 </svg>
242 <span><?php echo esc_html__('Enquire', 'yatra'); ?></span>
243 </button>
244 </div>
245
246 </div>
247
248 </div>
249 <?php endif; // end else (booking not disabled) ?>
250
251 <!-- JavaScript for mobile sidebar interactions -->
252 <script>
253 function yatraScrollToBooking() {
254 const bookingForm = document.querySelector('.yatra-booking-form');
255 if (bookingForm) {
256 bookingForm.scrollIntoView({ behavior: 'smooth', block: 'center' });
257 } else {
258 // Fallback to main sidebar
259 const sidebar = document.querySelector('.yatra-trip-sidebar');
260 if (sidebar) {
261 sidebar.scrollIntoView({ behavior: 'smooth', block: 'start' });
262 }
263 }
264 }
265
266 function yatraScrollToContact() {
267 const contactSection = document.querySelector('#contact') || document.querySelector('.yatra-contact-form');
268 if (contactSection) {
269 contactSection.scrollIntoView({ behavior: 'smooth', block: 'center' });
270 }
271 }
272
273
274 function yatraCloseMobileSidebar() {
275 const sidebar = document.getElementById('yatra-mobile-sticky-sidebar');
276 if (sidebar) {
277 sidebar.classList.add('user-closed');
278
279 // Save closed state in sessionStorage (persists until page reload)
280 sessionStorage.setItem('yatra-mobile-sidebar-closed', 'true');
281
282 // Hide sidebar with animation
283 setTimeout(() => {
284 sidebar.style.transform = 'translateY(100%)';
285 sidebar.style.opacity = '0';
286 }, 100);
287 }
288 }
289
290 // Auto-show on page load (reset closed state)
291 document.addEventListener('DOMContentLoaded', function() {
292 const sidebar = document.getElementById('yatra-mobile-sticky-sidebar');
293
294 if (sidebar) {
295 // Clear any previous closed state on page load
296 sessionStorage.removeItem('yatra-mobile-sidebar-closed');
297
298 // Ensure sidebar is visible on page load (CSS will handle responsive)
299 sidebar.classList.remove('user-closed');
300 sidebar.classList.remove('hidden');
301 }
302
303 // Initialize mobile booking form functionality
304 initializeMobileBookingForm();
305 });
306
307 function initializeMobileBookingForm() {
308 // Mobile quantity controls
309 const mobileMinusBtns = document.querySelectorAll('.yatra-mobile-quantity-minus');
310 const mobilePlusBtns = document.querySelectorAll('.yatra-mobile-quantity-plus');
311
312 mobileMinusBtns.forEach(btn => {
313 btn.addEventListener('click', function() {
314 const targetId = this.getAttribute('data-target');
315 const input = document.getElementById(targetId);
316 if (input) {
317 const current = parseInt(input.value) || 1;
318 const min = parseInt(input.getAttribute('min')) || 1;
319 if (current > min) {
320 input.value = current - 1;
321 updateMobileTotal();
322 }
323 }
324 });
325 });
326
327 mobilePlusBtns.forEach(btn => {
328 btn.addEventListener('click', function() {
329 const targetId = this.getAttribute('data-target');
330 const input = document.getElementById(targetId);
331 if (input) {
332 const current = parseInt(input.value) || 1;
333 const max = parseInt(input.getAttribute('max')) || 20;
334 if (current < max) {
335 input.value = current + 1;
336 updateMobileTotal();
337 }
338 }
339 });
340 });
341
342 // Mobile date picker - Initialize exactly like main date picker
343 const mobileDateInput = document.getElementById('mobile_travel_date');
344 if (mobileDateInput) {
345 // Default value: today (desktop behavior). If availability mode doesn't allow today,
346 // we will swap to the first enabled date after Flatpickr initializes.
347 const today = new Date();
348 today.setHours(0, 0, 0, 0);
349 const todayStr = today.getFullYear() + '-' +
350 String(today.getMonth() + 1).padStart(2, '0') + '-' +
351 String(today.getDate()).padStart(2, '0');
352 if (!mobileDateInput.value) {
353 mobileDateInput.value = todayStr;
354 }
355
356 // Check if Flatpickr is available
357 if (typeof flatpickr === 'undefined') {
358 // Make it a regular date input as fallback
359 mobileDateInput.type = 'date';
360 mobileDateInput.removeAttribute('readonly');
361 } else {
362 // Get availability dates from global trip data with fallback
363 let availabilityDates = [];
364 try {
365 if (window.yatraTripData && Array.isArray(window.yatraTripData.availabilityDates)) {
366 availabilityDates = window.yatraTripData.availabilityDates;
367 }
368 } catch (e) {
369 }
370
371 var mobileFpCommon = {
372 clickOpens: true,
373 appendTo: document.body,
374 static: false,
375 onReady: function(selectedDates, dateStr, instance) {
376 if (instance.calendarContainer) {
377 instance.calendarContainer.style.zIndex = '100050';
378 }
379
380 // Ensure we have a selected date (desktop behavior).
381 // If current value is empty, use today.
382 if (!instance.selectedDates || instance.selectedDates.length === 0) {
383 instance.setDate(mobileDateInput.value || todayStr, true);
384 }
385 },
386 onChange: function(selectedDates, dateStr, instance) {
387 var mainDateInput = document.getElementById('travel_date');
388 if (mainDateInput && mainDateInput._flatpickr) {
389 mainDateInput._flatpickr.setDate(dateStr);
390 }
391 updateMobileTotal();
392 }
393 };
394 function yatraBindMobileDatepickerTouch(inputEl) {
395 if (!inputEl || !inputEl._flatpickr) {
396 return;
397 }
398 var fp = inputEl._flatpickr;
399 var btn = document.querySelector('.yatra-mobile-date-btn');
400 var fallbackWrap = inputEl.closest('.yatra-mobile-date-section');
401 var target = btn || fallbackWrap;
402 if (!target) return;
403 target.addEventListener('click', function(e) {
404 e.stopPropagation();
405 fp.open();
406 });
407 target.addEventListener('keydown', function(e) {
408 var key = e.key || e.code;
409 if (key === 'Enter' || key === ' ' || key === 'Spacebar') {
410 e.preventDefault();
411 e.stopPropagation();
412 fp.open();
413 }
414 });
415 }
416 // Initialize Flatpickr with same configuration as main date picker
417 var altFormat = (window.yatraTripData && (window.yatraTripData.flatpickrAltFormat || window.yatraTripData.altDateFormat))
418 ? (window.yatraTripData.flatpickrAltFormat || window.yatraTripData.altDateFormat)
419 : null;
420 if (!altFormat && window.yatraTripData && (window.yatraTripData.dateFormat || window.yatraTripData.date_format)) {
421 // Keep in sync with assets/js/trip.js helper (minimal mapping)
422 var php = window.yatraTripData.dateFormat || window.yatraTripData.date_format;
423 var map = { d:'d', j:'j', D:'D', l:'l', m:'m', n:'n', M:'M', F:'F', Y:'Y', y:'y' };
424 altFormat = '';
425 var esc = false;
426 for (var i = 0; i < php.length; i++) {
427 var ch = php[i];
428 if (esc) { altFormat += ch; esc = false; continue; }
429 if (ch === '\\\\') { esc = true; continue; }
430 altFormat += map[ch] || ch;
431 }
432 }
433 if (!altFormat) {
434 altFormat = 'F j, Y';
435 }
436
437 if (availabilityDates.length > 0) {
438 // Availability-based booking - enable only specific dates
439 flatpickr(mobileDateInput, Object.assign({}, mobileFpCommon, {
440 dateFormat: 'Y-m-d',
441 altInput: true,
442 altFormat: altFormat,
443 minDate: 'today',
444 enable: availabilityDates,
445 defaultDate: availabilityDates[0] || todayStr,
446 }));
447 } else {
448 // Regular booking - flexible dates with min/max constraints
449 var config = Object.assign({}, mobileFpCommon, {
450 dateFormat: 'Y-m-d',
451 altInput: true,
452 altFormat: altFormat,
453 minDate: 'today',
454 defaultDate: mobileDateInput.value || todayStr,
455 });
456 var minDate = mobileDateInput.getAttribute('data-min-date');
457 var maxDate = mobileDateInput.getAttribute('data-max-date');
458 if (minDate) {
459 config.minDate = minDate;
460 if (!mobileDateInput.value) {
461 config.defaultDate = minDate;
462 }
463 }
464 if (maxDate) {
465 config.maxDate = maxDate;
466 }
467 flatpickr(mobileDateInput, config);
468 }
469 yatraBindMobileDatepickerTouch(mobileDateInput);
470 }
471 }
472
473 // Travelers selector is rendered in the sticky bar using the same component as desktop.
474
475 // Mobile check availability button
476 const mobileCheckBtn = document.getElementById('mobile-check-availability-btn');
477 if (mobileCheckBtn) {
478 mobileCheckBtn.addEventListener('click', function() {
479 // Sync mobile form data with main form
480 syncMobileToMainForm();
481 // Trigger main check availability
482 const mainCheckBtn = document.getElementById('check-availability-btn');
483 if (mainCheckBtn) {
484 mainCheckBtn.click();
485 }
486 });
487 }
488 }
489
490 function syncMobileToMainForm() {
491 // Sync date
492 const mobileDate = document.getElementById('mobile_travel_date');
493 const mainDate = document.getElementById('travel_date');
494 if (mobileDate && mainDate) {
495 // If main date has Flatpickr instance, use it
496 if (mainDate._flatpickr) {
497 mainDate._flatpickr.setDate(mobileDate.value);
498 } else {
499 mainDate.value = mobileDate.value;
500 }
501 }
502
503 // Sync travelers
504 const mobileTravelers = document.getElementById('mobile_num_travelers');
505 const mainTravelers = document.getElementById('num_travelers');
506 if (mobileTravelers && mainTravelers) {
507 mainTravelers.value = mobileTravelers.value;
508 }
509
510 // Sync traveler-based pricing (mobile selector -> desktop selector)
511 const mobileTravelerInputs = document.querySelectorAll('#mobile-quantity-selector input[id^="mobile_traveler_"]');
512 if (mobileTravelerInputs && mobileTravelerInputs.length > 0) {
513 mobileTravelerInputs.forEach((input) => {
514 const desktopId = input.id.replace(/^mobile_/, '');
515 const desktopInput = document.getElementById(desktopId);
516 if (desktopInput) {
517 desktopInput.value = input.value;
518 }
519 });
520 }
521 }
522
523 function updateMobileTotal() {
524 const sidebar = document.getElementById('yatra-mobile-sticky-sidebar');
525 if (!sidebar) return;
526
527 const pricingType = (window.yatraTripData && window.yatraTripData.pricingType) ? window.yatraTripData.pricingType : 'regular';
528 const mobileTotal = document.getElementById('mobile-total-amount');
529 if (!mobileTotal) return;
530
531 let total = 0;
532
533 if (pricingType === 'traveler_based') {
534 // Traveler-based pricing: sum all category quantities × prices
535 const travelerInputs = document.querySelectorAll('[id^="mobile_traveler_"]');
536 travelerInputs.forEach(input => {
537 const qty = parseInt(input.value) || 0;
538 const price = parseFloat(input.getAttribute('data-price')) || 0;
539 total += qty * price;
540 });
541 } else {
542 // Regular pricing: simple multiplication
543 const travelersInput = document.getElementById('mobile_num_travelers');
544 if (travelersInput) {
545 const travelers = parseInt(travelersInput.value) || 1;
546 const basePrice = parseFloat(travelersInput.getAttribute('data-price')) || 0;
547 total = travelers * basePrice;
548 }
549 }
550
551 // Format and update display (mobile total element has been removed)
552 // The mobile total price display is no longer shown
553 if (mobileTotal) {
554 if (window.yatraTripData && window.yatraTripData.currencySymbol) {
555 mobileTotal.textContent = window.yatraTripData.currencySymbol + total.toFixed(2);
556 } else {
557 mobileTotal.textContent = formatCurrency(total);
558 }
559 }
560
561 // Also sync with main form total if it exists (main total also removed)
562 const mainTotal = document.getElementById('total-amount');
563 if (mainTotal && window.yatraTripData && window.yatraTripData.currencySymbol) {
564 mainTotal.textContent = window.yatraTripData.currencySymbol + total.toFixed(2);
565 }
566 }
567
568 function formatCurrency(amount) {
569 // Simple currency formatting (can be enhanced)
570 return '$' + amount.toFixed(2);
571 }
572
573 // Auto-hide on scroll down, show on scroll up (also works for enquiry-only sticky)
574 let lastScrollTop = 0;
575 const mobileSidebar = document.getElementById('yatra-mobile-sticky-sidebar');
576 const scrollThreshold = 100;
577
578 window.addEventListener('scroll', function() {
579 if (!mobileSidebar) return;
580
581 const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
582
583 if (scrollTop > lastScrollTop && scrollTop > scrollThreshold) {
584 // Scrolling down - hide sidebar
585 mobileSidebar.classList.add('hidden');
586 } else {
587 // Scrolling up - show sidebar
588 mobileSidebar.classList.remove('hidden');
589 }
590
591 lastScrollTop = scrollTop <= 0 ? 0 : scrollTop;
592 }, false);
593 </script>
594