search bar
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
||||
import { searchStore } from '../search-store';
|
||||
|
||||
/**
|
||||
* SearchController connects a Lit component to the SearchStore.
|
||||
*
|
||||
* Usage in a component:
|
||||
*
|
||||
* private search = new SearchController(this);
|
||||
*
|
||||
* render() {
|
||||
* const term = this.search.term;
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
export class SearchController implements ReactiveController {
|
||||
private host: ReactiveControllerHost;
|
||||
private unsubscribe?: () => void;
|
||||
|
||||
constructor(host: ReactiveControllerHost) {
|
||||
this.host = host;
|
||||
host.addController(this);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// LIFECYCLE HOOKS
|
||||
// ===================================================================
|
||||
|
||||
hostConnected(): void {
|
||||
this.unsubscribe = searchStore.subscribe(() => {
|
||||
this.host.requestUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
hostDisconnected(): void {
|
||||
this.unsubscribe?.();
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// DATA ACCESS
|
||||
// ===================================================================
|
||||
|
||||
get term(): string {
|
||||
return searchStore.getTerm();
|
||||
}
|
||||
|
||||
set term(value: string) {
|
||||
searchStore.setTerm(value);
|
||||
}
|
||||
|
||||
get isSearchableView(): boolean {
|
||||
return searchStore.isSearchableView();
|
||||
}
|
||||
}
|
||||
@@ -7,3 +7,5 @@ export { QueueController } from './controllers/queue-controller';
|
||||
export { themeStore } from './theme-store';
|
||||
export type { ThemeState, BackgroundShade } from './theme-store';
|
||||
export { ThemeController } from './controllers/theme-controller';
|
||||
export { searchStore } from './search-store';
|
||||
export { SearchController } from './controllers/search-controller';
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/** Searchable views that respond to the global search term. */
|
||||
const SEARCHABLE_VIEWS = new Set(['tracks', 'albums', 'playlists']);
|
||||
|
||||
type Subscriber = () => void;
|
||||
|
||||
class SearchStore {
|
||||
private term = '';
|
||||
private currentView = 'tracks';
|
||||
private subscribers = new Set<Subscriber>();
|
||||
|
||||
// ===================================================================
|
||||
// SEARCH TERM
|
||||
// ===================================================================
|
||||
|
||||
getTerm(): string {
|
||||
return this.term;
|
||||
}
|
||||
|
||||
setTerm(term: string): void {
|
||||
if (term === this.term) return;
|
||||
|
||||
this.term = term;
|
||||
this.notify();
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// CURRENT VIEW
|
||||
// ===================================================================
|
||||
|
||||
getCurrentView(): string {
|
||||
return this.currentView;
|
||||
}
|
||||
|
||||
setCurrentView(view: string): void {
|
||||
if (view === this.currentView) return;
|
||||
|
||||
this.currentView = view;
|
||||
this.notify();
|
||||
}
|
||||
|
||||
isSearchableView(): boolean {
|
||||
return SEARCHABLE_VIEWS.has(this.currentView);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// SUBSCRIPTION SYSTEM
|
||||
// ===================================================================
|
||||
|
||||
subscribe(callback: Subscriber): () => void {
|
||||
this.subscribers.add(callback);
|
||||
|
||||
return () => this.subscribers.delete(callback);
|
||||
}
|
||||
|
||||
private notify(): void {
|
||||
this.subscribers.forEach((callback) => callback());
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton instance.
|
||||
export const searchStore = new SearchStore();
|
||||
Reference in New Issue
Block a user