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