search-results.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. <template lang="pug">
  2. .search-results(v-if='search.length > 1')
  3. .search-results-container
  4. .search-results-loader(v-if='searchIsLoading && results.length < 1')
  5. orbit-spinner(
  6. :animation-duration='1000'
  7. :size='100'
  8. color='#FFF'
  9. )
  10. .headline.mt-5 Searching...
  11. .search-results-none(v-if='!searchIsLoading && results.length < 1')
  12. img(src='/svg/icon-no-results.svg', alt='No Results')
  13. .subheading No pages matching your query.
  14. template(v-if='results.length > 0')
  15. v-subheader.white--text Found {{response.totalHits}} results
  16. v-list.radius-7(two-line)
  17. template(v-for='(item, idx) of results')
  18. v-list-tile(@click='', :key='item.id')
  19. v-list-tile-avatar(tile)
  20. img(src='/svg/icon-selective-highlighting.svg')
  21. v-list-tile-content
  22. v-list-tile-title(v-html='item.title')
  23. v-list-tile-sub-title(v-html='item.description')
  24. .caption.grey--text.mt-1(v-html='item.path')
  25. v-list-tile-action
  26. v-chip(label) {{item.locale.toUpperCase()}}
  27. v-divider(v-if='idx < results.length - 1')
  28. v-pagination.mt-3(
  29. v-if='paginationLength > 1'
  30. dark
  31. v-model='pagination'
  32. :length='paginationLength'
  33. )
  34. template(v-if='suggestions.length > 0')
  35. v-subheader.white--text.mt-3 Did you mean...
  36. v-list.radius-7(dense, dark)
  37. template(v-for='(term, idx) of suggestions')
  38. v-list-tile(:key='term', @click='setSearchTerm(term)')
  39. v-list-tile-avatar
  40. v-icon search
  41. v-list-tile-content
  42. v-list-tile-title(v-html='term')
  43. v-divider(v-if='idx < suggestions.length - 1')
  44. .text-xs-center.pt-4
  45. v-btn(outline, color='orange', @click='search = ``', v-if='results.length > 0')
  46. v-icon(left) save
  47. span Copy Search Link
  48. v-btn(outline, color='pink', @click='search = ``')
  49. v-icon(left) clear
  50. span Close
  51. </template>
  52. <script>
  53. import _ from 'lodash'
  54. import { get, sync } from 'vuex-pathify'
  55. import { OrbitSpinner } from 'epic-spinners'
  56. import searchPagesQuery from 'gql/common/common-pages-query-search.gql'
  57. export default {
  58. components: {
  59. OrbitSpinner
  60. },
  61. data() {
  62. return {
  63. pagination: 1,
  64. response: {
  65. results: [],
  66. suggestions: [],
  67. totalHits: 0
  68. }
  69. }
  70. },
  71. computed: {
  72. search: sync('site/search'),
  73. searchIsLoading: sync('site/searchIsLoading'),
  74. searchRestrictLocale: sync('site/searchRestrictLocale'),
  75. searchRestrictPath: sync('site/searchRestrictPath'),
  76. results() {
  77. return this.response.results ? this.response.results : []
  78. },
  79. hits() {
  80. return this.response.totalHits ? this.response.totalHits : 0
  81. },
  82. suggestions() {
  83. return this.response.suggestions ? this.response.suggestions : []
  84. },
  85. paginationLength() {
  86. return this.response.totalHits > 0 ? 0 : Math.ceil(this.response.totalHits / 10)
  87. }
  88. },
  89. methods: {
  90. setSearchTerm(term) {
  91. this.search = term
  92. }
  93. },
  94. apollo: {
  95. response: {
  96. query: searchPagesQuery,
  97. variables() {
  98. return {
  99. query: this.search
  100. }
  101. },
  102. fetchPolicy: 'cache-and-network',
  103. throttle: 1000,
  104. skip() {
  105. return !this.search || this.search.length < 2
  106. },
  107. update: (data) => _.get(data, 'pages.search', {}),
  108. watchLoading (isLoading) {
  109. this.searchIsLoading = isLoading
  110. }
  111. }
  112. }
  113. }
  114. </script>
  115. <style lang="scss">
  116. .search-results {
  117. position: fixed;
  118. top: 64px;
  119. left: 0;
  120. width: 100%;
  121. height: calc(100% - 64px);
  122. background-color: rgba(0,0,0,.9);
  123. z-index: 100;
  124. text-align: center;
  125. animation: searchResultsReveal .6s ease;
  126. @media #{map-get($display-breakpoints, 'sm-and-down')} {
  127. top: 112px;
  128. }
  129. &-container {
  130. margin: 12px auto;
  131. width: 90vw;
  132. max-width: 1024px;
  133. }
  134. &-loader {
  135. display: flex;
  136. justify-content: center;
  137. align-items: center;
  138. flex-direction: column;
  139. padding: 32px 0;
  140. color: #FFF;
  141. }
  142. &-none {
  143. color: #FFF;
  144. img {
  145. width: 200px;
  146. }
  147. }
  148. }
  149. @keyframes searchResultsReveal {
  150. 0% {
  151. background-color: rgba(0,0,0,0);
  152. padding-top: 32px;
  153. }
  154. 100% {
  155. background-color: rgba(0,0,0,.9);
  156. padding-top: 0;
  157. }
  158. }
  159. </style>