DropdownSearcher.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <template lang="pug">
  2. .searcher
  3. span.input-icon.fa.fa-search(
  4. ref='searchSpan'
  5. @click='onClickOptions'
  6. )
  7. input.search-input(
  8. autofocus
  9. v-model='search'
  10. :placeholder='placeholder'
  11. )
  12. .dropdown-options(
  13. ref='dropdownOptions'
  14. :class="{'input-show': showOptions }"
  15. )
  16. ul.input-options
  17. li.input-option(
  18. v-for='option in getOptions()'
  19. :key='option.id'
  20. @click='onSelectOption(option)'
  21. )
  22. h2 {{ option.name }}
  23. </template>
  24. <script>
  25. export default {
  26. props: {
  27. placeholder: {
  28. type: String,
  29. default: ''
  30. },
  31. autofocus: {
  32. type: Boolean,
  33. default: false
  34. },
  35. items: {
  36. type: Array,
  37. default: [],
  38. required: true
  39. },
  40. keys: {
  41. type: Array,
  42. default: [],
  43. required: true
  44. }
  45. },
  46. watch: {
  47. search(value, lastValue) {
  48. value = value.trim()
  49. if (!value && value.length != lastValue.length) {
  50. this.selectedOption = null
  51. }
  52. this.showOptions = !!value && !this.selectedOption
  53. this.performSearch(value)
  54. }
  55. },
  56. methods: {
  57. getOptions() {
  58. return this.results.length == 0 ? this.items : this.results
  59. },
  60. performSearch(value) {
  61. this.results = []
  62. if (this.selectedOption) {
  63. return
  64. }
  65. for (let item of this.items) {
  66. for (let field in item) {
  67. if (typeof item[field] !== 'string') {
  68. continue
  69. }
  70. if (this.keys.length !== 0 && this.keys.indexOf(field) === -1) {
  71. continue
  72. }
  73. if (item[field].toLowerCase().indexOf(value.toLowerCase()) !== -1) {
  74. this.results.push(item)
  75. break
  76. }
  77. }
  78. }
  79. },
  80. hideOptions() {
  81. this.showOptions = false
  82. },
  83. onClickOptions() {
  84. this.showOptions = !this.showOptions || !!this.search
  85. },
  86. onSelectOption(item) {
  87. this.selectedOption = item
  88. this.search = item.name
  89. this.results = []
  90. this.$emit('onSelect', item)
  91. },
  92. onClickOutside(e) {
  93. let searchSpan = this.$refs.searchSpan
  94. if (!searchSpan) {
  95. return
  96. }
  97. let target = e.target
  98. if (target === searchSpan) {
  99. return
  100. }
  101. let el = this.$refs.dropdownOptions
  102. if (el !== target && !el.contains(target)) {
  103. this.hideOptions()
  104. }
  105. }
  106. },
  107. created() {
  108. document.addEventListener('click', this.onClickOutside)
  109. },
  110. destroyed() {
  111. document.removeEventListener('click', this.onClickOutside)
  112. },
  113. data() {
  114. return {
  115. search: '',
  116. results: [],
  117. selectedOption: null,
  118. showOptions: false
  119. }
  120. }
  121. }
  122. </script>
  123. <style lang="sass">
  124. @import '../../assets/variables'
  125. .searcher
  126. width: 100%
  127. height: 35px
  128. position: relative
  129. .input-icon
  130. position: absolute
  131. top: 10px
  132. right: 10px
  133. font-size: 12pt
  134. color: $app-dark-color
  135. &:hover
  136. cursor: pointer
  137. .search-input
  138. width: 100%
  139. height: 100%
  140. border-radius: 0
  141. font-size: 12pt
  142. font-weight: normal
  143. padding-right: 35px
  144. .dropdown-options
  145. width: 100%
  146. height: 150px
  147. display: none
  148. position: absolute
  149. background: $app-light-color
  150. border: 1px solid $app-separator-color
  151. box-shadow: 0 3px 5px $app-separator-color
  152. z-index: 10
  153. top: 35px
  154. left: 0
  155. right: 0
  156. bottom: -325px
  157. padding: 0
  158. animation-duration: 500ms
  159. overflow-y: auto
  160. &.input-show
  161. display: block
  162. .input-options
  163. padding-left: 0
  164. .input-option
  165. height: 35px
  166. display: block
  167. text-align: left
  168. margin-top: 5px
  169. padding-top: 5px
  170. font-size: 10pt
  171. font-weight: normal
  172. border-bottom: 1px solid $app-border-color
  173. &:last-child
  174. margin-bottom: 10px
  175. &:hover
  176. border-bottom: 2px solid $app-main-color
  177. cursor: pointer
  178. h2
  179. font-size: 10pt
  180. font-weight: normal
  181. margin: 0
  182. </style>