<template> <ADialog v-model="localIsOpen" is-modal :title="t('noteEditor.editNote')" class="tw-w-screen lg:tw-w-[60vw] tw-max-w-[800px]" > <template #default> <FormTable> <FormGroup :label="t('noteEditor.title')" :errors="errors.title"> <template #default="attrs"> <input v-model="noteData.title" class="form-control" :placeholder="t('noteEditor.titlePlaceholder')" required v-bind="attrs" /> <p v-if="noteData.slug" class="tw-text-xs tw-text-gray-400 tw-mt-1 tw-mb-0"> {{ t('slug') }}: {{ noteData.slug }} </p> </template> </FormGroup> <FormGroup :label="t('noteEditor.summary')" :errors="errors.summary"> <template #default="attrs"> <textarea ref="summaryEl" v-model="noteData.summary" class="tw-resize-none tw-overflow-hidden" :placeholder="t('noteEditor.summaryPlaceholder')" v-bind="attrs" /> </template> </FormGroup> <FormGroup :label="t('noteEditor.content')" :errors="errors.content"> <template #default="attrs"> <textarea ref="contentEl" v-model="noteData.content" class="tw-resize-none tw-overflow-hidden" :placeholder="t('noteEditor.contentPlaceholder')" required v-bind="attrs" /> </template> </FormGroup> <FormGroup :label="t('noteEditor.image')" :errors="errors.image" custom-control> <ImagePicker v-model="noteData.image" class="tw-flex-none tw-w-min" /> </FormGroup> </FormTable> </template> <template #footer> <div class="tw-flex tw-items-center tw-gap-3"> <button type="button" class="btn btn-default" @click.stop="emit('show', false)"> {{ t('cancel') }} </button> <button type="button" class="btn btn-primary" @click.stop="save"> {{ t('noteEditor.save') }} </button> </div> </template> </ADialog> </template> <script lang="ts" setup> import { computed, ref, toRefs, watchEffect } from 'vue' import { useTextareaAutosize } from '@vueuse/core' import { useAPIObject, useServerErrors } from '@/api' import { useI18n } from '@/i18n' import { useUpdatableState } from '@/util' import { slugify } from '@/mixins/slugify' import { newNote, NewNote, Note, useNoteStore } from '@/stores/notes' import ADialog from '@/components/generic/ADialog.vue' import FormGroup from '@/components/generic/FormGroup.vue' import FormTable from '@/components/generic/FormTable.vue' import ImagePicker from '@/components/images/ImagePicker.vue' const props = defineProps<{ modelValue: null | number timeslotId: number isOpen: boolean }>() const emit = defineEmits<{ (e: 'update:modelValue', value: number | null): void (e: 'show', value: boolean): void }>() const noteId = computed(() => props.modelValue) const { t } = useI18n() const noteStore = useNoteStore() const { obj: storedNote } = useAPIObject(noteStore, noteId) const noteData = ref<Note | NewNote>(newNote(props.timeslotId)) const error = ref<Error>() const errors = useServerErrors(error) watchEffect(() => { if (storedNote.value) { noteData.value = { ...storedNote.value } } }) watchEffect(() => { noteData.value.slug = slugify(noteData.value.title) }) const localIsOpen = useUpdatableState( computed(() => props.isOpen), (isOpen) => emit('show', isOpen), ) const { textarea: summaryEl } = useTextareaAutosize({ watch: () => noteData.value.summary }) const { textarea: contentEl } = useTextareaAutosize({ watch: () => noteData.value.content }) async function save() { const data = noteData.value error.value = undefined let note: Note try { if ('id' in data) { note = await noteStore.update(data.id, data) } else { note = await noteStore.create(data) } } catch (e) { if (e instanceof Error) { error.value = e } return } emit('update:modelValue', note.id) emit('show', false) } </script> <script lang="ts"> export default { compatConfig: { MODE: 3, }, } </script>