mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 02:03:35 -04:00 
			
		
		
		
	| @@ -122,9 +122,8 @@ | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| <script setup lang="ts"> | ||||
| import Fuse from "fuse.js"; | ||||
|  | ||||
| import { useContextPresets } from "~/composables/use-context-presents"; | ||||
| import RecipeOrganizerDialog from "~/components/Domain/Recipe/RecipeOrganizerDialog.vue"; | ||||
| import { Organizer, type RecipeOrganizer } from "~/lib/api/types/non-generated"; | ||||
| @@ -138,156 +137,128 @@ interface GenericItem { | ||||
|   onHand: boolean; | ||||
| } | ||||
|  | ||||
| export default defineNuxtComponent({ | ||||
|   components: { | ||||
|     RecipeOrganizerDialog, | ||||
|   }, | ||||
|   props: { | ||||
|     items: { | ||||
|       type: Array as () => GenericItem[], | ||||
|       required: true, | ||||
|     }, | ||||
|     icon: { | ||||
|       type: String, | ||||
|       required: true, | ||||
|     }, | ||||
|     itemType: { | ||||
|       type: String as () => RecipeOrganizer, | ||||
|       required: true, | ||||
|     }, | ||||
|   }, | ||||
|   emits: ["update", "delete"], | ||||
|   setup(props, { emit }) { | ||||
|     const state = reactive({ | ||||
|       // Search Options | ||||
|       options: { | ||||
|         ignoreLocation: true, | ||||
|         shouldSort: true, | ||||
|         threshold: 0.2, | ||||
|         location: 0, | ||||
|         distance: 20, | ||||
|         findAllMatches: true, | ||||
|         maxPatternLength: 32, | ||||
|         minMatchCharLength: 1, | ||||
|         keys: ["name"], | ||||
|       }, | ||||
|     }); | ||||
| const props = defineProps<{ | ||||
|   items: GenericItem[]; | ||||
|   icon: string; | ||||
|   itemType: RecipeOrganizer; | ||||
| }>(); | ||||
|  | ||||
|     const $auth = useMealieAuth(); | ||||
|     const route = useRoute(); | ||||
|     const groupSlug = computed(() => route.params.groupSlug as string || $auth.user?.value?.groupSlug || ""); | ||||
| const emit = defineEmits<{ | ||||
|   update: [item: GenericItem]; | ||||
|   delete: [id: string]; | ||||
| }>(); | ||||
|  | ||||
|     // ================================================================= | ||||
|     // Context Menu | ||||
|  | ||||
|     const dialogs = ref({ | ||||
|       organizer: false, | ||||
|       update: false, | ||||
|       delete: false, | ||||
|     }); | ||||
|  | ||||
|     const presets = useContextPresets(); | ||||
|  | ||||
|     const translationKey = computed<string>(() => { | ||||
|       const typeMap = { | ||||
|         categories: "category.category", | ||||
|         tags: "tag.tag", | ||||
|         tools: "tool.tool", | ||||
|         foods: "shopping-list.food", | ||||
|         households: "household.household", | ||||
|       }; | ||||
|       return typeMap[props.itemType] || ""; | ||||
|     }); | ||||
|  | ||||
|     const deleteTarget = ref<GenericItem | null>(null); | ||||
|     const updateTarget = ref<GenericItem | null>(null); | ||||
|  | ||||
|     function confirmDelete(item: GenericItem) { | ||||
|       deleteTarget.value = item; | ||||
|       dialogs.value.delete = true; | ||||
|     } | ||||
|  | ||||
|     function deleteOne() { | ||||
|       if (!deleteTarget.value) { | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       emit("delete", deleteTarget.value.id); | ||||
|     } | ||||
|  | ||||
|     function openUpdateDialog(item: GenericItem) { | ||||
|       updateTarget.value = deepCopy(item); | ||||
|       dialogs.value.update = true; | ||||
|     } | ||||
|  | ||||
|     function updateOne() { | ||||
|       if (!updateTarget.value) { | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       emit("update", updateTarget.value); | ||||
|     } | ||||
|  | ||||
|     // ================================================================ | ||||
|     // Search Functions | ||||
|  | ||||
|     const searchString = useRouteQuery("q", ""); | ||||
|  | ||||
|     const fuse = computed(() => { | ||||
|       return new Fuse(props.items, state.options); | ||||
|     }); | ||||
|  | ||||
|     const fuzzyItems = computed<GenericItem[]>(() => { | ||||
|       if (searchString.value.trim() === "") { | ||||
|         return props.items; | ||||
|       } | ||||
|       const result = fuse.value.search(searchString.value.trim() as string); | ||||
|       return result.map(x => x.item); | ||||
|     }); | ||||
|  | ||||
|     // ================================================================= | ||||
|     // Sorted Items | ||||
|  | ||||
|     const itemsSorted = computed(() => { | ||||
|       const byLetter: { [key: string]: Array<GenericItem> } = {}; | ||||
|  | ||||
|       if (!fuzzyItems.value) { | ||||
|         return byLetter; | ||||
|       } | ||||
|  | ||||
|       [...fuzzyItems.value] | ||||
|         .sort((a, b) => a.name.localeCompare(b.name)) | ||||
|         .forEach((item) => { | ||||
|           const letter = item.name[0].toUpperCase(); | ||||
|           if (!byLetter[letter]) { | ||||
|             byLetter[letter] = []; | ||||
|           } | ||||
|           byLetter[letter].push(item); | ||||
|         }); | ||||
|  | ||||
|       return byLetter; | ||||
|     }); | ||||
|  | ||||
|     function isTitle(str: number | string) { | ||||
|       return typeof str === "string" && str.length === 1; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|       groupSlug, | ||||
|       isTitle, | ||||
|       dialogs, | ||||
|       confirmDelete, | ||||
|       openUpdateDialog, | ||||
|       updateOne, | ||||
|       updateTarget, | ||||
|       deleteOne, | ||||
|       deleteTarget, | ||||
|       Organizer, | ||||
|       presets, | ||||
|       itemsSorted, | ||||
|       searchString, | ||||
|       translationKey, | ||||
|     }; | ||||
| const state = reactive({ | ||||
|   // Search Options | ||||
|   options: { | ||||
|     ignoreLocation: true, | ||||
|     shouldSort: true, | ||||
|     threshold: 0.2, | ||||
|     location: 0, | ||||
|     distance: 20, | ||||
|     findAllMatches: true, | ||||
|     maxPatternLength: 32, | ||||
|     minMatchCharLength: 1, | ||||
|     keys: ["name"], | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| const $auth = useMealieAuth(); | ||||
| const route = useRoute(); | ||||
| const groupSlug = computed(() => route.params.groupSlug as string || $auth.user?.value?.groupSlug || ""); | ||||
|  | ||||
| // ================================================================= | ||||
| // Context Menu | ||||
|  | ||||
| const dialogs = ref({ | ||||
|   organizer: false, | ||||
|   update: false, | ||||
|   delete: false, | ||||
| }); | ||||
|  | ||||
| const presets = useContextPresets(); | ||||
|  | ||||
| const translationKey = computed<string>(() => { | ||||
|   const typeMap = { | ||||
|     categories: "category.category", | ||||
|     tags: "tag.tag", | ||||
|     tools: "tool.tool", | ||||
|     foods: "shopping-list.food", | ||||
|     households: "household.household", | ||||
|   }; | ||||
|   return typeMap[props.itemType] || ""; | ||||
| }); | ||||
|  | ||||
| const deleteTarget = ref<GenericItem | null>(null); | ||||
| const updateTarget = ref<GenericItem | null>(null); | ||||
|  | ||||
| function confirmDelete(item: GenericItem) { | ||||
|   deleteTarget.value = item; | ||||
|   dialogs.value.delete = true; | ||||
| } | ||||
|  | ||||
| function deleteOne() { | ||||
|   if (!deleteTarget.value) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   emit("delete", deleteTarget.value.id); | ||||
| } | ||||
|  | ||||
| function openUpdateDialog(item: GenericItem) { | ||||
|   updateTarget.value = deepCopy(item); | ||||
|   dialogs.value.update = true; | ||||
| } | ||||
|  | ||||
| function updateOne() { | ||||
|   if (!updateTarget.value) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   emit("update", updateTarget.value); | ||||
| } | ||||
|  | ||||
| // ================================================================ | ||||
| // Search Functions | ||||
|  | ||||
| const searchString = useRouteQuery("q", ""); | ||||
|  | ||||
| const fuse = computed(() => { | ||||
|   return new Fuse(props.items, state.options); | ||||
| }); | ||||
|  | ||||
| const fuzzyItems = computed<GenericItem[]>(() => { | ||||
|   if (searchString.value.trim() === "") { | ||||
|     return props.items; | ||||
|   } | ||||
|   const result = fuse.value.search(searchString.value.trim() as string); | ||||
|   return result.map(x => x.item); | ||||
| }); | ||||
|  | ||||
| // ================================================================= | ||||
| // Sorted Items | ||||
|  | ||||
| const itemsSorted = computed(() => { | ||||
|   const byLetter: { [key: string]: Array<GenericItem> } = {}; | ||||
|  | ||||
|   if (!fuzzyItems.value) { | ||||
|     return byLetter; | ||||
|   } | ||||
|  | ||||
|   [...fuzzyItems.value] | ||||
|     .sort((a, b) => a.name.localeCompare(b.name)) | ||||
|     .forEach((item) => { | ||||
|       const letter = item.name[0].toUpperCase(); | ||||
|       if (!byLetter[letter]) { | ||||
|         byLetter[letter] = []; | ||||
|       } | ||||
|       byLetter[letter].push(item); | ||||
|     }); | ||||
|  | ||||
|   return byLetter; | ||||
| }); | ||||
|  | ||||
| function isTitle(str: number | string) { | ||||
|   return typeof str === "string" && str.length === 1; | ||||
| } | ||||
| </script> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user