Add searchbar component
This commit is contained in:
parent
464596cd11
commit
39bc660478
154
src/searchbar.rs
Normal file
154
src/searchbar.rs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
use crate::search::search;
|
||||||
|
use crate::playstatus::PlayStatus;
|
||||||
|
use crate::song::Song;
|
||||||
|
use crate::models::Album;
|
||||||
|
use crate::models::Artist;
|
||||||
|
use crate::models::Song;
|
||||||
|
use leptos::*;
|
||||||
|
use leptos::ev::*;
|
||||||
|
use leptos::leptos_dom::*;
|
||||||
|
use leptos_icons::*;
|
||||||
|
use leptos_icons::BsIcon::*;
|
||||||
|
|
||||||
|
const OPTIONS_BTN_SIZE: &str = "2.5rem";
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn SearchBar(status: RwSignal<PlayStatus>) -> impl IntoView {
|
||||||
|
let search_query = create_rw_signal(String::new());
|
||||||
|
let search_results = create_rw_signal((Vec::<(Album, f32)>::new(), Vec::<(Artist, f32)>::new(), Vec::<(Song, f32)>::new()));
|
||||||
|
let search_limit = 10;
|
||||||
|
|
||||||
|
let on_input = move |e: Event| {
|
||||||
|
search_query.set(event_target_value(&e));
|
||||||
|
|
||||||
|
log!("Search Query: {:?}", search_query.get_untracked());
|
||||||
|
|
||||||
|
if search_query.get_untracked().len() < 3 {
|
||||||
|
search_results.set((Vec::<(Album, f32)>::new(), Vec::<(Artist, f32)>::new(), Vec::<(Song, f32)>::new()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
spawn_local(async move {
|
||||||
|
log!("Searching for: {:?}", search_query.get_untracked());
|
||||||
|
let results = search(search_query.get_untracked(), search_limit).await;
|
||||||
|
match results {
|
||||||
|
Ok((albums, artists, songs)) => {
|
||||||
|
search_results.set((albums, artists, songs));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log!("Error searching: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_disabled = move |_e: FocusEvent| {
|
||||||
|
|
||||||
|
log!("Search Bar Disabled");
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_enabled = move |_e: FocusEvent| {
|
||||||
|
status.update(|status| {
|
||||||
|
status.search_active = true;
|
||||||
|
});
|
||||||
|
log!("Search Bar Enabled");
|
||||||
|
};
|
||||||
|
|
||||||
|
let prevent_focus = move |e: MouseEvent| {
|
||||||
|
e.prevent_default();
|
||||||
|
};
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class="search-container">
|
||||||
|
<div class="search-bar">
|
||||||
|
<input type="search" placeholder="Search" on:input=on_input on:blur=on_disabled on:focus=on_enabled/>
|
||||||
|
</div>
|
||||||
|
<div class="search-results">
|
||||||
|
<ul class="search-results-list">
|
||||||
|
{
|
||||||
|
move || search_results.with(|(albums, artists, songs)| -> Vec<_> {
|
||||||
|
let mut album_index = 0;
|
||||||
|
let mut artist_index = 0;
|
||||||
|
let mut song_index = 0;
|
||||||
|
let mut views = Vec::new();
|
||||||
|
while album_index < albums.len() || artist_index < artists.len() || song_index < songs.len() {
|
||||||
|
const RM_BTN_SIZE: &str = "2.5rem";
|
||||||
|
let album_score = if album_index < albums.len() { albums[album_index].1 } else { f32::MAX };
|
||||||
|
let artist_score = if artist_index < artists.len() { artists[artist_index].1 } else { f32::MAX };
|
||||||
|
let song_score = if song_index < songs.len() { songs[song_index].1 } else { f32::MAX };
|
||||||
|
if artist_score <= album_score && artist_score <= song_score {
|
||||||
|
let artist = &artists[artist_index].0;
|
||||||
|
artist_index += 1;
|
||||||
|
views.push(view! {
|
||||||
|
<li class="search-result">
|
||||||
|
<div class="result-container">
|
||||||
|
<div class="search-result-artist">
|
||||||
|
{artist.name.clone()}
|
||||||
|
</div>
|
||||||
|
<div class="right-side-result">
|
||||||
|
<div class="search-item-type">
|
||||||
|
"(Artist)"
|
||||||
|
</div>
|
||||||
|
<button class="search-result-options" on:mousedown=prevent_focus>
|
||||||
|
<Icon class="search-result-options-icon" width=OPTIONS_BTN_SIZE height=OPTIONS_BTN_SIZE icon=Icon::from(BsThreeDotsVertical) />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if album_score <= artist_score && album_score <= song_score {
|
||||||
|
let album = &albums[album_index].0;
|
||||||
|
album_index += 1;
|
||||||
|
views.push(view! {
|
||||||
|
<li class="search-result">
|
||||||
|
<div class="result-container">
|
||||||
|
<div class="search-result-album">
|
||||||
|
{album.title.clone()}
|
||||||
|
{match album.release_date {
|
||||||
|
Some(date) => format!(" ({})", date),
|
||||||
|
None => "".to_string()
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div class="right-side-result">
|
||||||
|
<div class="search-item-type">
|
||||||
|
"(Album)"
|
||||||
|
</div>
|
||||||
|
<button class="search-result-options" on:mousedown=prevent_focus>
|
||||||
|
<Icon class="search-result-options-icon" width=OPTIONS_BTN_SIZE height=OPTIONS_BTN_SIZE icon=Icon::from(BsThreeDotsVertical) />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if song_score <= artist_score && song_score <= album_score {
|
||||||
|
let song = &songs[song_index].0;
|
||||||
|
song_index += 1;
|
||||||
|
views.push(view! {
|
||||||
|
<li class="search-result">
|
||||||
|
<div class="result-container">
|
||||||
|
<Song song_image_path=match song.image_path.clone() {
|
||||||
|
Some(path) => path,
|
||||||
|
None => "".to_string()
|
||||||
|
} song_title=song.title.clone() song_artist="".to_string() />
|
||||||
|
<div class="right-side-result">
|
||||||
|
<div class="search-item-type">
|
||||||
|
"(Song)"
|
||||||
|
</div>
|
||||||
|
<button class="search-result-options" on:mousedown=prevent_focus>
|
||||||
|
<Icon class="search-result-options-icon" width=OPTIONS_BTN_SIZE height=OPTIONS_BTN_SIZE icon=Icon::from(BsThreeDotsVertical) />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
views
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,8 @@
|
|||||||
@import 'queue.scss';
|
@import 'queue.scss';
|
||||||
@import 'login.scss';
|
@import 'login.scss';
|
||||||
@import 'signup.scss';
|
@import 'signup.scss';
|
||||||
|
@import 'song.scss';
|
||||||
|
@import 'searchbar.scss';
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
@ -11,3 +13,8 @@ body {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.home {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
99
style/searchbar.scss
Normal file
99
style/searchbar.scss
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
@import 'theme.scss';
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
display: flex;
|
||||||
|
margin: 5px auto;
|
||||||
|
margin-left: 282px;
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 100%;
|
||||||
|
width: calc(100% - 690px);
|
||||||
|
background-color: $search-background-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: left;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar input[type="search"] {
|
||||||
|
background-color: gray;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
color: black;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 16px;
|
||||||
|
&::placeholder {
|
||||||
|
color: rgb(61, 61, 61);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results {
|
||||||
|
background-color: #212121;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
display: flex;
|
||||||
|
margin-top: 55px;
|
||||||
|
height: calc(100% - 143px);
|
||||||
|
width: calc(100% - 690px);
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
li {
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
padding: 10px;
|
||||||
|
border-bottom: 1px solid #333;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: white;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-top: 2px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-side-result {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.search-item-type {
|
||||||
|
color: #666;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-options {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ $play-bar-background-color: #212121;
|
|||||||
$play-grad-start: #0a0533;
|
$play-grad-start: #0a0533;
|
||||||
$play-grad-end: $accent-color;
|
$play-grad-end: $accent-color;
|
||||||
$queue-background-color: $play-bar-background-color;
|
$queue-background-color: $play-bar-background-color;
|
||||||
|
$search-background-color: $play-bar-background-color;
|
||||||
|
|
||||||
$auth-inputs: #796dd4;
|
$auth-inputs: #796dd4;
|
||||||
$auth-containers: white;
|
$auth-containers: white;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user