mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 06:23:34 -07:00
Recipe Context Menu
This commit is contained in:
parent
275b89a9eb
commit
1cb1a0552b
1 changed files with 310 additions and 361 deletions
|
@ -100,7 +100,7 @@
|
||||||
:open-on-hover="$vuetify.display.mdAndUp"
|
:open-on-hover="$vuetify.display.mdAndUp"
|
||||||
content-class="d-print-none"
|
content-class="d-print-none"
|
||||||
>
|
>
|
||||||
<template #activator="{ props }">
|
<template #activator="{ props: activatorProps }">
|
||||||
<v-btn
|
<v-btn
|
||||||
icon
|
icon
|
||||||
:variant="fab ? 'flat' : undefined"
|
:variant="fab ? 'flat' : undefined"
|
||||||
|
@ -108,7 +108,7 @@
|
||||||
:size="fab ? 'small' : undefined"
|
:size="fab ? 'small' : undefined"
|
||||||
:color="fab ? 'info' : 'secondary'"
|
:color="fab ? 'info' : 'secondary'"
|
||||||
:fab="fab"
|
:fab="fab"
|
||||||
v-bind="props"
|
v-bind="activatorProps"
|
||||||
@click.prevent
|
@click.prevent
|
||||||
>
|
>
|
||||||
<v-icon
|
<v-icon
|
||||||
|
@ -150,7 +150,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import RecipeDialogAddToShoppingList from "./RecipeDialogAddToShoppingList.vue";
|
import RecipeDialogAddToShoppingList from "./RecipeDialogAddToShoppingList.vue";
|
||||||
import RecipeDialogPrintPreferences from "./RecipeDialogPrintPreferences.vue";
|
import RecipeDialogPrintPreferences from "./RecipeDialogPrintPreferences.vue";
|
||||||
import RecipeDialogShare from "./RecipeDialogShare.vue";
|
import RecipeDialogShare from "./RecipeDialogShare.vue";
|
||||||
|
@ -186,16 +186,22 @@ export interface ContextMenuItem {
|
||||||
isPublic: boolean;
|
isPublic: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineNuxtComponent({
|
interface Props {
|
||||||
components: {
|
useItems?: ContextMenuIncludes;
|
||||||
RecipeDialogAddToShoppingList,
|
appendItems?: ContextMenuItem[];
|
||||||
RecipeDialogPrintPreferences,
|
leadingItems?: ContextMenuItem[];
|
||||||
RecipeDialogShare,
|
menuTop?: boolean;
|
||||||
},
|
fab?: boolean;
|
||||||
props: {
|
color?: string;
|
||||||
useItems: {
|
slug: string;
|
||||||
type: Object as () => ContextMenuIncludes,
|
menuIcon?: string | null;
|
||||||
default: () => ({
|
name: string;
|
||||||
|
recipe?: Recipe;
|
||||||
|
recipeId: string;
|
||||||
|
recipeScale?: number;
|
||||||
|
}
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
useItems: () => ({
|
||||||
delete: true,
|
delete: true,
|
||||||
edit: true,
|
edit: true,
|
||||||
download: true,
|
download: true,
|
||||||
|
@ -207,78 +213,41 @@ export default defineNuxtComponent({
|
||||||
share: true,
|
share: true,
|
||||||
recipeActions: true,
|
recipeActions: true,
|
||||||
}),
|
}),
|
||||||
},
|
appendItems: () => [],
|
||||||
// Append items are added at the end of the useItems list
|
leadingItems: () => [],
|
||||||
appendItems: {
|
menuTop: true,
|
||||||
type: Array as () => ContextMenuItem[],
|
fab: false,
|
||||||
default: () => [],
|
color: "primary",
|
||||||
},
|
menuIcon: null,
|
||||||
// Append items are added at the beginning of the useItems list
|
recipe: undefined,
|
||||||
leadingItems: {
|
recipeScale: 1,
|
||||||
type: Array as () => ContextMenuItem[],
|
});
|
||||||
default: () => [],
|
|
||||||
},
|
const emit = defineEmits<{
|
||||||
menuTop: {
|
[key: string]: any;
|
||||||
type: Boolean,
|
delete: [slug: string];
|
||||||
default: true,
|
}>();
|
||||||
},
|
|
||||||
fab: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
default: "primary",
|
|
||||||
},
|
|
||||||
slug: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
menuIcon: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
required: true,
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
recipe: {
|
|
||||||
type: Object as () => Recipe,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
recipeId: {
|
|
||||||
required: true,
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
recipeScale: {
|
|
||||||
type: Number,
|
|
||||||
default: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ["delete"],
|
|
||||||
setup(props, context) {
|
|
||||||
const api = useUserApi();
|
const api = useUserApi();
|
||||||
|
|
||||||
const state = reactive({
|
const printPreferencesDialog = ref(false);
|
||||||
printPreferencesDialog: false,
|
const shareDialog = ref(false);
|
||||||
shareDialog: false,
|
const recipeDeleteDialog = ref(false);
|
||||||
recipeDeleteDialog: false,
|
const mealplannerDialog = ref(false);
|
||||||
mealplannerDialog: false,
|
const shoppingListDialog = ref(false);
|
||||||
shoppingListDialog: false,
|
const recipeDuplicateDialog = ref(false);
|
||||||
recipeDuplicateDialog: false,
|
const recipeName = ref(props.name);
|
||||||
recipeName: props.name,
|
const loading = ref(false);
|
||||||
loading: false,
|
const menuItems = ref<ContextMenuItem[]>([]);
|
||||||
menuItems: [] as ContextMenuItem[],
|
const newMealdate = ref(new Date());
|
||||||
newMealdate: new Date(),
|
const newMealType = ref<PlanEntryType>("dinner");
|
||||||
newMealType: "dinner" as PlanEntryType,
|
const pickerMenu = ref(false);
|
||||||
pickerMenu: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const newMealdateString = computed(() => {
|
const newMealdateString = computed(() => {
|
||||||
// Format the date to YYYY-MM-DD in the same timezone as newMealdate
|
// Format the date to YYYY-MM-DD in the same timezone as newMealdate
|
||||||
const year = state.newMealdate.getFullYear();
|
const year = newMealdate.value.getFullYear();
|
||||||
const month = String(state.newMealdate.getMonth() + 1).padStart(2, "0");
|
const month = String(newMealdate.value.getMonth() + 1).padStart(2, "0");
|
||||||
const day = String(state.newMealdate.getDate()).padStart(2, "0");
|
const day = String(newMealdate.value.getDate()).padStart(2, "0");
|
||||||
return `${year}-${month}-${day}`;
|
return `${year}-${month}-${day}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -365,7 +334,7 @@ export default defineNuxtComponent({
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add leading and Appending Items
|
// Add leading and Appending Items
|
||||||
state.menuItems = [...state.menuItems, ...props.leadingItems, ...props.appendItems];
|
menuItems.value = [...menuItems.value, ...props.leadingItems, ...props.appendItems];
|
||||||
|
|
||||||
const icon = props.menuIcon || $globals.icons.dotsVertical;
|
const icon = props.menuIcon || $globals.icons.dotsVertical;
|
||||||
|
|
||||||
|
@ -398,7 +367,7 @@ export default defineNuxtComponent({
|
||||||
|
|
||||||
const item = defaultItems[key];
|
const item = defaultItems[key];
|
||||||
if (item && (item.isPublic || isOwnGroup.value)) {
|
if (item && (item.isPublic || isOwnGroup.value)) {
|
||||||
state.menuItems.push(item);
|
menuItems.value.push(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,7 +407,7 @@ export default defineNuxtComponent({
|
||||||
if (data?.slug) {
|
if (data?.slug) {
|
||||||
router.push(`/g/${groupSlug.value}`);
|
router.push(`/g/${groupSlug.value}`);
|
||||||
}
|
}
|
||||||
context.emit("delete", props.slug);
|
emit("delete", props.slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
const download = useDownloader();
|
const download = useDownloader();
|
||||||
|
@ -454,7 +423,7 @@ export default defineNuxtComponent({
|
||||||
async function addRecipeToPlan() {
|
async function addRecipeToPlan() {
|
||||||
const { response } = await api.mealplans.createOne({
|
const { response } = await api.mealplans.createOne({
|
||||||
date: newMealdateString.value,
|
date: newMealdateString.value,
|
||||||
entryType: state.newMealType,
|
entryType: newMealType.value,
|
||||||
title: "",
|
title: "",
|
||||||
text: "",
|
text: "",
|
||||||
recipeId: props.recipeId,
|
recipeId: props.recipeId,
|
||||||
|
@ -469,7 +438,7 @@ export default defineNuxtComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
async function duplicateRecipe() {
|
async function duplicateRecipe() {
|
||||||
const { data } = await api.recipes.duplicateOne(props.slug, state.recipeName);
|
const { data } = await api.recipes.duplicateOne(props.slug, recipeName.value);
|
||||||
if (data && data.slug) {
|
if (data && data.slug) {
|
||||||
router.push(`/g/${groupSlug.value}/r/${data.slug}`);
|
router.push(`/g/${groupSlug.value}/r/${data.slug}`);
|
||||||
}
|
}
|
||||||
|
@ -479,21 +448,21 @@ export default defineNuxtComponent({
|
||||||
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
||||||
const eventHandlers: { [key: string]: () => void | Promise<any> } = {
|
const eventHandlers: { [key: string]: () => void | Promise<any> } = {
|
||||||
delete: () => {
|
delete: () => {
|
||||||
state.recipeDeleteDialog = true;
|
recipeDeleteDialog.value = true;
|
||||||
},
|
},
|
||||||
edit: () => router.push(`/g/${groupSlug.value}/r/${props.slug}` + "?edit=true"),
|
edit: () => router.push(`/g/${groupSlug.value}/r/${props.slug}` + "?edit=true"),
|
||||||
download: handleDownloadEvent,
|
download: handleDownloadEvent,
|
||||||
duplicate: () => {
|
duplicate: () => {
|
||||||
state.recipeDuplicateDialog = true;
|
recipeDuplicateDialog.value = true;
|
||||||
},
|
},
|
||||||
mealplanner: () => {
|
mealplanner: () => {
|
||||||
state.mealplannerDialog = true;
|
mealplannerDialog.value = true;
|
||||||
},
|
},
|
||||||
printPreferences: async () => {
|
printPreferences: async () => {
|
||||||
if (!recipeRef.value) {
|
if (!recipeRef.value) {
|
||||||
await refreshRecipe();
|
await refreshRecipe();
|
||||||
}
|
}
|
||||||
state.printPreferencesDialog = true;
|
printPreferencesDialog.value = true;
|
||||||
},
|
},
|
||||||
shoppingList: () => {
|
shoppingList: () => {
|
||||||
const promises: Promise<void>[] = [getShoppingLists()];
|
const promises: Promise<void>[] = [getShoppingLists()];
|
||||||
|
@ -502,11 +471,11 @@ export default defineNuxtComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.allSettled(promises).then(() => {
|
Promise.allSettled(promises).then(() => {
|
||||||
state.shoppingListDialog = true;
|
shoppingListDialog.value = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
share: () => {
|
share: () => {
|
||||||
state.shareDialog = true;
|
shareDialog.value = true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -515,34 +484,14 @@ export default defineNuxtComponent({
|
||||||
|
|
||||||
if (handler && typeof handler === "function") {
|
if (handler && typeof handler === "function") {
|
||||||
handler();
|
handler();
|
||||||
state.loading = false;
|
loading.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.emit(eventKey);
|
emit(eventKey);
|
||||||
state.loading = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const planTypeOptions = usePlanTypeOptions();
|
const planTypeOptions = usePlanTypeOptions();
|
||||||
|
const recipeActions = groupRecipeActionsStore.recipeActions;
|
||||||
return {
|
|
||||||
...toRefs(state),
|
|
||||||
newMealdateString,
|
|
||||||
recipeRef,
|
|
||||||
recipeRefWithScale,
|
|
||||||
executeRecipeAction,
|
|
||||||
recipeActions: groupRecipeActionsStore.recipeActions,
|
|
||||||
shoppingLists,
|
|
||||||
duplicateRecipe,
|
|
||||||
contextMenuEventHandler,
|
|
||||||
deleteRecipe,
|
|
||||||
addRecipeToPlan,
|
|
||||||
icon,
|
|
||||||
planTypeOptions,
|
|
||||||
firstDayOfWeek,
|
|
||||||
isAdminAndNotOwner,
|
|
||||||
canDelete,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue