AutoSuggest.vue 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <template>
  2. <div>
  3. <input
  4. v-model="value"
  5. class="input"
  6. type="text"
  7. :placeholder="placeholder"
  8. :disabled="disabled"
  9. @blur="blurInput($event)"
  10. @focus="focusInput()"
  11. @keydown.enter="$emit('submitted')"
  12. @keydown="keydownInput()"
  13. />
  14. <div
  15. class="autosuggest-container"
  16. v-if="
  17. (inputFocussed || containerFocussed || itemFocussed) &&
  18. items.length > 0
  19. "
  20. @mouseover="focusAutosuggestContainer()"
  21. @mouseleave="blurAutosuggestContainer()"
  22. >
  23. <span
  24. class="autosuggest-item"
  25. tabindex="0"
  26. @click="selectAutosuggestItem(item)"
  27. @keyup.enter="selectAutosuggestItem(item)"
  28. @focus="focusAutosuggestItem()"
  29. @blur="blurAutosuggestItem($event)"
  30. v-for="item in items"
  31. :key="item"
  32. >
  33. {{ item }}
  34. </span>
  35. </div>
  36. </div>
  37. </template>
  38. <script>
  39. export default {
  40. props: {
  41. modelValue: {
  42. type: String,
  43. default: ""
  44. },
  45. placeholder: {
  46. type: String,
  47. default: "Search value"
  48. },
  49. disabled: {
  50. type: Boolean,
  51. default: false
  52. },
  53. allItems: {
  54. type: Array,
  55. default: () => []
  56. }
  57. },
  58. emits: ["update:modelValue"],
  59. data() {
  60. return {
  61. inputFocussed: false,
  62. containerFocussed: false,
  63. itemFocussed: false,
  64. keydownInputTimeout: null,
  65. items: []
  66. };
  67. },
  68. computed: {
  69. value: {
  70. get() {
  71. return this.modelValue;
  72. },
  73. set(value) {
  74. this.$emit("update:modelValue", value);
  75. }
  76. }
  77. },
  78. methods: {
  79. blurInput(event) {
  80. if (
  81. event.relatedTarget &&
  82. event.relatedTarget.classList.contains("autosuggest-item")
  83. )
  84. this.itemFocussed = true;
  85. this.inputFocussed = false;
  86. },
  87. focusInput() {
  88. this.inputFocussed = true;
  89. },
  90. keydownInput() {
  91. clearTimeout(this.keydownInputTimeout);
  92. this.keydownInputTimeout = setTimeout(() => {
  93. if (this.value && this.value.length > 1) {
  94. this.items = this.allItems.filter(item =>
  95. item.toLowerCase().startsWith(this.value.toLowerCase())
  96. );
  97. } else this.items = [];
  98. }, 1000);
  99. },
  100. focusAutosuggestContainer() {
  101. this.containerFocussed = true;
  102. },
  103. blurAutosuggestContainer() {
  104. this.containerFocussed = false;
  105. },
  106. selectAutosuggestItem(item) {
  107. this.value = item;
  108. this.items = [];
  109. },
  110. focusAutosuggestItem() {
  111. this.itemFocussed = true;
  112. },
  113. blurAutosuggestItem(event) {
  114. if (
  115. !event.relatedTarget ||
  116. !event.relatedTarget.classList.contains("autosuggest-item")
  117. )
  118. this.itemFocussed = false;
  119. }
  120. }
  121. };
  122. </script>
  123. <style lang="less" scoped>
  124. .night-mode .autosuggest-container {
  125. background-color: var(--dark-grey) !important;
  126. .autosuggest-item {
  127. background-color: var(--dark-grey) !important;
  128. color: var(--white) !important;
  129. border-color: var(--dark-grey) !important;
  130. }
  131. .autosuggest-item:hover,
  132. .autosuggest-item:focus {
  133. background-color: var(--dark-grey-2) !important;
  134. }
  135. }
  136. .autosuggest-container {
  137. position: absolute;
  138. background: var(--white);
  139. width: calc(100% + 1px);
  140. top: 35px;
  141. z-index: 200;
  142. overflow: auto;
  143. max-height: 98px;
  144. clear: both;
  145. .autosuggest-item {
  146. padding: 8px;
  147. display: block;
  148. border: 1px solid var(--light-grey-2);
  149. margin-top: -1px;
  150. line-height: 16px;
  151. cursor: pointer;
  152. -webkit-user-select: none;
  153. -ms-user-select: none;
  154. -moz-user-select: none;
  155. user-select: none;
  156. }
  157. .autosuggest-item:hover,
  158. .autosuggest-item:focus {
  159. background-color: var(--light-grey);
  160. }
  161. .autosuggest-item:first-child {
  162. border-top: none;
  163. }
  164. .autosuggest-item:last-child {
  165. border-radius: 0 0 @border-radius @border-radius;
  166. }
  167. }
  168. </style>