add filter badges to search-box
This commit is contained in:
@@ -146,9 +146,96 @@ class SearchBox extends HTMLElement {
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
${this.renderFilterBadges()}
|
||||
`
|
||||
}
|
||||
|
||||
hasActiveFilters() {
|
||||
return this.searchQuery || this.selectedCategory || this.useCurrentLocation
|
||||
}
|
||||
|
||||
getActiveFilterCount() {
|
||||
let count = 0
|
||||
if (this.searchQuery) count++
|
||||
if (this.selectedCategory) count++
|
||||
if (this.useCurrentLocation) count++
|
||||
return count
|
||||
}
|
||||
|
||||
renderFilterBadges() {
|
||||
if (!this.hasActiveFilters()) return ''
|
||||
|
||||
const badges = []
|
||||
|
||||
// Query badge
|
||||
if (this.searchQuery) {
|
||||
badges.push(/* html */`
|
||||
<button type="button" class="filter-badge" data-filter="query">
|
||||
<span class="filter-badge-text">"${this.escapeHtml(this.searchQuery)}"</span>
|
||||
<svg class="filter-badge-close" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
`)
|
||||
}
|
||||
|
||||
// Category badge
|
||||
if (this.selectedCategory) {
|
||||
const categoryLabel = this.selectedSubcategory
|
||||
? `${t(`categories.${this.selectedCategory}`)} › ${t(`subcategories.${this.selectedSubcategory}`)}`
|
||||
: t(`categories.${this.selectedCategory}`)
|
||||
|
||||
badges.push(/* html */`
|
||||
<button type="button" class="filter-badge" data-filter="category">
|
||||
<span class="filter-badge-text">${categoryLabel}</span>
|
||||
<svg class="filter-badge-close" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
`)
|
||||
}
|
||||
|
||||
// Location badge (only when using current location with radius)
|
||||
if (this.useCurrentLocation) {
|
||||
const radiusText = t('search.radiusAround', { radius: this.selectedRadius })
|
||||
badges.push(/* html */`
|
||||
<button type="button" class="filter-badge" data-filter="location">
|
||||
<span class="filter-badge-text">📍 ${radiusText}</span>
|
||||
<svg class="filter-badge-close" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
`)
|
||||
}
|
||||
|
||||
// Only show "clear all" if more than one filter is active
|
||||
const showClearAll = this.getActiveFilterCount() > 1
|
||||
|
||||
return /* html */`
|
||||
<div class="filter-badges">
|
||||
${badges.join('')}
|
||||
${showClearAll ? `
|
||||
<button type="button" class="filter-badge filter-badge-clear" data-filter="all">
|
||||
<span class="filter-badge-text">${t('search.clearAll')}</span>
|
||||
<svg class="filter-badge-close" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
` : ''}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
escapeHtml(text) {
|
||||
const div = document.createElement('div')
|
||||
div.textContent = text
|
||||
return div.innerHTML
|
||||
}
|
||||
|
||||
renderFilters() {
|
||||
// Track which category accordion is expanded
|
||||
this._expandedCategory = this._expandedCategory || ''
|
||||
@@ -378,16 +465,71 @@ class SearchBox extends HTMLElement {
|
||||
const handleRadiusChange = (e) => {
|
||||
this.selectedRadius = parseInt(e.target.value)
|
||||
this.emitFilterChange()
|
||||
this.updateFilterBadges()
|
||||
}
|
||||
|
||||
radiusSelect?.addEventListener('change', handleRadiusChange)
|
||||
radiusSelectMobile?.addEventListener('change', handleRadiusChange)
|
||||
|
||||
// Filter badge click handlers
|
||||
this.querySelectorAll('.filter-badge').forEach(badge => {
|
||||
badge.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
const filterType = badge.dataset.filter
|
||||
this.removeFilter(filterType)
|
||||
})
|
||||
})
|
||||
|
||||
// Adjust select width to selected option (desktop only)
|
||||
this.adjustSelectWidth(countrySelect)
|
||||
this.adjustSelectWidth(radiusSelect)
|
||||
}
|
||||
|
||||
updateFilterBadges() {
|
||||
const badgesContainer = this.querySelector('.filter-badges')
|
||||
if (badgesContainer) {
|
||||
badgesContainer.outerHTML = this.renderFilterBadges()
|
||||
// Re-attach event listeners for new badges
|
||||
this.querySelectorAll('.filter-badge').forEach(badge => {
|
||||
badge.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
const filterType = badge.dataset.filter
|
||||
this.removeFilter(filterType)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
removeFilter(filterType) {
|
||||
switch (filterType) {
|
||||
case 'query':
|
||||
this.searchQuery = ''
|
||||
break
|
||||
case 'category':
|
||||
this.selectedCategory = ''
|
||||
this.selectedSubcategory = ''
|
||||
break
|
||||
case 'location':
|
||||
this.useCurrentLocation = false
|
||||
this.currentLat = null
|
||||
this.currentLng = null
|
||||
break
|
||||
case 'all':
|
||||
this.searchQuery = ''
|
||||
this.selectedCategory = ''
|
||||
this.selectedSubcategory = ''
|
||||
this.useCurrentLocation = false
|
||||
this.currentLat = null
|
||||
this.currentLng = null
|
||||
break
|
||||
}
|
||||
|
||||
this.saveFiltersToStorage()
|
||||
this.emitFilterChange()
|
||||
this.render()
|
||||
this.setupEventListeners()
|
||||
}
|
||||
|
||||
adjustSelectWidth(select) {
|
||||
if (!select) return
|
||||
|
||||
@@ -960,6 +1102,62 @@ style.textContent = /* css */`
|
||||
background: var(--color-primary-light);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Filter Badges */
|
||||
search-box .filter-badges {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-xs);
|
||||
margin-top: var(--space-sm);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
search-box .filter-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
padding: var(--space-xs) var(--space-sm);
|
||||
background: var(--color-bg-secondary);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-full);
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
search-box .filter-badge:hover {
|
||||
background: var(--color-bg-tertiary);
|
||||
border-color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
search-box .filter-badge-text {
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
search-box .filter-badge-close {
|
||||
flex-shrink: 0;
|
||||
color: var(--color-text-muted);
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
search-box .filter-badge:hover .filter-badge-close {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
search-box .filter-badge-clear {
|
||||
background: transparent;
|
||||
border-style: dashed;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
search-box .filter-badge-clear:hover {
|
||||
background: var(--color-bg-secondary);
|
||||
color: var(--color-text);
|
||||
}
|
||||
`
|
||||
document.head.appendChild(style)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user