SpeciesFileGroup/taxonworks

View on GitHub
app/javascript/vue/tasks/metadata/vocabulary/project_vocabulary/app.vue

Summary

Maintainability
Test Coverage
<template>
  <h1>Project vocabulary</h1>
  <VSpinner
    v-if="isLoading"
    full-screen
  />
  <div class="horizontal-left-content align-start gap-medium task-container">
    <div class="flex-col gap-medium full_height settings-panel">
      <div class="panel content">
        <PanelSettings
          v-model="parameters"
          class="margin-medium-bottom"
        />
        <VBtn
          color="primary"
          medium
          :disabled="!validate"
          @click="getWords"
        >
          Show records
        </VBtn>
      </div>
      <PanelLinks
        v-if="words.length"
        :model="parameters.model"
        :attribute="parameters.attribute"
      />
      <div
        v-if="words.length"
        class="overflow-y-auto"
      >
        <TableWords
          class="full_width"
          v-model="words"
          @select="openTask"
        />
      </div>
    </div>
    <div class="word-cloud-container panel padding-medium">
      <VSpinner
        v-if="isGeneratingCloud"
        legend="Generating word cloud..."
      />
      <VueWordCloud
        class="full_width full_height"
        :animation-enter="['bounceIn', 'bounceOut']"
        :words="words"
        :font-size-ratio="1 / 20"
        :spacing="1 / 4"
        @update:progress="updateLoadState"
      >
        <template #default="{ text, weight }">
          <div
            :title="weight"
            :class="TASK[parameters.model] && 'cursor-pointer link'"
            @click="() => openTask(text)"
          >
            {{ text }}
          </div>
        </template>
      </VueWordCloud>
    </div>
  </div>
</template>

<script setup>
import { URLParamsToJSON } from '@/helpers/url/parse'
import { TASK } from './constants/links'
import { RouteNames } from '@/routes/routes'
import { setParam } from '@/helpers'
import { Metadata } from '@/routes/endpoints'
import { computed, ref, onBeforeMount, watch } from 'vue'
import VSpinner from '@/components/ui/VSpinner.vue'
import VueWordCloud from 'vuewordcloud'
import PanelSettings from './components/PanelSettings.vue'
import VBtn from '@/components/ui/VBtn/index.vue'
import TableWords from './components/TableWords.vue'
import PanelLinks from './components/PanelLinks.vue'

defineOptions({
  name: 'ProjectVocabulary'
})

const words = ref([])
const isLoading = ref(false)
const isGeneratingCloud = ref(false)
const parameters = ref(initParameters())
const validate = computed(
  () => parameters.value.model && parameters.value.attribute
)

function getWords() {
  isLoading.value = true
  Metadata.vocabulary(parameters.value)
    .then(({ body }) => {
      setParam(RouteNames.ProjectVocabulary, parameters.value)
      words.value = Object.entries(body)
    })
    .catch(() => {})
    .finally(() => {
      isLoading.value = false
    })
}

function initParameters() {
  return {
    limit: 100
  }
}

onBeforeMount(() => {
  const urlParams = URLParamsToJSON(window.location.href)

  if (Object.keys(urlParams).length) {
    parameters.value = urlParams

    if (validate.value) {
      getWords()
    }
  }
})

function updateLoadState(e) {
  if (e) {
    const isProcessing = e.completedWords !== e.totalWords

    isGeneratingCloud.value = isProcessing
  } else {
    isGeneratingCloud.value = false
  }
}

function openTask(word) {
  const task = TASK[parameters.value.model]
  const { attribute } = parameters.value

  if (task) {
    const parameter = task.arrarProperties?.includes(attribute)
      ? `${attribute}[]`
      : attribute

    window.open(`${task.url}?${parameter}=${word}`, '_blank')
  }
}

watch(
  parameters,
  () => {
    words.value = []
  },
  { deep: true }
)
</script>

<style scoped>
.task-container {
  height: calc(100vh - 200px);
  max-height: calc(100vh - 200px);
}
.word-cloud-container {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
}

.settings-panel {
  width: 400px;
  max-width: 400px;
}

.link {
  color: var(--color-primary);
}
</style>