Shortcuts.vue 3.3 KB

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