feat: basic keyboard shortcuts (#319)
This commit is contained in:
parent
69c1bd8b6a
commit
c4d8137186
11 changed files with 270 additions and 4 deletions
119
components/magickeys/MagickeysKeyboardShortcuts.vue
Normal file
119
components/magickeys/MagickeysKeyboardShortcuts.vue
Normal file
|
@ -0,0 +1,119 @@
|
|||
<script setup lang="ts">
|
||||
const emit = defineEmits(['close'])
|
||||
const { t } = useI18n()
|
||||
|
||||
/* TODOs:
|
||||
* - I18n
|
||||
*/
|
||||
|
||||
interface ShortcutDef {
|
||||
keys: string[]
|
||||
isSequence: boolean
|
||||
}
|
||||
|
||||
interface ShortcutItem {
|
||||
description: string
|
||||
shortcut: ShortcutDef
|
||||
}
|
||||
|
||||
interface ShortcutItemGroup {
|
||||
name: string
|
||||
items: ShortcutItem[]
|
||||
}
|
||||
|
||||
const shortcutItemGroups: ShortcutItemGroup[] = [
|
||||
{
|
||||
name: t('magic_keys.groups.navigation.title'),
|
||||
items: [
|
||||
{
|
||||
description: t('magic_keys.groups.navigation.shortcut_help'),
|
||||
shortcut: { keys: ['?'], isSequence: false },
|
||||
},
|
||||
// {
|
||||
// description: t('magic_keys.groups.navigation.next_status'),
|
||||
// shortcut: { keys: ['j'], isSequence: false },
|
||||
// },
|
||||
// {
|
||||
// description: t('magic_keys.groups.navigation.previous_status'),
|
||||
// shortcut: { keys: ['k'], isSequence: false },
|
||||
// },
|
||||
{
|
||||
description: t('magic_keys.groups.navigation.go_to_home'),
|
||||
shortcut: { keys: ['g', 'h'], isSequence: true },
|
||||
},
|
||||
{
|
||||
description: t('magic_keys.groups.navigation.go_to_notifications'),
|
||||
shortcut: { keys: ['g', 'n'], isSequence: true },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: t('magic_keys.groups.actions.title'),
|
||||
items: [
|
||||
{
|
||||
description: t('magic_keys.groups.actions.command_mode'),
|
||||
shortcut: { keys: ['cmd', '/'], isSequence: false },
|
||||
},
|
||||
{
|
||||
description: t('magic_keys.groups.actions.compose'),
|
||||
shortcut: { keys: ['c'], isSequence: false },
|
||||
},
|
||||
{
|
||||
description: t('magic_keys.groups.actions.favourite'),
|
||||
shortcut: { keys: ['f'], isSequence: false },
|
||||
},
|
||||
{
|
||||
description: t('magic_keys.groups.actions.boost'),
|
||||
shortcut: { keys: ['b'], isSequence: false },
|
||||
},
|
||||
{
|
||||
description: t('magic_keys.groups.actions.zen_mode'),
|
||||
shortcut: { keys: ['z'], isSequence: false },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: t('magic_keys.groups.media.title'),
|
||||
items: [],
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div px-3 sm:px-5 py-2 sm:py-4 max-w-220 relative max-h-screen>
|
||||
<button btn-action-icon absolute top-1 sm:top-2 right-1 sm:right-2 m1 :aria-label="$t('modals.aria_label_close')" @click="emit('close')">
|
||||
<div i-ri:close-fill />
|
||||
</button>
|
||||
<h2 text-xl font-700 mb3>
|
||||
{{ $t('magic_keys.dialog_header') }}
|
||||
</h2>
|
||||
<div mb2 grid grid-cols-1 md:grid-cols-3 gap-y- md:gap-x-6 lg:gap-x-8>
|
||||
<div
|
||||
v-for="group in shortcutItemGroups"
|
||||
:key="group.name"
|
||||
>
|
||||
<h3 font-700 my-2 text-lg>
|
||||
{{ group.name }}
|
||||
</h3>
|
||||
<div
|
||||
v-for="item in group.items"
|
||||
:key="item.description"
|
||||
flex my-1 lg:my-2 justify-between place-items-center max-w-full text-base
|
||||
>
|
||||
<div mr-2 break-words overflow-hidden leading-4 h-full inline-block align-middle>
|
||||
{{ item.description }}
|
||||
</div>
|
||||
<div>
|
||||
<template
|
||||
v-for="(key, idx) in item.shortcut.keys"
|
||||
:key="idx"
|
||||
>
|
||||
<span v-if="idx !== 0" mx1 text-sm op80>{{ item.shortcut.isSequence ? $t('magic_keys.sequence_then') : '+' }}</span>
|
||||
<code class="px2 md:px1.5 lg:px2 lg:px2 py0 lg:py-0.5" rounded bg-code border="px $c-border-code" shadow-sm my1 font-mono font-600>{{ key }}</code>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -7,6 +7,7 @@ import {
|
|||
isEditHistoryDialogOpen,
|
||||
isErrorDialogOpen,
|
||||
isFavouritedBoostedByDialogOpen,
|
||||
isKeyboardShortcutsDialogOpen,
|
||||
isMediaPreviewOpen,
|
||||
isPreviewHelpOpen,
|
||||
isPublishDialogOpen,
|
||||
|
@ -98,5 +99,8 @@ const handleFavouritedBoostedByClose = () => {
|
|||
>
|
||||
<StatusFavouritedBoostedBy />
|
||||
</ModalDialog>
|
||||
<ModalDialog v-model="isKeyboardShortcutsDialogOpen" max-w-full sm:max-w-140 md:max-w-170 lg:max-w-220 md:min-w-160>
|
||||
<MagickeysKeyboardShortcuts @close="closeKeyboardShortcuts()" />
|
||||
</ModalDialog>
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
@ -156,7 +156,7 @@ defineExpose({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="isHydrated && currentUser" flex="~ col gap-4" py3 px2 sm:px4>
|
||||
<div v-if="isHydrated && currentUser" flex="~ col gap-4" py3 px2 sm:px4 aria-roledescription="publish-widget">
|
||||
<template v-if="draft.editingStatus">
|
||||
<div flex="~ col gap-1">
|
||||
<div id="state-editing" text-secondary self-center>
|
||||
|
|
|
@ -85,6 +85,7 @@ const showReplyTo = $computed(() => !replyToMain && !directReply)
|
|||
:class="{ 'hover:bg-active': hover }"
|
||||
tabindex="0"
|
||||
focus:outline-none focus-visible:ring="2 primary"
|
||||
aria-roledescription="status-card"
|
||||
:lang="status.language ?? undefined"
|
||||
@click="onclick"
|
||||
@keydown.enter="onclick"
|
||||
|
|
|
@ -30,7 +30,7 @@ const isDM = $computed(() => status.visibility === 'direct')
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div :id="`status-${status.id}`" flex flex-col gap-2 pt2 pb1 ps-3 pe-4 relative :lang="status.language ?? undefined">
|
||||
<div :id="`status-${status.id}`" flex flex-col gap-2 pt2 pb1 ps-3 pe-4 relative :lang="status.language ?? undefined" aria-roledescription="status-details">
|
||||
<StatusActionsMore :status="status" absolute inset-ie-2 top-2 />
|
||||
<NuxtLink :to="getAccountRoute(status.account)" rounded-full hover:bg-active transition-100 pe5 me-a>
|
||||
<AccountHoverWrapper :account="status.account">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue