188 lines
4.8 KiB
Vue
188 lines
4.8 KiB
Vue
<template>
|
|
<transition name="modalAni">
|
|
<div v-if="show" class="modal" @click.self="closeModal">
|
|
<div class="modalContainer">
|
|
<div class="modalWrapper">
|
|
<span class="closeModalImg" @click="closeModal">×</span>
|
|
<h1>Search Soundtracks</h1>
|
|
<div class="searchContainer">
|
|
<input
|
|
type="text"
|
|
v-model="searchTerm"
|
|
@input="debouncedSearch"
|
|
placeholder="Search for soundtracks..."
|
|
class="searchInput"
|
|
/>
|
|
<div v-if="searchTerm.length > 0" class="searchResults">
|
|
<div v-if="loading" class="loading">Searching...</div>
|
|
<div v-else-if="results.length > 0" class="resultsList">
|
|
<div v-for="soundtrack in results" :key="soundtrack" class="resultItem">
|
|
{{ soundtrack }}
|
|
</div>
|
|
</div>
|
|
<div v-else class="noResults">No results found</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</transition>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
data() {
|
|
return {
|
|
show: false,
|
|
searchTerm: "",
|
|
results: [],
|
|
loading: false,
|
|
searchTimeout: null,
|
|
};
|
|
},
|
|
methods: {
|
|
openModal() {
|
|
this.show = true;
|
|
this.searchTerm = "";
|
|
this.results = [];
|
|
},
|
|
closeModal() {
|
|
this.show = false;
|
|
this.searchTerm = "";
|
|
this.results = [];
|
|
this.loading = false;
|
|
if (this.searchTimeout) {
|
|
clearTimeout(this.searchTimeout);
|
|
this.searchTimeout = null;
|
|
}
|
|
},
|
|
debouncedSearch() {
|
|
// Clear existing timeout
|
|
if (this.searchTimeout) {
|
|
clearTimeout(this.searchTimeout);
|
|
}
|
|
// Set new timeout
|
|
this.searchTimeout = setTimeout(() => {
|
|
this.performSearch();
|
|
}, 250);
|
|
},
|
|
async performSearch() {
|
|
if (this.searchTerm.length === 0) {
|
|
this.results = [];
|
|
return;
|
|
}
|
|
this.loading = true;
|
|
try {
|
|
const response = await this.axios({
|
|
method: "post",
|
|
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/find`,
|
|
data: new URLSearchParams({ search_term: this.searchTerm }),
|
|
headers: {
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
},
|
|
});
|
|
// Parse soundtracks from HTML response
|
|
this.results = this.parseSoundtracksFromResponse(response.data);
|
|
} catch (error) {
|
|
console.error("Error searching soundtracks:", error);
|
|
this.results = [];
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
parseSoundtracksFromResponse(htmlData) {
|
|
// The server returns HTML from FoundSoundtracks templ component
|
|
// It generates divs containing soundtrack names in <p> tags
|
|
const soundtracks = [];
|
|
const parser = new DOMParser();
|
|
const doc = parser.parseFromString(htmlData, "text/html");
|
|
|
|
// Find all p tags inside divs (the templ generates <div><p>{soundtrack}</p></div>)
|
|
const pTags = doc.querySelectorAll("div p");
|
|
for (const p of pTags) {
|
|
const soundtrackName = p.textContent.trim();
|
|
if (soundtrackName) {
|
|
soundtracks.push(soundtrackName);
|
|
}
|
|
}
|
|
|
|
return soundtracks;
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.searchContainer {
|
|
width: 100%;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.searchInput {
|
|
width: 100%;
|
|
padding: 12px;
|
|
font-size: 1.1rem;
|
|
border: 1px solid #777;
|
|
background-color: rgb(66, 66, 66);
|
|
color: white;
|
|
border-radius: 5px;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.searchInput:focus {
|
|
outline: none;
|
|
border-color: #ff9c00;
|
|
box-shadow: 0 0 5px rgba(255, 156, 0, 0.3);
|
|
}
|
|
|
|
.searchResults {
|
|
margin-top: 15px;
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.loading {
|
|
color: #bbb;
|
|
font-style: italic;
|
|
text-align: center;
|
|
padding: 20px;
|
|
}
|
|
|
|
.resultsList {
|
|
width: 100%;
|
|
}
|
|
|
|
.resultItem {
|
|
padding: 12px;
|
|
margin-bottom: 8px;
|
|
background-color: rgb(66, 66, 66);
|
|
border: 1px solid #555;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
.resultItem:hover {
|
|
background-color: #555;
|
|
}
|
|
|
|
.noResults {
|
|
color: #bbb;
|
|
text-align: center;
|
|
padding: 20px;
|
|
font-style: italic;
|
|
}
|
|
|
|
@media only screen and (max-width: 1000px) {
|
|
.searchInput {
|
|
font-size: 1rem;
|
|
padding: 10px;
|
|
}
|
|
|
|
.resultItem {
|
|
padding: 10px;
|
|
font-size: 0.95rem;
|
|
}
|
|
}
|
|
</style>
|