Skip to content
Snippets Groups Projects
NoteEditorModal.vue 4.2 KiB
Newer Older
<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>