Recipe Search Dialog

This commit is contained in:
Kuchenpirat 2025-07-30 11:03:28 +00:00
commit cae6e18c58

View file

@ -52,10 +52,6 @@
<div class="mr-auto"> <div class="mr-auto">
{{ $t("search.results") }} {{ $t("search.results") }}
</div> </div>
<!-- <router-link
:to="advancedSearchUrl"
class="text-primary"
> {{ $t("search.advanced-search") }} </router-link> -->
</v-card-actions> </v-card-actions>
<RecipeCardMobile <RecipeCardMobile
@ -76,7 +72,7 @@
</div> </div>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import RecipeCardMobile from "./RecipeCardMobile.vue"; import RecipeCardMobile from "./RecipeCardMobile.vue";
import { useLoggedInState } from "~/composables/use-logged-in-state"; import { useLoggedInState } from "~/composables/use-logged-in-state";
import type { RecipeSummary } from "~/lib/api/types/recipe"; import type { RecipeSummary } from "~/lib/api/types/recipe";
@ -85,114 +81,104 @@ import { useRecipeSearch } from "~/composables/recipes/use-recipe-search";
import { usePublicExploreApi } from "~/composables/api/api-client"; import { usePublicExploreApi } from "~/composables/api/api-client";
const SELECTED_EVENT = "selected"; const SELECTED_EVENT = "selected";
export default defineNuxtComponent({
components: {
RecipeCardMobile,
},
setup(_, context) { // Define emits
const $auth = useMealieAuth(); const emit = defineEmits<{
const state = reactive({ selected: [recipe: RecipeSummary];
loading: false, }>();
selectedIndex: -1,
});
// =========================================================================== const $auth = useMealieAuth();
// Dialog State Management const loading = ref(false);
const dialog = ref(false); const selectedIndex = ref(-1);
// Reset or Grab Recipes on Change // ===========================================================================
watch(dialog, (val) => { // Dialog State Management
if (!val) { const dialog = ref(false);
search.query.value = "";
state.selectedIndex = -1;
search.data.value = [];
}
});
// =========================================================================== // Reset or Grab Recipes on Change
// Event Handlers watch(dialog, (val) => {
if (!val) {
search.query.value = "";
selectedIndex.value = -1;
search.data.value = [];
}
});
function selectRecipe() { // ===========================================================================
const recipeCards = document.getElementsByClassName("arrow-nav"); // Event Handlers
if (recipeCards) {
if (state.selectedIndex < 0) {
state.selectedIndex = -1;
document.getElementById("arrow-search")?.focus();
return;
}
if (state.selectedIndex >= recipeCards.length) { function selectRecipe() {
state.selectedIndex = recipeCards.length - 1; const recipeCards = document.getElementsByClassName("arrow-nav");
} if (recipeCards) {
if (selectedIndex.value < 0) {
(recipeCards[state.selectedIndex] as HTMLElement).focus(); selectedIndex.value = -1;
} document.getElementById("arrow-search")?.focus();
return;
} }
function onUpDown(e: KeyboardEvent) { if (selectedIndex.value >= recipeCards.length) {
if (e.key === "Enter") { selectedIndex.value = recipeCards.length - 1;
console.log(document.activeElement);
// (document.activeElement as HTMLElement).click();
}
else if (e.key === "ArrowUp") {
e.preventDefault();
state.selectedIndex--;
}
else if (e.key === "ArrowDown") {
e.preventDefault();
state.selectedIndex++;
}
else {
return;
}
selectRecipe();
} }
watch(dialog, (val) => { (recipeCards[selectedIndex.value] as HTMLElement).focus();
if (!val) { }
document.removeEventListener("keyup", onUpDown); }
}
else {
document.addEventListener("keyup", onUpDown);
}
});
const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || ""); function onUpDown(e: KeyboardEvent) {
const route = useRoute(); if (e.key === "Enter") {
const advancedSearchUrl = computed(() => `/g/${groupSlug.value}`); console.log(document.activeElement);
watch(route, close); // (document.activeElement as HTMLElement).click();
}
else if (e.key === "ArrowUp") {
e.preventDefault();
selectedIndex.value--;
}
else if (e.key === "ArrowDown") {
e.preventDefault();
selectedIndex.value++;
}
else {
return;
}
selectRecipe();
}
function open() { watch(dialog, (val) => {
dialog.value = true; if (!val) {
} document.removeEventListener("keyup", onUpDown);
function close() { }
dialog.value = false; else {
} document.addEventListener("keyup", onUpDown);
}
});
// =========================================================================== const route = useRoute();
// Basic Search const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || "");
const { isOwnGroup } = useLoggedInState(); watch(route, close);
const api = isOwnGroup.value ? useUserApi() : usePublicExploreApi(groupSlug.value).explore;
const search = useRecipeSearch(api);
// Select Handler function open() {
dialog.value = true;
}
function close() {
dialog.value = false;
}
function handleSelect(recipe: RecipeSummary) { // ===========================================================================
close(); // Basic Search
context.emit(SELECTED_EVENT, recipe); const { isOwnGroup } = useLoggedInState();
} const api = isOwnGroup.value ? useUserApi() : usePublicExploreApi(groupSlug.value).explore;
const search = useRecipeSearch(api);
return { // Select Handler
...toRefs(state), function handleSelect(recipe: RecipeSummary) {
advancedSearchUrl, close();
dialog, emit(SELECTED_EVENT, recipe);
open, }
close,
handleSelect, // Expose functions to parent components
search, defineExpose({
}; open,
}, close,
}); });
</script> </script>