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
const dialog = ref(false);
// Reset or Grab Recipes on Change
watch(dialog, (val) => {
if (!val) { if (!val) {
search.query.value = ""; search.query.value = "";
state.selectedIndex = -1; selectedIndex.value = -1;
search.data.value = []; search.data.value = [];
} }
}); });
// =========================================================================== // ===========================================================================
// Event Handlers // Event Handlers
function selectRecipe() { function selectRecipe() {
const recipeCards = document.getElementsByClassName("arrow-nav"); const recipeCards = document.getElementsByClassName("arrow-nav");
if (recipeCards) { if (recipeCards) {
if (state.selectedIndex < 0) { if (selectedIndex.value < 0) {
state.selectedIndex = -1; selectedIndex.value = -1;
document.getElementById("arrow-search")?.focus(); document.getElementById("arrow-search")?.focus();
return; return;
} }
if (state.selectedIndex >= recipeCards.length) { if (selectedIndex.value >= recipeCards.length) {
state.selectedIndex = recipeCards.length - 1; selectedIndex.value = recipeCards.length - 1;
} }
(recipeCards[state.selectedIndex] as HTMLElement).focus(); (recipeCards[selectedIndex.value] as HTMLElement).focus();
}
} }
}
function onUpDown(e: KeyboardEvent) { function onUpDown(e: KeyboardEvent) {
if (e.key === "Enter") { if (e.key === "Enter") {
console.log(document.activeElement); console.log(document.activeElement);
// (document.activeElement as HTMLElement).click(); // (document.activeElement as HTMLElement).click();
} }
else if (e.key === "ArrowUp") { else if (e.key === "ArrowUp") {
e.preventDefault(); e.preventDefault();
state.selectedIndex--; selectedIndex.value--;
} }
else if (e.key === "ArrowDown") { else if (e.key === "ArrowDown") {
e.preventDefault(); e.preventDefault();
state.selectedIndex++; selectedIndex.value++;
} }
else { else {
return; return;
} }
selectRecipe(); selectRecipe();
} }
watch(dialog, (val) => { watch(dialog, (val) => {
if (!val) { if (!val) {
document.removeEventListener("keyup", onUpDown); document.removeEventListener("keyup", onUpDown);
} }
else { else {
document.addEventListener("keyup", onUpDown); document.addEventListener("keyup", onUpDown);
} }
}); });
const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || ""); const route = useRoute();
const route = useRoute(); const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || "");
const advancedSearchUrl = computed(() => `/g/${groupSlug.value}`); watch(route, close);
watch(route, close);
function open() { function open() {
dialog.value = true; dialog.value = true;
} }
function close() { function close() {
dialog.value = false; dialog.value = false;
} }
// =========================================================================== // ===========================================================================
// Basic Search // Basic Search
const { isOwnGroup } = useLoggedInState(); const { isOwnGroup } = useLoggedInState();
const api = isOwnGroup.value ? useUserApi() : usePublicExploreApi(groupSlug.value).explore; const api = isOwnGroup.value ? useUserApi() : usePublicExploreApi(groupSlug.value).explore;
const search = useRecipeSearch(api); const search = useRecipeSearch(api);
// Select Handler // Select Handler
function handleSelect(recipe: RecipeSummary) {
function handleSelect(recipe: RecipeSummary) {
close(); close();
context.emit(SELECTED_EVENT, recipe); emit(SELECTED_EVENT, recipe);
} }
return { // Expose functions to parent components
...toRefs(state), defineExpose({
advancedSearchUrl,
dialog,
open, open,
close, close,
handleSelect,
search,
};
},
}); });
</script> </script>