SpeciesFileGroup/taxonworks

View on GitHub
app/javascript/vue/tasks/otu/browse/components/timeline/Timeline.vue

Summary

Maintainability
Test Coverage
<template>
  <section-panel
    :status="status"
    :title="title"
    :spinner="isLoading"
    menu
    @menu="showModal = true"
  >
    <div class="switch-radio separate-top separate-bottom">
      <template
        v-for="(item, index) in filterTabs"
        :key="index"
      >
        <input
          v-model="tabSelected"
          :id="`switch-filter-nomenclature-${index}`"
          name="switch-filter-nomenclature-options"
          type="radio"
          class="normal-input button-active"
          :value="item"
        />
        <label :for="`switch-filter-nomenclature-${index}`">{{
          item.label
        }}</label>
      </template>
    </div>
    <div
      v-if="nomenclature"
      :class="
        Object.assign(
          {},
          ...preferences.filterSections.show
            .concat(preferences.filterSections.topic)
            .map((item) => ({ [item.key]: !item.value }))
        )
      "
    >
      <timeline-citations :citations="filteredList" />
      <h3>References</h3>
      <ul
        v-if="nomenclature"
        class="no_bullets"
      >
        <template v-if="selectedReferences.length">
          <template
            v-for="item in references"
            :key="item"
          >
            <li
              v-show="filterSource(nomenclature.sources.list[item])"
              class="horizontal-left-content gap-small"
            >
              <label>
                <input
                  v-model="references"
                  :value="item"
                  type="checkbox"
                />
                <span v-html="nomenclature.sources.list[item].cached" />
              </label>
              <radial-annotator :global-id="item" />
              <radial-navigation :global-id="item" />
            </li>
          </template>
        </template>
        <template v-else>
          <template
            v-for="(item, key) in nomenclature.sources.list"
            :key="key"
          >
            <li
              v-show="filterSource(item)"
              class="horizontal-left-content gap-small"
            >
              <label>
                <input
                  v-model="references"
                  :value="key"
                  type="checkbox"
                />
                <span v-html="item.cached" />
              </label>
              <template v-if="showReferencesTopic">
                <span
                  v-for="(topic, index) in getSourceTopics(item).map(
                    (key) => nomenclature.topics.list[key]
                  )"
                  class="pill topic references_topics"
                  :key="index"
                  :style="{ 'background-color': topic.css_color }"
                  v-html="topic.name"
                />
              </template>
              <radial-annotator :global-id="key" />
              <radial-navigation :global-id="key" />
            </li>
          </template>
        </template>
      </ul>
    </div>
    <soft-validation-modal />
    <modal-component
      v-if="showModal"
      @close="showModal = false"
    >
      <template #header>
        <h3>Visualize</h3>
      </template>
      <template #body>
        <div class="flex-separate">
          <div class="full_width">
            <h4 class="capitalize separate-bottom">Time</h4>
            <ul class="no_bullets">
              <li
                class="separate-right"
                v-for="(item, key) in preferences.filterSections.and.time"
                :key="key"
              >
                <label>
                  <input
                    v-model="item.value"
                    type="checkbox"
                  />
                  {{ item.label }}
                </label>
              </li>
            </ul>
            <h4 class="capitalize separate-bottom">Year</h4>
            <year-picker
              v-model.number="preferences.filterSections.and.year[0].value"
              :years="nomenclature.sources.year_metadata"
            />
          </div>
          <div class="full_width">
            <h4 class="capitalize separate-bottom">Filter</h4>
            <ul class="no_bullets">
              <li
                class="separate-right"
                v-for="(item, key) in preferences.filterSections.and.current"
                :key="key"
              >
                <label>
                  <input
                    v-model="item.value"
                    type="checkbox"
                  />
                  {{ item.label }}
                </label>
              </li>
            </ul>
            <template v-for="section in preferences.filterSections.or">
              <ul class="no_bullets">
                <li
                  v-for="(item, key) in section"
                  :key="key"
                  class="separate-right"
                >
                  <label>
                    <input
                      v-model="item.value"
                      type="checkbox"
                    />
                    {{ item.label }}
                  </label>
                </li>
              </ul>
            </template>
          </div>
          <div class="full_width">
            <h4 class="capitalize separate-bottom">Show</h4>
            <ul class="no_bullets">
              <li
                class="separate-right"
                v-for="(item, key) in preferences.filterSections.show"
                :key="key"
              >
                <label>
                  <input
                    v-model="item.value"
                    type="checkbox"
                  />
                  {{ item.label }}
                </label>
              </li>
            </ul>
            <h4 class="capitalize separate-bottom">Topic</h4>
            <ul class="no_bullets">
              <li
                v-for="(item, key) in preferences.filterSections.topic"
                :key="key"
              >
                <label>
                  <input
                    v-model="item.value"
                    type="checkbox"
                  />
                  {{ item.label }}
                </label>
              </li>
              <li>
                <label>
                  <input
                    v-model="showReferencesTopic"
                    type="checkbox"
                  />
                  On references
                </label>
              </li>
            </ul>
          </div>
          <div>
            <div class="separate-bottom"></div>
            <ul class="no_bullets topic-section">
              <li
                v-for="(topic, key) in nomenclature.topics.list"
                :key="key"
              >
                <label>
                  <input
                    type="checkbox"
                    :value="key"
                    v-model="topicsSelected"
                  />
                  {{ topic.name }}
                </label>
              </li>
            </ul>
          </div>
        </div>
      </template>
    </modal-component>
  </section-panel>
