scinote-web/app/javascript/vue/shared/legacy/checklist_search.vue

142 lines
4.3 KiB
Vue

<template>
<checklistSelect
:class="{
['sn-select sn-select--search hover:border-sn-sleepy-grey']: true,
'sn-select--open': shouldOpen,
'sn-select--blank': !valueLabel,
'disabled': disabled
}"
:className="className"
:optionsClassName="optionsClassName"
:withButtons="withButtons"
:withEditCursor="withEditCursor"
:initialSelectedValues="initialSelectedValues"
:options="currentOptions"
:placeholder="placeholder"
:noOptionsPlaceholder="isLoading || isLazyLoading ? i18n.t('general.loading') : noOptionsPlaceholder"
:disabled="disabled"
:shouldOpen="shouldOpen"
:shouldUpdateWithoutValues="shouldUpdateWithoutValues"
:shouldUpdateOnToggleClose="shouldUpdateOnToggleClose"
@reached-end="handleReachedEnd"
@close="shouldOpen = false"
@update-selected-values="selectedValues = $event"
>
<div class="flex w-full" @click="shouldOpen = !shouldOpen">
<input
ref="focusElement"
v-model="query"
type="text"
class="sn-select__search-input"
:placeholder="placeholder"
@blur="query = ''" />
<span class="sn-select__value">{{ valueLabel || (placeholder || i18n.t('general.select')) }}</span>
<i class="sn-icon" :class="{ 'sn-icon-down': !shouldOpen, 'sn-icon-up': shouldOpen}"></i>
</div>
</checklistSelect>
</template>
<script>
import checklistSelect from './checklist_select.vue';
import { debounce } from '../debounce';
export default {
name: 'ChecklistSearch',
props: {
withButtons: { type: Boolean, default: false },
withEditCursor: { type: Boolean, default: false },
initialSelectedValues: { type: Array, default: () => [] },
options: { type: Array, default: () => [] },
placeholder: { type: String },
noOptionsPlaceholder: { type: String },
disabled: { type: Boolean, default: false },
className: { type: String, default: '' },
optionsClassName: { type: String, default: '' },
shouldUpdateWithoutValues: { type: Boolean, default: false },
shouldUpdateOnToggleClose: { type: Boolean, default: false },
isLoading: { type: Boolean, default: false },
params: { type: Array, default: () => [] },
optionsUrl: { type: String, required: true },
lazyLoadEnabled: { type: Boolean, default: false }
},
components: { checklistSelect },
emits: ['reached-end', 'update-options'],
data() {
return {
query: null,
currentOptions: null,
shouldOpen: false,
nextPage: 1,
isLazyLoading: false,
selectedValues: []
};
},
created() {
this.currentOptions = this.options;
},
watch: {
query() {
if (!this.query) {
this.currentOptions = this.options;
return;
}
if (this.optionsUrl) {
// reset current options and next page when lazy loading is enabled
if (this.lazyLoadEnabled) {
this.currentOptions = [];
this.isLazyLoading = true;
this.nextPage = 1;
}
this.$nextTick(() => {
debounce(this.fetchOptions(), 10);
});
} else {
this.currentOptions = this.options.filter((o) => o[1].toLowerCase().includes(this.query.toLowerCase()));
}
},
options() {
this.currentOptions = this.options;
}
},
computed: {
valueLabel() {
if (!this.selectedValues.length) return '';
if (this.selectedValues.length === 1) {
return this.currentOptions.find(({ id }) => id === this.selectedValues[0])?.label;
}
return `${this.selectedValues.length} ${this.i18n.t('general.options_selected')}`;
}
},
methods: {
handleReachedEnd() {
if (this.query) {
this.fetchOptions();
} else {
this.$emit('reached-end');
}
},
fetchOptions() {
if (!this.nextPage || !this.optionsUrl) return;
$.ajax({
url: this.optionsUrl,
data: {
...(this.query && { query: this.query }),
...(this.lazyLoadEnabled && this.nextPage && { page: this.nextPage }),
...this.params
},
success: (result) => {
if (this.lazyLoadEnabled) {
this.nextPage = result.next_page;
this.$emit('update-options', this.currentOptions, result);
this.isLazyLoading = false;
return;
}
this.currentOptions = result;
}
});
}
}
};
</script>