Загрузка...

Интерактивная строка поиска для бронирования путешествий. Выбор направлений, дат, пассажиров. Адаптивный дизайн.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Travel Search Bar</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: #f9fafb;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
.container {
width: 100%;
max-width: 80rem;
position: relative;
}
.search-bar {
background: rgba(243, 244, 246, 0.3);
border-radius: 9999px;
padding: 0.5rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 0.5rem;
}
.search-field {
flex: 1;
padding: 1rem 1.5rem;
border-radius: 9999px;
border: none;
background: transparent;
cursor: pointer;
text-align: left;
transition: all 0.2s;
}
.search-field:hover {
background: rgba(255, 255, 255, 0.5);
}
.search-field.active {
background: white;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.field-label {
font-size: 0.75rem;
font-weight: 600;
color: #111827;
margin-bottom: 0.25rem;
}
.field-value {
font-size: 0.875rem;
color: #6b7280;
}
.search-button {
height: 3.5rem;
width: 3.5rem;
border-radius: 9999px;
background: #0E7490;
border: none;
color: white;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: background 0.2s;
}
.search-button:hover {
background: #0c6481;
}
.overlay {
position: absolute;
top: calc(100% + 1.5rem);
background: white;
border-radius: 32px;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
padding: 1.5rem;
z-index: 50;
opacity: 0;
transform: translateY(-10px);
transition: opacity 0.2s, transform 0.2s;
pointer-events: none;
}
.overlay.active {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
.overlay.destinations {
left: 0;
right: 0;
max-width: 28rem;
}
.overlay.calendar {
left: 0;
right: 0;
padding: 2rem;
}
.overlay.passengers {
right: 0;
width: 26.25rem;
padding: 2.5rem;
}
.section-title {
font-size: 0.875rem;
font-weight: 700;
color: #111827;
margin-bottom: 0.75rem;
}
.destination-item {
width: 100%;
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.5rem 0.75rem;
border-radius: 0.75rem;
border: none;
background: transparent;
cursor: pointer;
text-align: left;
transition: background 0.2s;
}
.destination-item:hover {
background: #f9fafb;
}
.icon-wrapper {
padding: 0.5rem;
border-radius: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
}
.icon-wrapper.yellow {
background: #fef3c7;
}
.icon-wrapper.blue {
background: #dbeafe;
}
.icon-wrapper.green {
background: #d1fae5;
}
.icon-wrapper.orange {
background: #fed7aa;
}
.icon-wrapper.yellow svg {
color: #fbbf24;
}
.icon-wrapper.blue svg {
color: #3b82f6;
}
.icon-wrapper.green svg {
color: #10b981;
}
.icon-wrapper.orange svg {
color: #f97316;
}
.destination-name {
font-size: 0.875rem;
font-weight: 500;
color: #111827;
}
.recent-item {
flex: 1;
}
.recent-route {
font-size: 0.875rem;
font-weight: 700;
color: #111827;
line-height: 1.25;
}
.recent-details {
font-size: 0.75rem;
color: #6b7280;
margin-top: 0.125rem;
}
.calendar-grid {
display: flex;
gap: 4rem;
justify-content: center;
}
.month {
flex: none;
}
.month-name {
font-size: 1.125rem;
font-weight: 700;
text-align: center;
margin-bottom: 1.25rem;
}
.weekdays {
display: grid;
grid-template-columns: repeat(7, 2.75rem);
gap: 0.25rem;
margin-bottom: 0.5rem;
}
.weekday {
font-size: 0.875rem;
color: #6b7280;
text-align: center;
padding: 0.25rem;
}
.days {
display: grid;
grid-template-columns: repeat(7, 2.75rem);
gap: 0.25rem;
}
.day {
height: 2.75rem;
width: 2.75rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.5rem;
border: none;
background: transparent;
cursor: pointer;
font-size: 1rem;
color: #111827;
transition: background 0.2s;
}
.day:hover:not(.empty):not(.other-month) {
background: #f3f4f6;
}
.day.selected {
background: #0E7490;
color: white;
}
.day.empty {
cursor: default;
}
.day.other-month {
color: #d1d5db;
}
.passenger-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1rem;
}
.passenger-info {
flex: 1;
}
.passenger-label {
font-size: 0.875rem;
font-weight: 600;
color: #111827;
}
.passenger-desc {
font-size: 0.75rem;
color: #6b7280;
}
.passenger-controls {
display: flex;
align-items: center;
gap: 0.75rem;
}
.passenger-btn {
height: 2rem;
width: 2rem;
border-radius: 9999px;
border: 1px solid #e5e7eb;
background: white;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
transition: all 0.2s;
}
.passenger-btn:hover {
background: #f9fafb;
border-color: #d1d5db;
}
.passenger-count {
width: 2rem;
text-align: center;
font-weight: 500;
}
.space-y-5>*:not(:last-child) {
margin-bottom: 1.25rem;
}
.space-y-1>*:not(:last-child) {
margin-bottom: 0.25rem;
}
</style>
</head>
<body>
<div class="container" id="container">
<div class="search-bar">
<button class="search-field" id="departure-field">
<div class="field-label">Departure</div>
<div class="field-value" id="departure-value">Search destinations</div>
</button>
<button class="search-field" id="arrival-field">
<div class="field-label">Arrival</div>
<div class="field-value" id="arrival-value">Search destinations</div>
</button>
<button class="search-field" id="date-field">
<div class="field-label">Date</div>
<div class="field-value" id="date-value">Add dates</div>
</button>
<button class="search-field" id="passengers-field">
<div class="field-label">Passengers</div>
<div class="field-value" id="passengers-value">Add passengers</div>
</button>
<button class="search-button" id="search-btn">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
</button>
</div>
<!-- Destinations Overlay -->
<div class="overlay destinations" id="destinations-overlay">
<div class="space-y-5">
<div>
<h3 class="section-title">Recent Searches</h3>
<div class="space-y-1" id="recent-searches"></div>
</div>
<div>
<h3 class="section-title" id="destinations-title">Suggested Departure</h3>
<div class="space-y-1" id="destinations-list"></div>
</div>
</div>
</div>
<!-- Calendar Overlay -->
<div class="overlay calendar" id="calendar-overlay">
<div class="calendar-grid" id="calendar-grid"></div>
</div>
<!-- Passengers Overlay -->
<div class="overlay passengers" id="passengers-overlay">
<div class="passenger-row">
<div class="passenger-info">
<div class="passenger-label">Children</div>
<div class="passenger-desc">Below age 5</div>
</div>
<div class="passenger-controls">
<button class="passenger-btn" id="children-minus">−</button>
<span class="passenger-count" id="children-count">0</span>
<button class="passenger-btn" id="children-plus">+</button>
</div>
</div>
<div class="passenger-row">
<div class="passenger-info">
<div class="passenger-label">Members</div>
<div class="passenger-desc">Above age 20</div>
</div>
<div class="passenger-controls">
<button class="passenger-btn" id="members-minus">−</button>
<span class="passenger-count" id="members-count">0</span>
<button class="passenger-btn" id="members-plus">+</button>
</div>
</div>
</div>
</div>
<script>
// State
let activeField = null;
let departure = "Search destinations";
let arrival = "Search destinations";
let selectedDates = [];
let passengers = { children: 0, members: 0 };
// Data
const recentSearches = [
{ id: "1", route: "New York → London", date: "Aug 15, 2025", passengers: "2 passengers", color: "yellow" },
{ id: "2", route: "Paris → Tokyo", date: "Sep 20, 2025", passengers: "1 passenger", color: "blue" },
];
const destinations = [
{ id: "1", name: "New York", color: "yellow" },
{ id: "2", name: "London", color: "blue" },
{ id: "3", name: "Paris", color: "green" },
{ id: "4", name: "Tokyo", color: "orange" },
{ id: "5", name: "Dubai", color: "yellow" },
];
// Elements
const container = document.getElementById('container');
const departureField = document.getElementById('departure-field');
const arrivalField = document.getElementById('arrival-field');
const dateField = document.getElementById('date-field');
const passengersField = document.getElementById('passengers-field');
const departureValue = document.getElementById('departure-value');
const arrivalValue = document.getElementById('arrival-value');
const dateValue = document.getElementById('date-value');
const passengersValue = document.getElementById('passengers-value');
const destinationsOverlay = document.getElementById('destinations-overlay');
const calendarOverlay = document.getElementById('calendar-overlay');
const passengersOverlay = document.getElementById('passengers-overlay');
// Handle field clicks
function handleFieldClick(field) {
if (activeField === field) {
closeAllOverlays();
return;
}
closeAllOverlays();
activeField = field;
const fields = [departureField, arrivalField, dateField, passengersField];
fields.forEach(f => f.classList.remove('active'));
if (field === 'departure') {
departureField.classList.add('active');
destinationsOverlay.classList.add('active');
document.getElementById('destinations-title').textContent = 'Suggested Departure';
renderDestinations();
renderRecentSearches();
} else if (field === 'arrival') {
arrivalField.classList.add('active');
destinationsOverlay.classList.add('active');
document.getElementById('destinations-title').textContent = 'Suggested Arrival';
renderDestinations();
renderRecentSearches();
} else if (field === 'date') {
dateField.classList.add('active');
calendarOverlay.classList.add('active');
renderCalendar();
} else if (field === 'passengers') {
passengersField.classList.add('active');
passengersOverlay.classList.add('active');
}
}
function closeAllOverlays() {
activeField = null;
[departureField, arrivalField, dateField, passengersField].forEach(f => f.classList.remove('active'));
[destinationsOverlay, calendarOverlay, passengersOverlay].forEach(o => o.classList.remove('active'));
}
// Render destinations
function renderDestinations() {
const list = document.getElementById('destinations-list');
list.innerHTML = destinations.map(dest => `
<button class="destination-item" onclick="selectDestination('${dest.name}')">
<div class="icon-wrapper ${dest.color}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.1-1.1.5l-.3.5c-.2.5-.1 1 .3 1.3L9 12l-2 3H4l-1 1 3 2 2 3 1-1v-3l3-2 3.5 5.3c.3.4.8.5 1.3.3l.5-.2c.4-.3.6-.7.5-1.2z"/></svg>
</div>
<span class="destination-name">${dest.name}</span>
</button>
`).join('');
}
function renderRecentSearches() {
const list = document.getElementById('recent-searches');
list.innerHTML = recentSearches.map(search => {
const cities = search.route.split(' → ');
const city = activeField === 'departure' ? cities[0] : cities[1];
return `
<button class="destination-item" onclick="selectDestination('${city}')">
<div class="icon-wrapper ${search.color}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.1-1.1.5l-.3.5c-.2.5-.1 1 .3 1.3L9 12l-2 3H4l-1 1 3 2 2 3 1-1v-3l3-2 3.5 5.3c.3.4.8.5 1.3.3l.5-.2c.4-.3.6-.7.5-1.2z"/></svg>
</div>
<div class="recent-item">
<div class="recent-route">${search.route}</div>
<div class="recent-details">${search.date} · ${search.passengers}</div>
</div>
</button>
`;
}).join('');
}
function selectDestination(name) {
if (activeField === 'departure') {
departure = name;
departureValue.textContent = name;
} else if (activeField === 'arrival') {
arrival = name;
arrivalValue.textContent = name;
}
closeAllOverlays();
}
// Calendar
function renderCalendar() {
const grid = document.getElementById('calendar-grid');
const today = new Date();
const months = [today, new Date(today.getFullYear(), today.getMonth() + 1)];
grid.innerHTML = months.map(month => {
const monthName = month.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
const firstDay = new Date(month.getFullYear(), month.getMonth(), 1);
const lastDay = new Date(month.getFullYear(), month.getMonth() + 1, 0);
const startPadding = firstDay.getDay();
const daysInMonth = lastDay.getDate();
let daysHTML = '';
for (let i = 0; i < startPadding; i++) {
daysHTML += '<button class="day empty"></button>';
}
for (let day = 1; day <= daysInMonth; day++) {
const date = new Date(month.getFullYear(), month.getMonth(), day);
const isSelected = selectedDates.some(d => d.getTime() === date.getTime());
daysHTML += `<button class="day ${isSelected ? 'selected' : ''}" onclick="toggleDate('${date.toISOString()}')">${day}</button>`;
}
return `
<div class="month">
<div class="month-name">${monthName}</div>
<div class="weekdays">
<div class="weekday">S</div>
<div class="weekday">M</div>
<div class="weekday">T</div>
<div class="weekday">W</div>
<div class="weekday">T</div>
<div class="weekday">F</div>
<div class="weekday">S</div>
</div>
<div class="days">${daysHTML}</div>
</div>
`;
}).join('');
}
function toggleDate(dateStr) {
const date = new Date(dateStr);
const index = selectedDates.findIndex(d => d.getTime() === date.getTime());
if (index > -1) {
selectedDates.splice(index, 1);
} else {
selectedDates.push(date);
}
updateDateDisplay();
renderCalendar();
}
function updateDateDisplay() {
if (selectedDates.length === 0) {
dateValue.textContent = 'Add dates';
} else if (selectedDates.length === 1) {
dateValue.textContent = selectedDates[0].toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
} else {
const sorted = [...selectedDates].sort((a, b) => a - b);
dateValue.textContent = `${sorted[0].toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} - ${sorted[sorted.length - 1].toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}`;
}
}
// Passengers
function updatePassengerDisplay() {
document.getElementById('children-count').textContent = passengers.children;
document.getElementById('members-count').textContent = passengers.members;
const total = passengers.children + passengers.members;
passengersValue.textContent = total === 0 ? 'Add passengers' : `${total} passenger${total > 1 ? 's' : ''}`;
}
document.getElementById('children-minus').addEventListener('click', () => {
passengers.children = Math.max(0, passengers.children - 1);
updatePassengerDisplay();
});
document.getElementById('children-plus').addEventListener('click', () => {
passengers.children++;
updatePassengerDisplay();
});
document.getElementById('members-minus').addEventListener('click', () => {
passengers.members = Math.max(0, passengers.members - 1);
updatePassengerDisplay();
});
document.getElementById('members-plus').addEventListener('click', () => {
passengers.members++;
updatePassengerDisplay();
});
// Event listeners
departureField.addEventListener('click', () => handleFieldClick('departure'));
arrivalField.addEventListener('click', () => handleFieldClick('arrival'));
dateField.addEventListener('click', () => handleFieldClick('date'));
passengersField.addEventListener('click', () => handleFieldClick('passengers'));
document.addEventListener('click', (e) => {
if (!container.contains(e.target)) {
closeAllOverlays();
}
});
// Make functions global
window.selectDestination = selectDestination;
window.toggleDate = toggleDate;
</script>
</body>
</html>