feat(ui): enable and style shortcuts modal

This commit is contained in:
upsiflu 2024-12-17 12:57:11 +01:00
parent 74958dcf34
commit 1740fa7c11
7 changed files with 67 additions and 38 deletions

View File

@ -1,12 +1,11 @@
<script setup lang="ts">
// import LegacyLayout from '~/LegacyLayout.vue'
// TODO: Check if we ever need LegacyLayout
// TODO: see below (isUIv2)
import UiApp from '~/ui/App.vue'
import type { QueueTrack } from '~/composables/audio/queue'
import { watchEffect, computed, onMounted, nextTick } from 'vue'
import onKeyboardShortcut from '~/composables/onKeyboardShortcut'
import { useQueue } from '~/composables/audio/queue'
import { useStore } from '~/store'
@ -24,9 +23,9 @@ const ShortcutsModal = defineAsyncComponent(() => import('~/components/Shortcuts
const AudioPlayer = defineAsyncComponent(() => import('~/components/audio/Player.vue'))
const Sidebar = defineAsyncComponent(() => import('~/components/Sidebar.vue'))
const Queue = defineAsyncComponent(() => import('~/components/Queue.vue'))
import { useLocalStorage } from '@vueuse/core'
import { useLocalStorage, useStyleTag, useIntervalFn, useToggle, useWindowSize } from '@vueuse/core'
import { useLocalStorage, useStyleTag, useIntervalFn, useToggle } from '@vueuse/core'
const logger = useLogger()
logger.debug('App setup()')
@ -70,10 +69,6 @@ useIntervalFn(() => {
store.commit('ui/computeLastDate')
}, 1000 * 60)
// Shortcuts
const [_, toggleShortcutsModal] = useToggle(false)
onKeyboardShortcut('h', () => toggleShortcutsModal())
// Fetch user data on startup
// NOTE: We're not checking if we're authenticated in the store,
// because we want to learn if we are authenticated at all

View File

@ -3,6 +3,7 @@ import Modal from '~/components/ui/Modal.vue'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import Button from '~/components/ui/Button.vue'
import Layout from '~/components/ui/Layout.vue'
const model = defineModel<boolean>()
@ -98,52 +99,57 @@ const player = computed(() => [
<template>
<Modal
overPopover
:title="t('components.ShortcutsModal.header.modal')"
v-model="model">
<section class="scrolling content">
<div class="ui stackable two column grid">
<div class="column">
<Layout flex>
<div class="column" :class="$style.withPadding">
<table
v-for="section in player"
:key="section.title"
class="ui compact basic table"
>
<caption>{{ section.title }}</caption>
<h3>{{ section.title }}</h3>
<tbody>
<tr
v-for="shortcut in section.shortcuts"
:key="shortcut.summary"
>
<td>{{ shortcut.summary }}</td>
<td><span class="ui label">{{ shortcut.key }}</span></td>
<td :class="$style.description">{{ shortcut.summary }}</td>
<td>          <Button style="pointer-events:none;" class="is-icon-only">{{ shortcut.key }}</Button></td>
</tr>
</tbody>
</table>
</div>
<div class="column">
<div class="column" :class="$style.withPadding">
<table
v-for="section in general"
:key="section.title"
class="ui compact basic table"
>
<caption>{{ section.title }}</caption>
<h3>{{ section.title }}</h3>
<tbody>
<tr
v-for="shortcut in section.shortcuts"
:key="shortcut.summary"
>
<td>{{ shortcut.summary }}</td>
<td><span class="ui label">{{ shortcut.key }}</span></td>
<td :class="$style.description">{{ shortcut.summary }}</td>
<td>          <Button style="pointer-events:none;" class="is-icon-only">{{ shortcut.key }}</Button></td>
</tr>
</tbody>
</table>
</div>
</div>
</Layout>
</section>
<footer class="actions">
<Button color="secondary">
{{ t('components.ShortcutsModal.button.close') }}
</Button>
</footer>
</Modal>
</template>
<style module>
.withPadding {
padding:8px;
}
.description {
font-size: 0.875em;
}
</style>

View File

@ -1,5 +1,6 @@
<script setup lang="ts">
import Button from '~/components/ui/Button.vue'
import Spacer from '~/components/ui/layout/Spacer.vue'
const { title, overPopover = false } = defineProps<{ title:string, overPopover?:true }>()
const isOpen = defineModel<boolean>({ default:false })
@ -34,6 +35,7 @@ const isOpen = defineModel<boolean>({ default:false })
<div v-if="$slots.actions" class="modal-actions">
<slot name="actions" />
</div>
<Spacer :size="128" v-else />
</div>
</div>
</Transition>

View File

@ -3,7 +3,7 @@
box-shadow: 0 2px 4px 2px rgba(#000, 0.2);
border-radius: 1rem;
max-width: min(90vw, 40rem);
max-width: min(90vw, 55rem);
width: 100%;
display: grid;

View File

@ -1,20 +1,26 @@
<script setup lang="ts">
import { onMounted, nextTick } from 'vue'
import { onMounted, nextTick, ref, defineAsyncComponent } from 'vue'
import onKeyboardShortcut from '~/composables/onKeyboardShortcut';
import Sidebar from '~/ui/components/Sidebar.vue'
import ShortcutsModal from '~/components/ShortcutsModal.vue'
// Fake content
onMounted(async () => {
await nextTick()
document.getElementById('fake-app')?.remove()
})
const isShortcutsModalOpen = ref(false)
onKeyboardShortcut('h', () => isShortcutsModalOpen.value = !isShortcutsModalOpen.value)
</script>
<template>
<div class="funkwhale grid">
<Sidebar />
<Sidebar :openShortcutsModal = "() => isShortcutsModalOpen=true" />
<main>
<RouterView />
</main>
<ShortcutsModal v-model="isShortcutsModalOpen" />
</div>
</template>

View File

@ -13,6 +13,8 @@ import Button from '~/components/ui/Button.vue'
import Layout from '~/components/ui/Layout.vue'
import Spacer from '~/components/ui/layout/Spacer.vue'
const { openShortcutsModal } = defineProps<{ openShortcutsModal: ()=> void }>()
const searchQuery = ref('')
// Hide the fake app when the real one is loaded
@ -62,7 +64,7 @@ const uploads = useUploadsStore()
</Button>
</Link>
<UserMenu />
<UserMenu :showShortcutsModal="openShortcutsModal" />
</nav>
</header>
@ -129,7 +131,7 @@ const uploads = useUploadsStore()
{{ t('components.Sidebar.link.favorites') }}
</Link>
</nav>
<Spacer grow />
<Spacer />
<h3>{{ t('components.Sidebar.link.channels') }}</h3>
<Spacer grow />
<nav>

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import { SUPPORTED_LOCALES, setI18nLanguage } from '~/init/locale'
import { useI18n } from 'vue-i18n'
import { computed, ref } from 'vue'
import { computed, ref, watch, watchEffect } from 'vue'
import { useStore } from '~/store'
import { useRoute } from 'vue-router'
@ -16,16 +16,17 @@ import PopoverRadio from '~/components/ui/popover/PopoverRadio.vue'
import PopoverRadioItem from '~/components/ui/popover/PopoverRadioItem.vue'
import PopoverSubmenu from '~/components/ui/popover/PopoverSubmenu.vue'
import Link from '~/components/ui/Link.vue'
import Modal from '~/components/ui/Modal.vue'
const { showShortcutsModal } = defineProps<{
showShortcutsModal : () => void
}>()
interface Events {
(e: 'show:shortcuts-modal'): void
}
const route = useRoute()
const store = useStore()
const emit = defineEmits<Events>()
const { t, locale } = useI18n()
const themes = useThemeList()
@ -33,6 +34,8 @@ const { theme } = useTheme()
const openUserMenu = ref(false)
const isLanguageModelOpen = ref(false)
const labels = computed(() => ({
profile: t('components.common.UserMenu.link.profile'),
settings: t('components.common.UserMenu.link.settings'),
@ -50,6 +53,10 @@ const labels = computed(() => ({
signup: t('components.common.UserMenu.link.signup'),
notifications: t('components.common.UserMenu.link.notifications')
}))
watchEffect(()=> {
isLanguageModelOpen.value = isLanguageModelOpen.value && openUserMenu.value
})
</script>
<template>
@ -57,7 +64,7 @@ const labels = computed(() => ({
<Button
@click="openUserMenu = !openUserMenu"
round
icon
icon=""
ghost
class="is-icon-only"
>
@ -69,11 +76,11 @@ const labels = computed(() => ({
<i v-else class="bi bi-gear-fill" />
</Button>
<template #items>
<PopoverItem v-if="store.state.ui.notifications.inbox + additionalNotifications > 0">
<PopoverItem v-if="store.state.ui.notifications.inbox /* TODO: Check: + additionalNotifications */ > 0">
<Link :to="{name: 'notifications'}">
<i class="bi bi-inbox-fill" />
>
{{ store.state.ui.notifications.inbox + additionalNotifications }}
{{ store.state.ui.notifications.inbox /* TODO: Check: + additionalNotifications */ }}
{{ labels.notifications }}
</Link>
</PopoverItem>
@ -94,6 +101,17 @@ const labels = computed(() => ({
</Link>
</PopoverItem>
<hr v-if="store.state.auth.authenticated"/>
<PopoverItem @click="isLanguageModelOpen = true">
<i class="bi bi-translate" />
{{ labels.language }}...
<Modal v-model="isLanguageModelOpen" :title="labels.language" overPopover>
<Button ghost v-for="(language, key) in SUPPORTED_LOCALES"
:key="key"
@click="setI18nLanguage(key)" >
{{ language }}
</Button>
</Modal>
</PopoverItem>
<PopoverSubmenu>
<i class="bi bi-translate" />
{{ labels.language }}
@ -161,7 +179,7 @@ const labels = computed(() => ({
</nav>
</PopoverItem>
<PopoverItem
@click.prevent="emit('show:shortcuts-modal')"
@click.prevent="showShortcutsModal ()"
>
<i class="bi bi-keyboard" />
{{ labels.shortcuts }}