refactor: always show sort and collapsible price filter toolbar on home page
This commit is contained in:
@@ -25,6 +25,7 @@ class PageHome extends HTMLElement {
|
|||||||
this.sort = 'newest'
|
this.sort = 'newest'
|
||||||
this.minPrice = null
|
this.minPrice = null
|
||||||
this.maxPrice = null
|
this.maxPrice = null
|
||||||
|
this.priceFilterOpen = false
|
||||||
|
|
||||||
// Geo state
|
// Geo state
|
||||||
this.userLat = null
|
this.userLat = null
|
||||||
@@ -155,6 +156,11 @@ class PageHome extends HTMLElement {
|
|||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Toggle price filter
|
||||||
|
this.querySelector('#toggle-price-filter')?.addEventListener('click', () => {
|
||||||
|
this.togglePriceFilter()
|
||||||
|
})
|
||||||
|
|
||||||
// Sort change
|
// Sort change
|
||||||
this.querySelector('#sort-select')?.addEventListener('change', (e) => {
|
this.querySelector('#sort-select')?.addEventListener('change', (e) => {
|
||||||
this.sort = e.target.value
|
this.sort = e.target.value
|
||||||
@@ -190,6 +196,7 @@ class PageHome extends HTMLElement {
|
|||||||
this.sort = 'newest'
|
this.sort = 'newest'
|
||||||
this.minPrice = null
|
this.minPrice = null
|
||||||
this.maxPrice = null
|
this.maxPrice = null
|
||||||
|
this.priceFilterOpen = false
|
||||||
this.updateUrl()
|
this.updateUrl()
|
||||||
this.render()
|
this.render()
|
||||||
this.setupEventListeners()
|
this.setupEventListeners()
|
||||||
@@ -384,18 +391,17 @@ class PageHome extends HTMLElement {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const showFilters = this.hasActiveFilters()
|
const showFilters = this.hasActiveFilters()
|
||||||
|
const hasPriceFilter = this.minPrice || this.maxPrice
|
||||||
|
|
||||||
this.innerHTML = /* html */`
|
this.innerHTML = /* html */`
|
||||||
<section class="search-section">
|
<section class="search-section">
|
||||||
<search-box no-navigate></search-box>
|
<search-box no-navigate></search-box>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
${showFilters ? this.renderFilterBar() : ''}
|
|
||||||
|
|
||||||
<section class="listings-section">
|
<section class="listings-section">
|
||||||
<div class="listings-header">
|
<div class="listings-header">
|
||||||
${showFilters ? `<h2 class="listings-title">${this.getListingsTitle()}</h2>` : ''}
|
${showFilters ? `<h2 class="listings-title">${this.getListingsTitle()}</h2>` : ''}
|
||||||
${!showFilters ? `
|
<div class="listings-toolbar">
|
||||||
<div class="sort-inline">
|
<div class="sort-inline">
|
||||||
<select id="sort-select">
|
<select id="sort-select">
|
||||||
${this.hasUserLocation() ? `<option value="distance" ${this.sort === 'distance' ? 'selected' : ''}>${t('search.sortDistance')}</option>` : ''}
|
${this.hasUserLocation() ? `<option value="distance" ${this.sort === 'distance' ? 'selected' : ''}>${t('search.sortDistance')}</option>` : ''}
|
||||||
@@ -405,7 +411,35 @@ class PageHome extends HTMLElement {
|
|||||||
<option value="price_desc" ${this.sort === 'price_desc' ? 'selected' : ''}>${t('search.sortPriceDesc')}</option>
|
<option value="price_desc" ${this.sort === 'price_desc' ? 'selected' : ''}>${t('search.sortPriceDesc')}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
<button class="btn btn-sm btn-outline" id="toggle-price-filter">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<line x1="4" y1="21" x2="4" y2="14"></line>
|
||||||
|
<line x1="4" y1="10" x2="4" y2="3"></line>
|
||||||
|
<line x1="12" y1="21" x2="12" y2="12"></line>
|
||||||
|
<line x1="12" y1="8" x2="12" y2="3"></line>
|
||||||
|
<line x1="20" y1="21" x2="20" y2="16"></line>
|
||||||
|
<line x1="20" y1="12" x2="20" y2="3"></line>
|
||||||
|
<line x1="1" y1="14" x2="7" y2="14"></line>
|
||||||
|
<line x1="9" y1="8" x2="15" y2="8"></line>
|
||||||
|
<line x1="17" y1="16" x2="23" y2="16"></line>
|
||||||
|
</svg>
|
||||||
|
<span>${t('search.priceRange')}</span>
|
||||||
|
${hasPriceFilter ? '<span class="filter-active-dot"></span>' : ''}
|
||||||
|
</button>
|
||||||
|
${showFilters ? `
|
||||||
|
<button class="btn btn-sm btn-ghost" id="clear-filters">${t('search.clearAll')}</button>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="price-filter-panel ${this.priceFilterOpen ? 'open' : ''}" id="price-filter-panel">
|
||||||
|
<div class="price-inputs">
|
||||||
|
<input type="number" id="min-price" placeholder="${t('search.min')}"
|
||||||
|
value="${this.minPrice || ''}" min="0" step="1">
|
||||||
|
<span class="price-separator">–</span>
|
||||||
|
<input type="number" id="max-price" placeholder="${t('search.max')}"
|
||||||
|
value="${this.maxPrice || ''}" min="0" step="1">
|
||||||
|
<button class="btn btn-sm btn-primary" id="apply-price">${t('search.apply')}</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="listings-container" class="listings-grid">
|
<div id="listings-container" class="listings-grid">
|
||||||
${this.renderListings()}
|
${this.renderListings()}
|
||||||
@@ -423,39 +457,10 @@ class PageHome extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFilterBar() {
|
togglePriceFilter() {
|
||||||
return /* html */`
|
this.priceFilterOpen = !this.priceFilterOpen
|
||||||
<section class="filter-bar">
|
const panel = this.querySelector('#price-filter-panel')
|
||||||
<div class="filter-row">
|
if (panel) panel.classList.toggle('open', this.priceFilterOpen)
|
||||||
<div class="price-filter">
|
|
||||||
<label>${t('search.priceRange')}</label>
|
|
||||||
<div class="price-inputs">
|
|
||||||
<input type="number" id="min-price" placeholder="${t('search.min')}"
|
|
||||||
value="${this.minPrice || ''}" min="0" step="1">
|
|
||||||
<span class="price-separator">–</span>
|
|
||||||
<input type="number" id="max-price" placeholder="${t('search.max')}"
|
|
||||||
value="${this.maxPrice || ''}" min="0" step="1">
|
|
||||||
<button class="btn btn-sm btn-outline" id="apply-price">${t('search.apply')}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="sort-filter">
|
|
||||||
<label for="sort-select">${t('search.sortBy')}</label>
|
|
||||||
<select id="sort-select">
|
|
||||||
${this.hasUserLocation() ? `<option value="distance" ${this.sort === 'distance' ? 'selected' : ''}>${t('search.sortDistance')}</option>` : ''}
|
|
||||||
<option value="newest" ${this.sort === 'newest' ? 'selected' : ''}>${t('search.sortNewest')}</option>
|
|
||||||
<option value="oldest" ${this.sort === 'oldest' ? 'selected' : ''}>${t('search.sortOldest')}</option>
|
|
||||||
<option value="price_asc" ${this.sort === 'price_asc' ? 'selected' : ''}>${t('search.sortPriceAsc')}</option>
|
|
||||||
<option value="price_desc" ${this.sort === 'price_desc' ? 'selected' : ''}>${t('search.sortPriceDesc')}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn btn-sm btn-ghost" id="clear-filters">
|
|
||||||
${t('search.clearAll')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderListings() {
|
renderListings() {
|
||||||
@@ -526,57 +531,14 @@ style.textContent = /* css */`
|
|||||||
padding: var(--space-xl) 0 var(--space-lg);
|
padding: var(--space-xl) 0 var(--space-lg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Filter Bar */
|
/* Toolbar */
|
||||||
page-home .filter-bar {
|
page-home .listings-toolbar {
|
||||||
margin-bottom: var(--space-lg);
|
|
||||||
padding: var(--space-md);
|
|
||||||
background: var(--color-bg-secondary);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
page-home .filter-row {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: var(--space-lg);
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
page-home .price-filter,
|
|
||||||
page-home .sort-filter {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
page-home .price-filter label,
|
|
||||||
page-home .sort-filter label {
|
|
||||||
font-size: var(--font-size-sm);
|
|
||||||
font-weight: var(--font-weight-medium);
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
page-home .price-inputs {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--space-xs);
|
gap: var(--space-sm);
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
page-home .price-inputs input {
|
|
||||||
width: 100px;
|
|
||||||
padding: var(--space-xs) var(--space-sm);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
background: var(--color-bg);
|
|
||||||
color: var(--color-text);
|
|
||||||
font-size: var(--font-size-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
page-home .price-separator {
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
page-home .sort-filter select,
|
|
||||||
page-home .sort-inline select {
|
page-home .sort-inline select {
|
||||||
padding: var(--space-xs) var(--space-sm);
|
padding: var(--space-xs) var(--space-sm);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
@@ -586,29 +548,79 @@ style.textContent = /* css */`
|
|||||||
font-size: var(--font-size-sm);
|
font-size: var(--font-size-sm);
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
page-home .btn-sm {
|
page-home .btn-sm {
|
||||||
padding: var(--space-xs) var(--space-sm);
|
padding: var(--space-xs) var(--space-sm);
|
||||||
font-size: var(--font-size-sm);
|
font-size: var(--font-size-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
page-home .btn-ghost {
|
page-home .btn-ghost {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
page-home .btn-ghost:hover {
|
page-home .btn-ghost:hover {
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
page-home .filter-active-dot {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: var(--radius-full);
|
||||||
|
background: var(--color-accent);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Price Filter Panel */
|
||||||
|
page-home .price-filter-panel {
|
||||||
|
display: none;
|
||||||
|
padding: var(--space-md);
|
||||||
|
margin-bottom: var(--space-md);
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
page-home .price-filter-panel.open {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
page-home .price-inputs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
page-home .price-inputs input {
|
||||||
|
width: 100px;
|
||||||
|
padding: var(--space-xs) var(--space-sm);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
background: var(--color-bg);
|
||||||
|
color: var(--color-text);
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
page-home .price-separator {
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
page-home .filter-row {
|
page-home .listings-toolbar {
|
||||||
flex-direction: column;
|
width: 100%;
|
||||||
align-items: stretch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
page-home .sort-inline {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
page-home .sort-inline select {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
page-home .price-inputs {
|
page-home .price-inputs {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
@@ -622,10 +634,6 @@ style.textContent = /* css */`
|
|||||||
page-home .price-inputs .btn {
|
page-home .price-inputs .btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
page-home .sort-filter select {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Listings Section */
|
/* Listings Section */
|
||||||
|
|||||||
Reference in New Issue
Block a user