Tour page (boards 2 and 3):
- Replace the blurred 'Welcome to {city}' hero with a breadcrumb and a photo
gallery (1 large + 2, 'All N photos' opens the lightbox); the H1 title,
rating with review count and facts follow; booking card stays sticky
- Phones/tablets: swipeable photos, name before price, 2x2 facts, and a
fixed bottom bar with price + Check availability (vc-tour.css)
Board 1:
- Hide Viator internal tags ('Agent Low Quality Product'...) from the tour
category, listing filter and activities section (VC_Tags, tested)
- Listings: run the query filter late and set posts_per_archive_page so
pages show 12 tours as the count says
- Footer columns without a menu show default links (quick links, structure
cities, contact/about/faq) instead of empty headings
Plugin 1.9.5, theme 1.0.13.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
152 lines
6.6 KiB
JavaScript
152 lines
6.6 KiB
JavaScript
/* Viator Collector — front-end interactions for the "Hop On Hop Off" design.
|
|
Ported from the mockup's main.js, trimmed to what the plugin renders inside
|
|
#vc-app. Listing filtering itself stays server-side (GET form submit); this
|
|
file only adds UX: card rails, FAQ accordion, testimonials marquee, the date
|
|
field, and auto-submit on quick filter toggles. */
|
|
(function () {
|
|
"use strict";
|
|
|
|
// The design is scoped to .vc-app wrappers; there can be several on one page
|
|
// (e.g. multiple homepage section shortcodes), so query document-wide.
|
|
var app = document;
|
|
|
|
/* ---------- card rails (prev / next) ---------- */
|
|
function railStep(rail) {
|
|
var card = rail.firstElementChild;
|
|
if (!card) return rail.clientWidth;
|
|
var gap = parseFloat(getComputedStyle(rail).columnGap) || 0;
|
|
return card.getBoundingClientRect().width + gap;
|
|
}
|
|
|
|
function syncRailButtons(rail, prev, next) {
|
|
var max = rail.scrollWidth - rail.clientWidth - 1;
|
|
if (prev) prev.disabled = rail.scrollLeft <= 0;
|
|
if (next) next.disabled = rail.scrollLeft >= max;
|
|
}
|
|
|
|
Array.prototype.forEach.call(app.querySelectorAll(".rail"), function (rail) {
|
|
var prev = document.querySelector('[data-rail-prev="' + rail.id + '"]');
|
|
var next = document.querySelector('[data-rail-next="' + rail.id + '"]');
|
|
|
|
if (prev) prev.addEventListener("click", function () {
|
|
rail.scrollBy({ left: -railStep(rail), behavior: "smooth" });
|
|
});
|
|
if (next) next.addEventListener("click", function () {
|
|
rail.scrollBy({ left: railStep(rail), behavior: "smooth" });
|
|
});
|
|
|
|
rail.addEventListener("scroll", function () {
|
|
syncRailButtons(rail, prev, next);
|
|
}, { passive: true });
|
|
window.addEventListener("resize", function () {
|
|
syncRailButtons(rail, prev, next);
|
|
});
|
|
syncRailButtons(rail, prev, next);
|
|
});
|
|
|
|
/* ---------- testimonials marquee: duplicate for a seamless loop ---------- */
|
|
Array.prototype.forEach.call(app.querySelectorAll("[data-marquee] .marquee__track"), function (track) {
|
|
Array.prototype.slice.call(track.children).forEach(function (node) {
|
|
var copy = node.cloneNode(true);
|
|
copy.setAttribute("aria-hidden", "true");
|
|
track.appendChild(copy);
|
|
});
|
|
});
|
|
|
|
/* ---------- FAQ accordion ---------- */
|
|
Array.prototype.forEach.call(app.querySelectorAll("[data-accordion]"), function (accordion) {
|
|
accordion.addEventListener("click", function (e) {
|
|
var btn = e.target.closest(".acc__q");
|
|
if (!btn) return;
|
|
var open = btn.getAttribute("aria-expanded") === "true";
|
|
Array.prototype.forEach.call(accordion.querySelectorAll(".acc__q"), function (other) {
|
|
other.setAttribute("aria-expanded", "false");
|
|
});
|
|
btn.setAttribute("aria-expanded", String(!open));
|
|
});
|
|
});
|
|
|
|
/* ---------- date field: text placeholder, native picker on focus ---------- */
|
|
Array.prototype.forEach.call(app.querySelectorAll("[data-datefield]"), function (dateField) {
|
|
dateField.addEventListener("focus", function () {
|
|
dateField.type = "date";
|
|
if (typeof dateField.showPicker === "function") {
|
|
try { dateField.showPicker(); } catch (err) { /* not user-activated */ }
|
|
}
|
|
});
|
|
dateField.addEventListener("blur", function () {
|
|
if (!dateField.value) dateField.type = "text";
|
|
});
|
|
});
|
|
|
|
/* ---------- listing: auto-submit on quick toggles (checks / sort) ---------- */
|
|
Array.prototype.forEach.call(app.querySelectorAll("form.filters, form#vc-filter-form"), function (form) {
|
|
form.addEventListener("change", function (e) {
|
|
if (e.target.closest(".vc-autosubmit")) form.submit();
|
|
});
|
|
});
|
|
|
|
/* ---------- single tour: "view all photos" scrolls the gallery ---------- */
|
|
var viewAll = app.querySelector(".tg-viewall, .gallery__viewall");
|
|
if (viewAll) viewAll.addEventListener("click", function () {
|
|
var g = app.querySelector(".gallery, .tour-gallery");
|
|
if (g) g.scrollIntoView({ behavior: "smooth" });
|
|
});
|
|
|
|
/* ---------- gallery lightbox (zoom / view photos) ---------- */
|
|
(function () {
|
|
var imgs = Array.prototype.slice.call(app.querySelectorAll(".gallery img"));
|
|
if (!imgs.length) return;
|
|
|
|
var host = imgs[0].closest(".vc-app") || document.body;
|
|
var box = document.createElement("div");
|
|
box.className = "vc-lightbox" + (imgs.length < 2 ? " vc-lightbox--single" : "");
|
|
box.setAttribute("role", "dialog");
|
|
box.setAttribute("aria-modal", "true");
|
|
box.innerHTML =
|
|
'<button class="vc-lightbox__btn vc-lightbox__prev" type="button" aria-label="Previous"><svg class="icon icon--24" aria-hidden="true"><use href="#i-arrow-left"></use></svg></button>' +
|
|
'<img class="vc-lightbox__img" alt="">' +
|
|
'<button class="vc-lightbox__btn vc-lightbox__next" type="button" aria-label="Next"><svg class="icon icon--24" aria-hidden="true"><use href="#i-arrow-right"></use></svg></button>' +
|
|
'<button class="vc-lightbox__btn vc-lightbox__close" type="button" aria-label="Close"><svg class="icon icon--24" aria-hidden="true"><use href="#i-x"></use></svg></button>' +
|
|
'<span class="vc-lightbox__count"></span>';
|
|
host.appendChild(box);
|
|
|
|
var bigImg = box.querySelector(".vc-lightbox__img");
|
|
var count = box.querySelector(".vc-lightbox__count");
|
|
var idx = 0;
|
|
|
|
function show(i) {
|
|
idx = (i + imgs.length) % imgs.length;
|
|
bigImg.src = imgs[idx].currentSrc || imgs[idx].src;
|
|
bigImg.alt = imgs[idx].alt || "";
|
|
count.textContent = (idx + 1) + " / " + imgs.length;
|
|
}
|
|
function open(i) {
|
|
show(i);
|
|
box.classList.add("is-open");
|
|
document.documentElement.style.overflow = "hidden";
|
|
}
|
|
function close() {
|
|
box.classList.remove("is-open");
|
|
document.documentElement.style.overflow = "";
|
|
}
|
|
|
|
imgs.forEach(function (img, i) {
|
|
img.addEventListener("click", function () { open(i); });
|
|
});
|
|
box.querySelector(".vc-lightbox__prev").addEventListener("click", function () { show(idx - 1); });
|
|
box.querySelector(".vc-lightbox__next").addEventListener("click", function () { show(idx + 1); });
|
|
box.querySelector(".vc-lightbox__close").addEventListener("click", close);
|
|
box.addEventListener("click", function (e) { if (e.target === box) close(); });
|
|
/* tour page top gallery: "All N photos" opens at the first photo not on show */
|
|
var allBtn = app.querySelector(".tgal__all");
|
|
if (allBtn) allBtn.addEventListener("click", function () { open(Math.min(3, imgs.length - 1)); });
|
|
document.addEventListener("keydown", function (e) {
|
|
if (!box.classList.contains("is-open")) return;
|
|
if (e.key === "Escape") close();
|
|
else if (e.key === "ArrowLeft") show(idx - 1);
|
|
else if (e.key === "ArrowRight") show(idx + 1);
|
|
});
|
|
})();
|
|
})();
|