Skip to content
Snippets Groups Projects
NoteDescriptionEditor.vue 4.93 KiB
Newer Older
<template>
  <FormTable class="tw-max-w-3xl">
    <FormGroup :label="t('noteEditor.title')" :errors="title.errors" :is-saving="title.isSaving">
      <template #default="attrs">
        <input
          v-model="title.value"
          :placeholder="t('noteEditor.titlePlaceholder')"
          required
          v-bind="attrs"
          @blur="title.save()"
        />
        <p v-if="note.slug" class="tw-text-xs tw-text-gray-400 tw-mt-1 tw-mb-0">
          {{ t('slug') }}: {{ note.slug }}
        </p>
      </template>
    </FormGroup>

    <FormGroup
      :label="t('noteEditor.summary')"
      :errors="summary.errors"
      :is-saving="summary.isSaving"
    >
      <template #default="attrs">
        <textarea
          ref="summaryEl"
          v-model="summary.value"
          class="tw-resize-none tw-overflow-hidden tw-min-h-[3.55rem]"
          :placeholder="t('noteEditor.summaryPlaceholder')"
          v-bind="attrs"
          @blur="summary.save()"
        />
      </template>
    </FormGroup>

    <FormGroup
      :label="t('noteEditor.content')"
      :errors="content.errors"
      :is-saving="content.isSaving"
    >
      <template #default="attrs">
        <textarea
          ref="contentEl"
          v-model="content.value"
          class="tw-resize-none tw-overflow-hidden tw-min-h-[8rem]"
          rows="6"
          :placeholder="t('noteEditor.contentPlaceholder')"
          required
          v-bind="attrs"
          @blur="content.save()"
        />
      </template>
    </FormGroup>

    <FormGroup
      :label="t('noteEditor.image')"
      :errors="imageId.errors"
      :is-saving="imageId.isSaving"
      custom-control
    >
      <ImagePicker v-model="imageId.value" class="tw-flex-none tw-w-min" />
    </FormGroup>

    <FormGroup
      :label="t('noteEditor.contributors')"
      :errors="contributors.errors"
      :is-saving="contributors.isSaving"
    >
      <ComboBoxSimple v-model="contributors.value" :choices="contributors.choices" />
    </FormGroup>

    <FormGroup :label="t('noteEditor.topics')" :errors="topics.errors" :is-saving="topics.isSaving">
      <ComboBoxSimple v-model="topics.value" :choices="topics.choices" />
    </FormGroup>

    <FormGroup
      :label="t('noteEditor.languages')"
      :errors="languages.errors"
      :is-saving="languages.isSaving"
    >
      <ComboBoxSimple v-model="languages.value" :choices="languages.choices" />
    </FormGroup>

    <FormGroup :label="t('noteEditor.tags')" :errors="tags.errors" :is-saving="tags.isSaving">
      <TagInput v-model="tags.value" />
    </FormGroup>

    <FormGroup
      :label="t('noteEditor.links')"
      :is-saving="links.isSaving"
      :errors="getFieldErrors(links.errors, 'links')"
      :has-error="links.errors.length > 0"
      custom-control
    >
      <ALinkCollectionEditor
        v-model="links.value"
        :error-lists="getTreeFieldChildrenErrorsList(links.errors, 'links')"
        allow-add
        @save="links.save()"
      />
    </FormGroup>
  </FormTable>
</template>

<script lang="ts" setup>
import { useTextareaAutosize } from '@vueuse/core'
import { computed } from 'vue'

import { useI18n } from '@/i18n'
import { Note, Show, TimeSlot } from '@/types'
import { useHostStore, useLanguageStore, useNoteStore, useTopicStore } from '@/stores'
import FormGroup from '@/components/generic/FormGroup.vue'
import ImagePicker from '@/components/images/ImagePicker.vue'
import TagInput from '@/components/generic/TagInput.vue'
import ComboBoxSimple from '@/components/ComboBoxSimple.vue'
import {
  getFieldErrors,
  getTreeFieldChildrenErrorsList,
  useAPIObjectFieldCopy,
  useRelationList,
} from '@/form'
import FormTable from '@/components/generic/FormTable.vue'
import ALinkCollectionEditor from '@/components/generic/ALinkCollectionEditor.vue'

defineOptions({ compatConfig: { MODE: 3 } })

const props = defineProps<{
  timeslot: TimeSlot
  show: Show
  note: Note
}>()
const { t } = useI18n()
const noteStore = useNoteStore()
const hostStore = useHostStore()
const languageStore = useLanguageStore()
const topicStore = useTopicStore()
const note = computed(() => props.note)

const title = useAPIObjectFieldCopy(noteStore, note, 'title', { debounce: 2 })
const summary = useAPIObjectFieldCopy(noteStore, note, 'summary', { noAutoSave: true })
const content = useAPIObjectFieldCopy(noteStore, note, 'content', { noAutoSave: true })
const tags = useAPIObjectFieldCopy(noteStore, note, 'tags')
const imageId = useAPIObjectFieldCopy(noteStore, note, 'imageId', { debounce: 0 })
const links = useAPIObjectFieldCopy(noteStore, note, 'links', { debounce: 2 })
const contributors = useRelationList(noteStore, note, 'contributorIds', hostStore)
const languages = useRelationList(noteStore, note, 'languageIds', languageStore)
const topics = useRelationList(noteStore, note, 'topicIds', topicStore)

const { textarea: summaryEl } = useTextareaAutosize({ watch: () => note.value.summary })
const { textarea: contentEl } = useTextareaAutosize({ watch: () => note.value.content })
</script>