</template>

<script>
import SectionPanel from '../shared/sectionPanel'
import ModalComponent from '@/components/ui/Modal'
import YearPicker from './TimelineYearsPick.vue'
import extendSection from '../shared/extendSections'
import SoftValidationModal from '../softValidationModal'
import TimelineCitations from './TimelineCitations.vue'
import RadialAnnotator from '@/components/radials/annotator/annotator.vue'
import RadialNavigation from '@/components/radials/navigation/radial.vue'
import { GetterNames } from '../../store/getters/getters'
import { MutationNames } from '../../store/mutations/mutations'
import { Otu } from '@/routes/endpoints'

export default {
  mixins: [extendSection],
  components: {
    SectionPanel,
    ModalComponent,
    YearPicker,
    SoftValidationModal,
    TimelineCitations,
    RadialAnnotator,
    RadialNavigation
  },
  computed: {
    selectedReferences() {
      return this.references.map((item) => this.nomenclature.sources.list[item])
    },
    itemsList() {
      return this.references.length
        ? this.nomenclature.items.filter((item) =>
            this.selectedReferences.find((ref) =>
              ref.objects.includes(item.data_attributes['history-object-id'])
            )
          )
        : this.nomenclature.items
    },
    preferences: {
      get() {
        return this.$store.getters[GetterNames.GetPreferences]
      },
      set(value) {
        this.$store.commit(MutationNames.SetPreferences, value)
      }
    },
    filteredList() {
      return Array.isArray(this.itemsList)
        ? this.itemsList.filter((item) => this.checkFilter(item))
        : []
    }
  },
  data() {
    return {
      isLoading: true,
      showReferencesTopic: false,
      references: [],
      topicsSelected: [],
      showModal: false,
      nomenclature: undefined,
      tabSelected: {
        label: 'All',
        key: '',
        value: ''
      },
      hideInfo: [],
      filterTabs: [
        {
          label: 'All',
          key: '',
          value: '',
          equal: true
        },
        {
          label: 'Nomenclature',
          key: 'history-origin',
          value: 'otu',
          equal: false
        },
        {
          label: 'Protonym',
          key: 'history-origin',
          value: 'protonym',
          equal: true
        },
        {
          label: 'OTU (biology)',
          key: 'history-origin',
          value: 'otu',
          equal: true
        }
      ]
    }
  },
  watch: {
    otu: {
      handler(newVal) {
        if (newVal) {
          this.isLoading = true
          Otu.timeline(this.otu.id).then((response) => {
            this.nomenclature = response.body
            this.isLoading = false
          })
        }
      },
      immediate: true
    }
  },
  methods: {
    checkFilter(item) {
      const keysAND = Object.keys(this.preferences.filterSections.and)
      const keysOR = Object.keys(this.preferences.filterSections.or)

      return (
        ((this.tabSelected.equal
          ? item.data_attributes[this.tabSelected.key] ===
            this.tabSelected.value
          : item.data_attributes[this.tabSelected.key] !==
            this.tabSelected.value) ||
          this.tabSelected.label === 'All') &&
        keysAND.every((key) => {
          if (
            this.preferences.filterSections.and[key].every(
              (filter) => filter.value === false
            )
          )
            return true

          return this.preferences.filterSections.and[key].every((filter) => {
            if (filter.value === undefined) return true
            return filter.equal
              ? item.data_attributes[filter.key] === filter.value
              : item.data_attributes[filter.key] !== filter.value
          })
        }) &&
        keysOR.every((key) =>
          this.preferences.filterSections.or[key].some((filter) =>
            filter.equal
              ? item.data_attributes[filter.key] === filter.value
              : item.data_attributes[filter.key] !== filter.value
          )
        ) &&
        (this.topicsSelected.length
          ? item.topics.some((topic) => this.topicsSelected.includes(topic))
          : true)
      )
    },

    filterSource(source) {
      const globalIds = source.objects

      return this.itemsList
        .filter((item) => this.checkFilter(item))
        .find((item) =>
          globalIds.includes(item.data_attributes['history-object-id'])
        )
    },

    getSourceTopics(source) {
      const globalIds = source.objects
      const topics = this.itemsList
        .filter((item) => this.checkFilter(item))
        .filter((item) =>
          globalIds.includes(item.data_attributes['history-object-id'])
        )
        .map((item) => item.topics)

      return [...new Set([].concat(...topics))]
    }
  }
}
</script>
<style lang="scss" scoped>
.hidden {
  display: none;
}
:deep(.modal-container) {
  width: 900px;
}
.topic-section {
  overflow-y: scroll;
  max-height: 480px;
}
.references_topics {
  color: black;
}
:deep(.annotation__note) {
  display: inline;
}
:deep(.hide-validations) {
  .soft_validation_anchor {
    display: none !important;
  }
}
:deep(.hide-notes) {
  .history__citation_notes {
    display: none !important;
  }
}
:deep(.hide-topics) {
  .history__citation_topics {
    display: none !important;
  }
}
</style>