WIP Search places by category
This commit is contained in:
@@ -6,10 +6,12 @@ import { on } from '@ember/modifier';
|
||||
import Icon from '#components/icon';
|
||||
import UserMenu from '#components/user-menu';
|
||||
import SearchBox from '#components/search-box';
|
||||
import CategoryChips from '#components/category-chips';
|
||||
|
||||
export default class AppHeaderComponent extends Component {
|
||||
@service storage;
|
||||
@tracked isUserMenuOpen = false;
|
||||
@tracked hasQuery = false;
|
||||
|
||||
@action
|
||||
toggleUserMenu() {
|
||||
@@ -21,10 +23,31 @@ export default class AppHeaderComponent extends Component {
|
||||
this.isUserMenuOpen = false;
|
||||
}
|
||||
|
||||
@action
|
||||
handleQueryChange(query) {
|
||||
this.hasQuery = !!query;
|
||||
}
|
||||
|
||||
@action
|
||||
handleChipSelect() {
|
||||
// When a chip is selected, we might want to ensure the search box is cleared visually,
|
||||
// although the route transition will happen.
|
||||
// The SearchBox component manages its own state, so we rely on the route transition.
|
||||
// However, if we want to clear the search box input from here, we'd need to control it.
|
||||
// For now, let's just let the route change happen.
|
||||
}
|
||||
|
||||
<template>
|
||||
<header class="app-header">
|
||||
<div class="header-left">
|
||||
<SearchBox @onToggleMenu={{@onToggleMenu}} />
|
||||
<SearchBox
|
||||
@onToggleMenu={{@onToggleMenu}}
|
||||
@onQueryChange={{this.handleQueryChange}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="header-center {{if this.hasQuery 'searching'}}">
|
||||
<CategoryChips @onSelect={{this.handleChipSelect}} />
|
||||
</div>
|
||||
|
||||
<div class="header-right">
|
||||
|
||||
52
app/components/category-chips.gjs
Normal file
52
app/components/category-chips.gjs
Normal file
@@ -0,0 +1,52 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { on } from '@ember/modifier';
|
||||
import { fn } from '@ember/helper';
|
||||
import Icon from '#components/icon';
|
||||
import { POI_CATEGORIES } from '../utils/poi-categories';
|
||||
|
||||
export default class CategoryChipsComponent extends Component {
|
||||
@service router;
|
||||
@service mapUi;
|
||||
|
||||
get categories() {
|
||||
return POI_CATEGORIES;
|
||||
}
|
||||
|
||||
@action
|
||||
searchCategory(category) {
|
||||
// If passed an onSelect action, call it (e.g. to clear search box)
|
||||
if (this.args.onSelect) {
|
||||
this.args.onSelect(category);
|
||||
}
|
||||
|
||||
let queryParams = { category: category.id, q: null };
|
||||
|
||||
if (this.mapUi.currentCenter) {
|
||||
const { lat, lon } = this.mapUi.currentCenter;
|
||||
queryParams.lat = parseFloat(lat).toFixed(4);
|
||||
queryParams.lon = parseFloat(lon).toFixed(4);
|
||||
}
|
||||
|
||||
this.router.transitionTo('search', { queryParams });
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="category-chips-scroll">
|
||||
<div class="category-chips-container">
|
||||
{{#each this.categories as |category|}}
|
||||
<button
|
||||
type="button"
|
||||
class="category-chip"
|
||||
{{on "click" (fn this.searchCategory category)}}
|
||||
aria-label={{category.label}}
|
||||
>
|
||||
<Icon @name={{category.icon}} @size={{16}} />
|
||||
<span>{{category.label}}</span>
|
||||
</button>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
@@ -914,6 +914,7 @@ export default class MapComponent extends Component {
|
||||
const [maxLon, maxLat] = toLonLat([extent[2], extent[3]]);
|
||||
|
||||
const bbox = { minLat, minLon, maxLat, maxLon };
|
||||
this.mapUi.updateBounds(bbox);
|
||||
await this.storage.loadPlacesInBounds(bbox);
|
||||
this.loadBookmarks(this.storage.placesInView);
|
||||
|
||||
|
||||
@@ -27,6 +27,10 @@ export default class SearchBoxComponent extends Component {
|
||||
@action
|
||||
handleInput(event) {
|
||||
this.query = event.target.value;
|
||||
if (this.args.onQueryChange) {
|
||||
this.args.onQueryChange(this.query);
|
||||
}
|
||||
|
||||
if (this.query.length < 2) {
|
||||
this.results = [];
|
||||
return;
|
||||
@@ -35,6 +39,7 @@ export default class SearchBoxComponent extends Component {
|
||||
this.searchTask.perform();
|
||||
}
|
||||
|
||||
|
||||
searchTask = task({ restartable: true }, async () => {
|
||||
await timeout(300);
|
||||
|
||||
@@ -121,8 +126,10 @@ export default class SearchBoxComponent extends Component {
|
||||
clear() {
|
||||
this.query = '';
|
||||
this.results = [];
|
||||
this.router.transitionTo('index'); // Or stay on current page?
|
||||
// Usually clear just clears the input.
|
||||
if (this.args.onQueryChange) {
|
||||
this.args.onQueryChange('');
|
||||
}
|
||||
this.router.transitionTo('index');
|
||||
}
|
||||
|
||||
<template>
|
||||
|
||||
Reference in New Issue
Block a user