Skip to content
Snippets Groups Projects
Select Git revision
  • main
1 result

Dockerfile

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    InputCombobox.vue 8.26 KiB
    <template>
      <MultiSelect
        :model-value="selectedOption"
        :options="selectableOptions"
        label="name"
        track-by="name"
        :placeholder="t('selectPlaceholder')"
        :required="required"
        :disabled="disabledMode || locked"
        select-label=""
        :loading="isLoadingSearchResults"
        selected-label=""
        deselect-label=""
        @update:model-value="updateMetadataValue"
        @search-change="triggerFetchOptions"
      >
        <template #afterList>
          <div
            v-if="selectableOptions.length && !allResultsLoaded"
            v-observe-visibility="reachedEndOfList"
          >
            <div class="d-flex justify-content-left my-3 ps-3">
              <b-spinner v-if="resultsLoading" small />
            </div>
          </div>
        </template>
      </MultiSelect>
    </template>
    
    <script lang="ts">
    import i18n from '@/plugins/i18n';
    
    import MultiSelect from '@/plugins/vue-multiselect';
    import { ObserveVisibility } from 'vue-observe-visibility';
    
    import type { Label } from '@/types/labels';
    import type { NamedNode, Quad_Object } from '@rdfjs/types';
    import factory from 'rdf-ext';
    import { retrieveChildren, retrieveInstance } from '@/util/linkedData';
    
    export default defineComponent({
      name: 'InputCombobox',
      components: {
        MultiSelect,
      },
      directives: {
        'observe-visibility': ObserveVisibility,
      },
      props: {
        locked: {
          default: false,
          type: Boolean,
        },
        required: {
          default: false,
          type: Boolean,
        },
        entry: {
          required: true,
          type: Object as PropType<Quad_Object>,
        },
        disabledMode: {
          default: false,
          type: Boolean,
        },
        languageCode: {
          default: 'en',
          type: String,
        },
        classObject: {
          required: true,
          type: Object as PropType<NamedNode<string>>,
        },
        classReceiver: {
          default: () => retrieveChildren,
          type: Function as PropType<typeof retrieveChildren>,
        },
        instanceReceiver: {
          default: () => retrieveInstance,
          type: Function as PropType<typeof retrieveInstance>,
        },
      },
      setup() {
        return { locale: i18n.global.locale, t: i18n.global.t };
      },
      data() {
        return {
          resultList: [] as Label[],
          searchResultList: [] as Label[],
          object: factory.namedNode('') as NamedNode<string>,
          pageSize: 10,
          pageNumber: 1,
          searchTerm: undefined as undefined | string,
          queryTimer: 0,
          allResultsLoaded: false,
          resultsLoading: false,
          hasNextDeInstances: true,
          hasNextEnInstances: true,
          isVisible: true,
          isLoadingSearchResults: false,
        };
      },
      computed: {
        selectableOptions(): Label[] {
          return [...this.resultList].sort((item1, item2) =>
            item1.name && item2.name ? item1.name.localeCompare(item2?.name) : 0,
          );
        },
        selectedOption(): Label | undefined {
          if (this.object.value !== '') {
            const foundOption = this.resultList.find(
              (option) => option.value === this.object.value,
            );
            return {
              name: foundOption?.name,
              value: this.object.value,
            };
          }
          return undefined;
        },
      },
      watch: {
        entry() {
          this.loadData();
        },
      },
      mounted() {
        if (this.languageCode === 'en' || this.languageCode === 'de') {
          this.locale = this.languageCode;
        } else {
          this.locale = 'en';
        }
        this.retrieveLabels();
        this.loadData();
      },
      methods: {
        updateMetadataValue(label: Label | null) {
          if (label?.value) {
            this.object.value = label.value;
          } else {
            this.object.value = '';
          }
          this.$emit('update:metadata-value', this.object);
        },
        async loadData() {
          if (this.object.value !== this.entry.value) {
            this.object.value = this.entry.value;
            const foundOption = this.resultList.find(
              (option) => option.value === this.object.value,
            );
            if (!foundOption) {
              let instanceReceiver:
                | typeof retrieveInstance
                | (() => typeof retrieveInstance) = this.instanceReceiver;
              // Deal with weird behavior during dev builds
              try {
                // If the function has no arguments, it's not the retrieveChildren one
                if (instanceReceiver.length === 0) {
                  instanceReceiver = (
                    instanceReceiver as unknown as () => typeof retrieveInstance
                  )();
                }
              } catch (e) {
                // Ignore
              }
    
              const instanceLabels = await instanceReceiver(
                this.object.value,
                this.classObject.value,
              );
              // List to be extended
              let newEntriesToAppend: Label;
              if (this.languageCode === 'en' && instanceLabels.en) {
                newEntriesToAppend = instanceLabels.en;
              } else if (this.languageCode === 'de' && instanceLabels.de) {
                newEntriesToAppend = instanceLabels.de;
              } else {
                newEntriesToAppend = {
                  name: this.object.value,
                  value: this.object.value,
                };
              }
              this.resultList.push(newEntriesToAppend);
            }
          }
        },
        async reachedEndOfList(isVisible: boolean) {
          if (!isVisible) {
            return;
          }
          this.isVisible = isVisible;
          if (this.hasNextDeInstances || this.hasNextEnInstances) {
            //fetch vocabularies
            await this.retrieveLabels();
          }
        },
        async retrieveLabels() {
          const selectedSearchOption = this.selectedOption;
          this.resultsLoading = true;
          let classReceiver:
            | typeof retrieveChildren
            | (() => typeof retrieveChildren) = this.classReceiver;
          // Deal with weird behavior during dev builds
          try {
            // If the function has no arguments, it's not the retrieveChildren one
            if (classReceiver.length === 0) {
              classReceiver = (
                classReceiver as unknown as () => typeof retrieveChildren
              )();
            }
          } catch (e) {
            // Ignore
          }
          try {
            const bilingualLabels = await classReceiver(
              this.classObject.value,
              this.pageNumber,
              this.pageSize,
              this.searchTerm,
            );
            // List to be extended
            let newEntriesToAppend: Label[] = [];
            if (this.languageCode === 'en' && bilingualLabels.en) {
              newEntriesToAppend = bilingualLabels.en;
            } else if (this.languageCode === 'de' && bilingualLabels.de) {
              newEntriesToAppend = bilingualLabels.de;
            } else if (bilingualLabels.en && bilingualLabels.en.length) {
              newEntriesToAppend = bilingualLabels.en;
            } else if (bilingualLabels.de && bilingualLabels.de.length) {
              newEntriesToAppend = bilingualLabels.de;
            }
            this.hasNextDeInstances =
              bilingualLabels.dePagination?.hasNext === true;
            this.hasNextEnInstances =
              bilingualLabels.enPagination?.hasNext === true;
            // Avoid duplicates
            this.resultList = this.resultList.concat(
              newEntriesToAppend.filter(
                (entry) =>
                  !this.resultList.some(
                    (otherEntry) => entry.value === otherEntry.value,
                  ),
              ),
            );
            if (
              selectedSearchOption &&
              !this.resultList.some(
                (entry) => entry.value === selectedSearchOption?.value,
              )
            ) {
              this.resultList.push(selectedSearchOption);
            }
            this.loadData();
            this.allResultsLoaded =
              !this.hasNextDeInstances || !this.hasNextEnInstances;
            // Increment the page number for next iteration
    
            this.pageNumber++;
          } catch (e) {
            // Method not available
            console.error(e);
          }
          this.resultsLoading = false;
        },
        triggerFetchOptions(searchTerm: string) {
          clearTimeout(this.queryTimer);
    
          this.isLoadingSearchResults = true; // Set the flag here, to avoid delayed loading indication
          // Set the search term
          this.searchTerm = searchTerm;
          // Reset the page number to show the first result
          this.pageNumber = 1;
    
          this.resultList = this.selectedOption ? [this.selectedOption] : [];
    
          this.queryTimer = window.setTimeout(() => this.retrieveLabels(), 500);
          this.isLoadingSearchResults = false;
        },
      },
    });
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped></style>