| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- <template>
- <div class="formcontrol">
- <label class="formcontrol-label" for="" v-if="optional || label"
- >{{ label }}
- <div class="formcontrol-optional" v-if="optional">Optional</div></label
- >
- <div
- class="select-multiple"
- ref="container"
- :class="{ 'select-multiple--expanded': showAutocomplete }"
- @click="openDropDown"
- >
- <div class="select-multiple-value">
- <input
- ref="input"
- class="select-multiple-input"
- :placeholder="placeholder"
- :size="Math.max(value.length, placeholder.length, 10)"
- v-model="value"
- @keyup="onKeyup"
- />
- </div>
- <div class="dropdown-options" tabindex="0">
- <div
- v-for="(v, i) in displayedAutocomplete"
- :class="{ 'select--active': i == activeIndex }"
- @click="toggle(v, $event)"
- :key="v.id"
- >
- <span v-html="highlight(v.name)"></span>
- </div>
- <div v-if="filtredAutocomplete.length > displayedAutocomplete.length">...</div>
- </div>
- </div>
- </div>
- </template>
- <script lang="ts">
- import AutocompleteOptions from "@/models/AutocompleteOptions";
- import { defineComponent, PropType } from "vue";
- import toast from "@/utils/Toast";
- export default defineComponent({
- props: {
- id: String,
- label: String,
- modelValue: {
- type: String,
- },
- autocompleteList: {
- type: Array as PropType<Array<AutocompleteOptions>>,
- default: () => [],
- },
- placeholder: { type: String, default: () => "" },
- optional: Boolean,
- limit: {
- type: Number,
- default: () => Infinity,
- },
- strictAutocomplete: {
- type: Boolean,
- default: () => false,
- },
- },
- data: function () {
- return {
- value: "",
- activeIndex: -1,
- expanded: false,
- };
- },
- watch: {
- modelValue(v) {
- this.value = this.autocompleteList.find((o) => o.id == v)?.name || "";
- },
- value(value) {
- if (!this.strictAutocomplete) this.$emit("update:modelValue", value);
- },
- },
- computed: {
- filtredAutocomplete(): Array<AutocompleteOptions> {
- const lower = this.value.toLowerCase();
- return this.autocompleteList.filter((o) => o.name.toLowerCase().includes(lower));
- },
- displayedAutocomplete(): Array<AutocompleteOptions> {
- return this.filtredAutocomplete.slice(0, this.limit);
- },
- showAutocomplete(): boolean {
- return this.expanded && this.filtredAutocomplete.length > 0;
- },
- input(): HTMLInputElement {
- return this.$refs["input"] as HTMLInputElement;
- },
- },
- methods: {
- openDropDown: function (e: MouseEvent) {
- this.expanded = true;
- this.input.focus();
- window.addEventListener("click", this.closeDropDown);
- e.stopPropagation();
- },
- closeDropDown: function (e: MouseEvent) {
- if (this.$refs["container"]) {
- if (!(this.$refs["container"] as HTMLElement).contains(e.target as Node)) {
- this.expanded = false;
- window.removeEventListener("click", this.closeDropDown);
- e.stopPropagation();
- if (this.value == "") this.$emit("update:modelValue", this.value);
- }
- }
- },
- toggle(v: AutocompleteOptions, e: MouseEvent) {
- e.stopPropagation();
- this.expanded = false;
- this.value = v.name;
- this.$emit("update:modelValue", v.id);
- },
- onKeyup: function (e: KeyboardEvent): void {
- if (e.key === "Enter") {
- if (this.value === "") {
- this.$emit("update:modelValue", "");
- this.expanded = false;
- } else if (this.activeIndex > -1 && this.activeIndex < this.displayedAutocomplete.length) {
- const v = this.displayedAutocomplete[this.activeIndex];
- this.value = v.name;
- this.$emit("update:modelValue", v.id);
- this.expanded = false;
- } else if (this.strictAutocomplete) {
- toast({
- html: "Vous ne pouvez ajouter que des valeurs prédéfinies",
- classes: "red",
- });
- }
- }
- if (e.key === "ArrowUp") {
- this.activeIndex--;
- }
- if (e.key === "ArrowDown") {
- this.activeIndex++;
- }
- if (this.activeIndex < -1) {
- this.activeIndex = this.displayedAutocomplete.length - 1;
- }
- if (this.activeIndex >= this.displayedAutocomplete.length) {
- this.activeIndex = 0;
- }
- },
- highlight: function (txt: string) {
- if (this.value) {
- const reg = new RegExp(this.value, "ig");
- const highlightedText = txt.match(reg);
- if (highlightedText) {
- const plaintext = txt.split(reg);
- return (
- highlightedText.map((t, i) => plaintext[i] + `<b class="highlight">${t}</b>`).join("") +
- plaintext.pop()
- );
- }
- }
- return txt;
- },
- },
- });
- </script>
- <style src="../assets/css/multiple-select.css"></style>
|