RecipeTimelineContextMenu script setup and no emit mutation

This commit is contained in:
Kuchenpirat 2025-06-19 10:26:59 +00:00
commit 4a2d248c43

View file

@ -6,12 +6,12 @@
:icon="$globals.icons.edit" :icon="$globals.icons.edit"
can-submit can-submit
:submit-text="$t('general.save')" :submit-text="$t('general.save')"
@submit="$emit('update')" @submit="submitEdit"
> >
<v-card-text> <v-card-text>
<v-form ref="domMadeThisForm"> <v-form ref="domEditEventForm">
<v-text-field v-model="event.subject" :label="$t('general.subject')" /> <v-text-field v-model="localEvent.subject" :label="$t('general.subject')" />
<v-textarea v-model="event.eventMessage" :label="$t('general.message')" rows="4" /> <v-textarea v-model="localEvent.eventMessage" :label="$t('general.message')" rows="4" />
</v-form> </v-form>
</v-card-text> </v-card-text>
</BaseDialog> </BaseDialog>
@ -25,30 +25,30 @@
@confirm="$emit('delete')" @confirm="$emit('delete')"
> >
<v-card-text> <v-card-text>
{{ $t("events.event-delete-confirmation") }} {{ $t('events.event-delete-confirmation') }}
</v-card-text> </v-card-text>
</BaseDialog> </BaseDialog>
<v-menu <v-menu
offset-y offset-y
start start
:bottom="!menuTop" :bottom="!props.menuTop"
:nudge-bottom="!menuTop ? '5' : '0'" :nudge-bottom="!props.menuTop ? '5' : '0'"
:top="menuTop" :top="props.menuTop"
:nudge-top="menuTop ? '5' : '0'" :nudge-top="props.menuTop ? '5' : '0'"
allow-overflow allow-overflow
close-delay="125" close-delay="125"
:open-on-hover="!useMobileFormat" :open-on-hover="!props.useMobileFormat"
content-class="d-print-none" content-class="d-print-none"
> >
<template #activator="{ props }"> <template #activator="{ props: btnProps }">
<v-btn <v-btn
:class="{ 'rounded-circle': fab }" :class="{ 'rounded-circle': props.fab }"
:x-small="fab" :x-small="props.fab"
:elevation="elevation" :elevation="props.elevation ?? undefined"
:color="color" :color="props.color"
:icon="!fab" :icon="!props.fab"
v-bind="props" v-bind="btnProps"
@click.prevent @click.prevent
> >
<v-icon>{{ icon }}</v-icon> <v-icon>{{ icon }}</v-icon>
@ -72,7 +72,8 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { useI18n, useNuxtApp } from "#imports";
import type { RecipeTimelineEventOut } from "~/lib/api/types/recipe"; import type { RecipeTimelineEventOut } from "~/lib/api/types/recipe";
export interface TimelineContextMenuIncludes { export interface TimelineContextMenuIncludes {
@ -87,132 +88,90 @@ export interface ContextMenuItem {
event: string; event: string;
} }
export default defineNuxtComponent({ const props = defineProps<{
props: { useItems?: TimelineContextMenuIncludes;
useItems: { appendItems?: ContextMenuItem[];
type: Object as () => TimelineContextMenuIncludes, leadingItems?: ContextMenuItem[];
default: () => ({ menuTop?: boolean;
edit: true, fab?: boolean;
delete: true, elevation?: number | null;
}), color?: string;
}, event: RecipeTimelineEventOut;
// Append items are added at the end of the useItems list menuIcon?: string | null;
appendItems: { useMobileFormat?: boolean;
type: Array as () => ContextMenuItem[], }>();
default: () => [],
}, const emit = defineEmits(["delete", "update"]);
// Append items are added at the beginning of the useItems list
leadingItems: { const domEditEventForm = ref();
type: Array as () => ContextMenuItem[], const recipeEventEditDialog = ref(false);
default: () => [], const recipeEventDeleteDialog = ref(false);
}, const loading = ref(false);
menuTop: {
type: Boolean, const i18n = useI18n();
default: true, const { $globals } = useNuxtApp();
},
fab: { const defaultItems: { [key: string]: ContextMenuItem } = {
type: Boolean, edit: {
default: false, title: i18n.t("general.edit"),
}, icon: $globals.icons.edit,
elevation: { color: undefined,
type: Number, event: "edit",
default: null,
},
color: {
type: String,
default: "primary",
},
event: {
type: Object as () => RecipeTimelineEventOut,
required: true,
},
menuIcon: {
type: String,
default: null,
},
useMobileFormat: {
type: Boolean,
default: true,
},
}, },
emits: ["delete", "update"], delete: {
setup(props, context) { title: i18n.t("general.delete"),
const domEditEventForm = ref<VForm>(); icon: $globals.icons.delete,
const state = reactive({ color: "error",
recipeEventEditDialog: false, event: "delete",
recipeEventDeleteDialog: false,
loading: false,
menuItems: [] as ContextMenuItem[],
});
const i18n = useI18n();
const { $globals } = useNuxtApp();
// ===========================================================================
// Context Menu Setup
const defaultItems: { [key: string]: ContextMenuItem } = {
edit: {
title: i18n.t("general.edit"),
icon: $globals.icons.edit,
color: undefined,
event: "edit",
},
delete: {
title: i18n.t("general.delete"),
icon: $globals.icons.delete,
color: "error",
event: "delete",
},
};
// Get Default Menu Items Specified in Props
for (const [key, value] of Object.entries(props.useItems)) {
if (value) {
const item = defaultItems[key];
if (item) {
state.menuItems.push(item);
}
}
}
// Add Leading and Appending Items
state.menuItems = [...state.menuItems, ...props.leadingItems, ...props.appendItems];
const icon = props.menuIcon || $globals.icons.dotsVertical;
// ===========================================================================
// Context Menu Event Handler
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
const eventHandlers: { [key: string]: () => void | Promise<any> } = {
edit: () => {
state.recipeEventEditDialog = true;
},
delete: () => {
state.recipeEventDeleteDialog = true;
},
};
function contextMenuEventHandler(eventKey: string) {
const handler = eventHandlers[eventKey];
if (handler && typeof handler === "function") {
handler();
state.loading = false;
return;
}
context.emit(eventKey);
state.loading = false;
}
return {
...toRefs(state),
contextMenuEventHandler,
domEditEventForm,
icon,
};
}, },
};
const menuItems = computed(() => {
const items: ContextMenuItem[] = [];
const useItems = props.useItems ?? { edit: true, delete: true };
for (const [key, value] of Object.entries(useItems)) {
if (value) {
const item = defaultItems[key];
if (item) items.push(item);
}
}
return [
...items,
...(props.leadingItems ?? []),
...(props.appendItems ?? []),
];
}); });
const icon = computed(() => props.menuIcon || $globals.icons.dotsVertical);
const localEvent = ref({ ...props.event });
watch(() => props.event, (val) => {
localEvent.value = { ...val };
});
function openEditDialog() {
localEvent.value = { ...props.event };
recipeEventEditDialog.value = true;
}
function openDeleteDialog() {
recipeEventDeleteDialog.value = true;
}
function contextMenuEventHandler(eventKey: string) {
if (eventKey === "edit") {
openEditDialog();
loading.value = false;
return;
}
if (eventKey === "delete") {
openDeleteDialog();
loading.value = false;
return;
}
emit(eventKey as "delete" | "update");
loading.value = false;
}
function submitEdit() {
emit("update", { ...localEvent.value });
recipeEventEditDialog.value = false;
}
</script> </script>