Shortcuts.vue 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. <template>
  2. <AppSlideOver :show="show" @close="close()">
  3. <template #content>
  4. <div
  5. class="
  6. bg-primary
  7. border-b border-dividerLight
  8. flex
  9. p-2
  10. top-0
  11. z-10
  12. items-center
  13. sticky
  14. justify-between
  15. "
  16. >
  17. <h3 class="ml-4 heading">{{ $t("app.shortcuts") }}</h3>
  18. <div class="flex">
  19. <ButtonSecondary svg="x" class="rounded" @click.native="close()" />
  20. </div>
  21. </div>
  22. <div class="bg-primary border-b border-dividerLight">
  23. <div class="flex flex-col my-4 mx-6">
  24. <input
  25. v-model="filterText"
  26. type="search"
  27. autocomplete="off"
  28. class="
  29. bg-primaryLight
  30. border border-dividerLight
  31. rounded
  32. flex
  33. w-full
  34. py-2
  35. px-4
  36. focus-visible:border-divider
  37. "
  38. :placeholder="`${$t('action.search')}`"
  39. />
  40. </div>
  41. </div>
  42. <div v-if="filterText">
  43. <div
  44. v-for="(map, mapIndex) in searchResults"
  45. :key="`map-${mapIndex}`"
  46. class="space-y-4 py-4 px-6"
  47. >
  48. <h1 class="font-semibold text-secondaryDark">
  49. {{ $t(map.item.section) }}
  50. </h1>
  51. <AppShortcutsEntry
  52. v-for="(shortcut, index) in map.item.shortcuts"
  53. :key="`shortcut-${index}`"
  54. :shortcut="shortcut"
  55. />
  56. </div>
  57. <div
  58. v-if="searchResults.length === 0"
  59. class="
  60. flex flex-col
  61. text-secondaryLight
  62. p-4
  63. items-center
  64. justify-center
  65. "
  66. >
  67. <i class="opacity-75 pb-2 material-icons">manage_search</i>
  68. <span class="text-center">
  69. {{ $t("state.nothing_found") }} "{{ filterText }}"
  70. </span>
  71. </div>
  72. </div>
  73. <div
  74. v-else
  75. class="
  76. divide-y divide-dividerLight
  77. flex flex-col flex-1
  78. overflow-auto
  79. hide-scrollbar
  80. "
  81. >
  82. <div
  83. v-for="(map, mapIndex) in mappings"
  84. :key="`map-${mapIndex}`"
  85. class="space-y-4 py-4 px-6"
  86. >
  87. <h1 class="font-semibold text-secondaryDark">
  88. {{ $t(map.section) }}
  89. </h1>
  90. <AppShortcutsEntry
  91. v-for="(shortcut, shortcutIndex) in map.shortcuts"
  92. :key="`map-${mapIndex}-shortcut-${shortcutIndex}`"
  93. :shortcut="shortcut"
  94. />
  95. </div>
  96. </div>
  97. </template>
  98. </AppSlideOver>
  99. </template>
  100. <script setup lang="ts">
  101. import { computed, ref } from "@nuxtjs/composition-api"
  102. import Fuse from "fuse.js"
  103. import mappings from "~/helpers/shortcuts"
  104. defineProps<{
  105. show: boolean
  106. }>()
  107. const options = {
  108. keys: ["shortcuts.label"],
  109. }
  110. const fuse = new Fuse(mappings, options)
  111. const filterText = ref("")
  112. const searchResults = computed(() => fuse.search(filterText.value))
  113. const emit = defineEmits<{
  114. (e: "close"): void
  115. }>()
  116. const close = () => {
  117. filterText.value = ""
  118. emit("close")
  119. }
  120. </script>
  121. <style lang="scss" scoped>
  122. .shortcut-key {
  123. @apply bg-dividerLight;
  124. @apply rounded;
  125. @apply ml-2;
  126. @apply py-1;
  127. @apply px-2;
  128. @apply inline-flex;
  129. }
  130. </style>