refactor: move shortcuts to use minisearch

This commit is contained in:
Andrew Bastin
2023-07-05 11:51:15 +05:30
parent 6b38363fab
commit 7c5722586c
4 changed files with 180 additions and 364 deletions

View File

@@ -131,7 +131,6 @@ declare module '@vue/runtime-core' {
IconLucideLayers: typeof import('~icons/lucide/layers')['default'] IconLucideLayers: typeof import('~icons/lucide/layers')['default']
IconLucideListEnd: typeof import('~icons/lucide/list-end')['default'] IconLucideListEnd: typeof import('~icons/lucide/list-end')['default']
IconLucideMinus: typeof import('~icons/lucide/minus')['default'] IconLucideMinus: typeof import('~icons/lucide/minus')['default']
IconLucideRefreshCcw: typeof import('~icons/lucide/refresh-ccw')['default']
IconLucideRefreshCw: typeof import('~icons/lucide/refresh-cw')['default'] IconLucideRefreshCw: typeof import('~icons/lucide/refresh-cw')['default']
IconLucideSearch: typeof import('~icons/lucide/search')['default'] IconLucideSearch: typeof import('~icons/lucide/search')['default']
IconLucideUsers: typeof import('~icons/lucide/users')['default'] IconLucideUsers: typeof import('~icons/lucide/users')['default']

View File

@@ -14,46 +14,18 @@
/> />
</div> </div>
</div> </div>
<div v-if="filterText" class="flex flex-col divide-y divide-dividerLight"> <div class="flex flex-col divide-y divide-dividerLight">
<details <HoppSmartPlaceholder v-if="isEmpty(shortcutsResults)">
v-for="(map, mapIndex) in searchResults"
:key="`map-${mapIndex}`"
class="flex flex-col"
open
>
<summary
class="flex items-center flex-1 min-w-0 px-6 py-4 font-semibold transition cursor-pointer focus:outline-none text-secondaryLight hover:text-secondaryDark"
>
<icon-lucide-chevron-right class="mr-2 indicator" />
<span
class="font-semibold truncate capitalize-first text-secondaryDark"
>
{{ t(map.item.section) }}
</span>
</summary>
<div class="flex flex-col px-6 pb-4 space-y-2">
<AppShortcutsEntry
v-for="(shortcut, index) in map.item.shortcuts"
:key="`shortcut-${index}`"
:shortcut="shortcut"
/>
</div>
</details>
<HoppSmartPlaceholder
v-if="searchResults.length === 0"
:text="`${t('state.nothing_found')} ‟${filterText}”`"
>
<icon-lucide-search class="pb-2 opacity-75 svg-icons" /> <icon-lucide-search class="pb-2 opacity-75 svg-icons" />
<span class="my-2 text-center flex flex-col"> <span class="my-2 text-center flex flex-col">
{{ t("state.nothing_found") }} {{ t("state.nothing_found") }}
<span class="break-all">"{{ filterText }}"</span> <span class="break-all">"{{ filterText }}"</span>
</span> </span>
</HoppSmartPlaceholder> </HoppSmartPlaceholder>
</div>
<div v-else class="flex flex-col divide-y divide-dividerLight">
<details <details
v-for="(map, mapIndex) in mappings" v-for="(sectionResults, sectionTitle) in shortcutsResults"
:key="`map-${mapIndex}`" v-else
:key="`section-${sectionTitle}`"
class="flex flex-col" class="flex flex-col"
open open
> >
@@ -64,13 +36,13 @@
<span <span
class="font-semibold truncate capitalize-first text-secondaryDark" class="font-semibold truncate capitalize-first text-secondaryDark"
> >
{{ t(map.section) }} {{ sectionTitle }}
</span> </span>
</summary> </summary>
<div class="flex flex-col px-6 pb-4 space-y-2"> <div class="flex flex-col px-6 pb-4 space-y-2">
<AppShortcutsEntry <AppShortcutsEntry
v-for="(shortcut, shortcutIndex) in map.shortcuts" v-for="(shortcut, index) in sectionResults"
:key="`map-${mapIndex}-shortcut-${shortcutIndex}`" :key="`shortcut-${index}`"
:shortcut="shortcut" :shortcut="shortcut"
/> />
</div> </div>
@@ -81,10 +53,11 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from "vue" import { computed, onBeforeMount, ref } from "vue"
import Fuse from "fuse.js" import { ShortcutDef, getShortcuts } from "~/helpers/shortcuts"
import mappings from "~/helpers/shortcuts" import MiniSearch from "minisearch"
import { useI18n } from "@composables/i18n" import { useI18n } from "@composables/i18n"
import { groupBy, isEmpty } from "lodash-es"
const t = useI18n() const t = useI18n()
@@ -92,15 +65,33 @@ defineProps<{
show: boolean show: boolean
}>() }>()
const options = { const minisearch = new MiniSearch({
keys: ["shortcuts.label"], fields: ["label", "keys", "section"],
} idField: "label",
storeFields: ["label", "keys", "section"],
searchOptions: {
fuzzy: true,
prefix: true,
},
})
const fuse = new Fuse(mappings, options) const shortcuts = getShortcuts(t)
onBeforeMount(() => {
minisearch.addAllAsync(shortcuts)
})
const filterText = ref("") const filterText = ref("")
const searchResults = computed(() => fuse.search(filterText.value)) const shortcutsResults = computed(() => {
// If there are no search text, return all the shortcuts
const results =
filterText.value.length > 0
? minisearch.search(filterText.value)
: shortcuts
return groupBy(results, "section") as Record<string, ShortcutDef[]>
})
const emit = defineEmits<{ const emit = defineEmits<{
(e: "close"): void (e: "close"): void

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="flex items-center py-1"> <div class="flex items-center py-1">
<span class="flex flex-1 mr-4"> <span class="flex flex-1 mr-4">
{{ t(shortcut.label) }} {{ shortcut.label }}
</span> </span>
<kbd <kbd
v-for="(key, index) in shortcut.keys" v-for="(key, index) in shortcut.keys"
@@ -14,14 +14,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useI18n } from "@composables/i18n" import { ShortcutDef } from "~/helpers/shortcuts"
const t = useI18n()
defineProps<{ defineProps<{
shortcut: { shortcut: ShortcutDef
label: string
keys: string[]
}
}>() }>()
</script> </script>

View File

@@ -1,315 +1,146 @@
import IconLifeBuoy from "~icons/lucide/life-buoy"
import IconZap from "~icons/lucide/zap"
import IconArrowRight from "~icons/lucide/arrow-right"
import IconGift from "~icons/lucide/gift"
import IconMonitor from "~icons/lucide/monitor"
import IconSun from "~icons/lucide/sun"
import IconCloud from "~icons/lucide/cloud"
import IconMoon from "~icons/lucide/moon"
import { getPlatformAlternateKey, getPlatformSpecialKey } from "./platformutils" import { getPlatformAlternateKey, getPlatformSpecialKey } from "./platformutils"
export default [ export type ShortcutDef = {
{ label: string
section: "shortcut.general.title", keys: string[]
shortcuts: [ section: string
{ }
keys: ["?"],
label: "shortcut.general.help_menu",
},
{
keys: ["/"],
label: "shortcut.general.command_menu",
},
{
keys: [getPlatformSpecialKey(), "K"],
label: "shortcut.general.show_all",
},
{
keys: ["ESC"],
label: "shortcut.general.close_current_menu",
},
],
},
{
section: "shortcut.request.title",
shortcuts: [
{
keys: [getPlatformSpecialKey(), "↩"],
label: "shortcut.request.send_request",
},
{
keys: [getPlatformSpecialKey(), "S"],
label: "shortcut.request.save_to_collections",
},
{
keys: [getPlatformSpecialKey(), "U"],
label: "shortcut.request.copy_request_link",
},
{
keys: [getPlatformSpecialKey(), "I"],
label: "shortcut.request.reset_request",
},
{
keys: [getPlatformAlternateKey(), "↑"],
label: "shortcut.request.next_method",
},
{
keys: [getPlatformAlternateKey(), "↓"],
label: "shortcut.request.previous_method",
},
{
keys: [getPlatformAlternateKey(), "G"],
label: "shortcut.request.get_method",
},
{
keys: [getPlatformAlternateKey(), "H"],
label: "shortcut.request.head_method",
},
{
keys: [getPlatformAlternateKey(), "P"],
label: "shortcut.request.post_method",
},
{
keys: [getPlatformAlternateKey(), "U"],
label: "shortcut.request.put_method",
},
{
keys: [getPlatformAlternateKey(), "X"],
label: "shortcut.request.delete_method",
},
],
},
{
section: "shortcut.response.title",
shortcuts: [
{
keys: [getPlatformSpecialKey(), "J"],
label: "shortcut.response.download",
},
{
keys: [getPlatformSpecialKey(), "."],
label: "shortcut.response.copy",
},
],
},
{
section: "shortcut.navigation.title",
shortcuts: [
{
keys: [getPlatformSpecialKey(), "←"],
label: "shortcut.navigation.back",
},
{
keys: [getPlatformSpecialKey(), "→"],
label: "shortcut.navigation.forward",
},
{
keys: [getPlatformAlternateKey(), "R"],
label: "shortcut.navigation.rest",
},
{
keys: [getPlatformAlternateKey(), "Q"],
label: "shortcut.navigation.graphql",
},
{
keys: [getPlatformAlternateKey(), "W"],
label: "shortcut.navigation.realtime",
},
{
keys: [getPlatformAlternateKey(), "S"],
label: "shortcut.navigation.settings",
},
{
keys: [getPlatformAlternateKey(), "M"],
label: "shortcut.navigation.profile",
},
],
},
{
section: "shortcut.miscellaneous.title",
shortcuts: [
{
keys: [getPlatformSpecialKey(), "M"],
label: "shortcut.miscellaneous.invite",
},
],
},
]
export const spotlight = [ export function getShortcuts(t: (x: string) => string): ShortcutDef[] {
{ // General
section: "app.spotlight", return [
shortcuts: [ {
{ label: t("shortcut.general.help_menu"),
keys: ["?"], keys: ["?"],
label: "shortcut.general.help_menu", section: t("shortcut.general.title"),
action: "modals.support.toggle", },
icon: IconLifeBuoy, {
}, label: t("shortcut.general.command_menu"),
{ keys: ["/"],
keys: [getPlatformSpecialKey(), "K"], section: t("shortcut.general.title"),
label: "shortcut.general.show_all", },
action: "flyouts.keybinds.toggle", {
icon: IconZap, label: t("shortcut.general.show_all"),
}, keys: [getPlatformSpecialKey(), "K"],
], section: t("shortcut.general.title"),
}, },
{ {
section: "shortcut.navigation.title", label: t("shortcut.general.close_current_menu"),
shortcuts: [ keys: ["ESC"],
{ section: t("shortcut.general.title"),
keys: [getPlatformAlternateKey(), "R"], },
label: "shortcut.navigation.rest",
action: "navigation.jump.rest",
icon: IconArrowRight,
},
{
keys: [getPlatformAlternateKey(), "Q"],
label: "shortcut.navigation.graphql",
action: "navigation.jump.graphql",
icon: IconArrowRight,
},
{
keys: [getPlatformAlternateKey(), "W"],
label: "shortcut.navigation.realtime",
action: "navigation.jump.realtime",
icon: IconArrowRight,
},
{
keys: [getPlatformAlternateKey(), "S"],
label: "shortcut.navigation.settings",
action: "navigation.jump.settings",
icon: IconArrowRight,
},
{
keys: [getPlatformAlternateKey(), "M"],
label: "shortcut.navigation.profile",
action: "navigation.jump.profile",
icon: IconArrowRight,
},
],
},
{
section: "shortcut.miscellaneous.title",
shortcuts: [
{
keys: [getPlatformSpecialKey(), "M"],
label: "shortcut.miscellaneous.invite",
action: "modals.share.toggle",
icon: IconGift,
},
],
},
]
export const fuse = [ // Request
{ {
keys: ["?"], label: t("shortcut.request.send_request"),
label: "shortcut.general.help_menu", keys: [getPlatformSpecialKey(), "↩"],
action: "modals.support.toggle", section: t("shortcut.request.title"),
icon: IconLifeBuoy, },
tags: [ {
"help", keys: [getPlatformSpecialKey(), "S"],
"support", label: t("shortcut.request.save_to_collections"),
"menu", section: t("shortcut.request.title"),
"discord", },
"twitter", {
"documentation", keys: [getPlatformSpecialKey(), "U"],
"troubleshooting", label: t("shortcut.request.copy_request_link"),
"chat", section: t("shortcut.request.title"),
"community", },
"feedback", {
"report", keys: [getPlatformSpecialKey(), "I"],
"bug", label: t("shortcut.request.reset_request"),
"issue", section: t("shortcut.request.title"),
"ticket", },
], {
}, keys: [getPlatformAlternateKey(), "↑"],
{ label: t("shortcut.request.next_method"),
keys: [getPlatformSpecialKey(), "K"], section: t("shortcut.request.title"),
label: "shortcut.general.show_all", },
action: "flyouts.keybinds.toggle", {
icon: IconZap, keys: [getPlatformAlternateKey(), "↓"],
tags: ["keyboard", "shortcuts"], label: t("shortcut.request.previous_method"),
}, section: t("shortcut.request.title"),
{ },
keys: [getPlatformAlternateKey(), "R"], {
label: "shortcut.navigation.rest", keys: [getPlatformAlternateKey(), "G"],
action: "navigation.jump.rest", label: t("shortcut.request.get_method"),
icon: IconArrowRight, section: t("shortcut.request.title"),
tags: ["rest", "jump", "page", "navigation", "go"], },
}, {
{ keys: [getPlatformAlternateKey(), "H"],
keys: [getPlatformAlternateKey(), "Q"], label: t("shortcut.request.head_method"),
label: "shortcut.navigation.graphql", section: t("shortcut.request.title"),
action: "navigation.jump.graphql", },
icon: IconArrowRight, {
tags: ["graphql", "jump", "page", "navigation", "go"], keys: [getPlatformAlternateKey(), "P"],
}, label: t("shortcut.request.post_method"),
{ section: t("shortcut.request.title"),
keys: [getPlatformAlternateKey(), "W"], },
label: "shortcut.navigation.realtime", {
action: "navigation.jump.realtime", keys: [getPlatformAlternateKey(), "U"],
icon: IconArrowRight, label: t("shortcut.request.put_method"),
tags: [ section: t("shortcut.request.title"),
"realtime", },
"jump", {
"page", keys: [getPlatformAlternateKey(), "X"],
"navigation", label: t("shortcut.request.delete_method"),
"websocket", section: t("shortcut.request.title"),
"socket", },
"mqtt",
"sse", // Response
"go", {
], keys: [getPlatformSpecialKey(), "J"],
}, label: t("shortcut.response.download"),
{ section: t("shortcut.response.title"),
keys: [getPlatformAlternateKey(), "S"], },
label: "shortcut.navigation.settings", {
action: "navigation.jump.settings", keys: [getPlatformSpecialKey(), "."],
icon: IconArrowRight, label: t("shortcut.response.copy"),
tags: ["settings", "jump", "page", "navigation", "account", "theme", "go"], section: t("shortcut.response.title"),
}, },
{
keys: [getPlatformAlternateKey(), "M"], // Navigation
label: "shortcut.navigation.profile", {
action: "navigation.jump.profile", keys: [getPlatformSpecialKey(), "←"],
icon: IconArrowRight, label: t("shortcut.navigation.back"),
tags: ["profile", "jump", "page", "navigation", "account", "theme", "go"], section: t("shortcut.navigation.title"),
}, },
{ {
keys: [getPlatformSpecialKey(), "M"], keys: [getPlatformSpecialKey(), ""],
label: "shortcut.miscellaneous.invite", label: t("shortcut.navigation.forward"),
action: "modals.share.toggle", section: t("shortcut.navigation.title"),
icon: IconGift, },
tags: ["invite", "share", "app", "friends", "people", "social"], {
}, keys: [getPlatformAlternateKey(), "R"],
{ label: t("shortcut.navigation.rest"),
keys: [getPlatformAlternateKey(), "0"], section: t("shortcut.navigation.title"),
label: "shortcut.theme.system", },
action: "settings.theme.system", {
icon: IconMonitor, keys: [getPlatformAlternateKey(), "Q"],
tags: ["theme", "system"], label: t("shortcut.navigation.graphql"),
}, section: t("shortcut.navigation.title"),
{ },
keys: [getPlatformAlternateKey(), "1"], {
label: "shortcut.theme.light", keys: [getPlatformAlternateKey(), "W"],
action: "settings.theme.light", label: t("shortcut.navigation.realtime"),
icon: IconSun, section: t("shortcut.navigation.title"),
tags: ["theme", "light"], },
}, {
{ keys: [getPlatformAlternateKey(), "S"],
keys: [getPlatformAlternateKey(), "2"], label: t("shortcut.navigation.settings"),
label: "shortcut.theme.dark", section: t("shortcut.navigation.title"),
action: "settings.theme.dark", },
icon: IconCloud, {
tags: ["theme", "dark"], keys: [getPlatformAlternateKey(), "M"],
}, label: t("shortcut.navigation.profile"),
{ section: t("shortcut.navigation.title"),
keys: [getPlatformAlternateKey(), "3"], },
label: "shortcut.theme.black",
action: "settings.theme.black", // Miscellaneous
icon: IconMoon, {
tags: ["theme", "black"], keys: [getPlatformSpecialKey(), "M"],
}, label: t("shortcut.miscellaneous.invite"),
] section: t("shortcut.miscellaneous.title"),
},
]
}