feat(front): [WIP] Couple modal state with Url query
This commit is contained in:
parent
a2824e1f43
commit
0e4cef36a0
|
@ -15,7 +15,7 @@ const props = defineProps<{
|
|||
cancel?: string
|
||||
} & (ColorProps | DefaultProps)>()
|
||||
|
||||
const isOpen = defineModel<boolean>({ default:false })
|
||||
const isOpen = defineModel<boolean>({ default: false })
|
||||
|
||||
const previouslyFocusedElement = ref();
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ import { useRoute } from 'vue-router'
|
|||
import useThemeList from '~/composables/useThemeList'
|
||||
import useTheme from '~/composables/useTheme'
|
||||
|
||||
import { useModal } from '~/ui/composables/useModal.ts'
|
||||
|
||||
import Button from '~/components/ui/Button.vue'
|
||||
import Popover from '~/components/ui/Popover.vue'
|
||||
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
|
||||
|
@ -89,9 +91,7 @@ const labels = computed(() => ({
|
|||
{{ labels.settings }}
|
||||
</PopoverItem>
|
||||
<hr v-if="store.state.auth.authenticated"/>
|
||||
<PopoverItem @click="store.commit('ui/toggleModal', 'languages')"
|
||||
:aria-pressed="store.state.ui.modalsOpen.has('languages') || undefined"
|
||||
>
|
||||
<PopoverItem :to="useModal('languages').to">
|
||||
<i class="bi bi-translate" />
|
||||
{{ labels.language }}...
|
||||
</PopoverItem>
|
||||
|
@ -130,9 +130,7 @@ const labels = computed(() => ({
|
|||
<i class="bi bi-book" />
|
||||
{{ labels.docs }}
|
||||
</PopoverItem>
|
||||
<PopoverItem @click="store.commit('ui/toggleModal', 'shortcuts')"
|
||||
:aria-pressed="store.state.ui.modalsOpen.has('shortcuts') || undefined"
|
||||
>
|
||||
<PopoverItem :to="useModal('shortcuts').to">
|
||||
<i class="bi bi-keyboard" />
|
||||
{{ labels.shortcuts }}
|
||||
</PopoverItem>
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import { computed } from 'vue'
|
||||
import { useRouter, type RouteLocationRaw } from 'vue-router'
|
||||
|
||||
/**
|
||||
* Bind a modal to a single query parameter in the URL (and vice versa)
|
||||
*
|
||||
* @param flag query parameter to appear in the `url`, corresponding with `isOpen`
|
||||
*
|
||||
* This functionality completely independent from the `router` modules.
|
||||
*/
|
||||
export function useModal(flag: string) {
|
||||
const router = useRouter()
|
||||
const query = computed(() => router?.currentRoute.value ? router.currentRoute.value.query : {})
|
||||
|
||||
/**
|
||||
* Visibility of this modal
|
||||
*
|
||||
* Use this function to bind a modal to a query parameter in the URL.
|
||||
* Using this helper function will not change the routes.
|
||||
*
|
||||
* For example, if the modal flag is 'upload':
|
||||
*
|
||||
* ```vue
|
||||
* <template>
|
||||
* <Modal :open="isOpen('upload')">...</Modal>
|
||||
* </template>
|
||||
* ```
|
||||
*
|
||||
* Url: `https://funkwhale.audio/?upload`
|
||||
*
|
||||
* More information on query flags: https://router.vuejs.org/api/type-aliases/LocationQueryValue.html
|
||||
*
|
||||
* Use `asAttribute` instead to bind a modal to an on/off attribute such as `aria-expanded` or `aria-pressed`
|
||||
*
|
||||
* @param flag Identifier for the modal
|
||||
* @returns a `ref`<boolean>
|
||||
*/
|
||||
const isOpen = computed({
|
||||
get() {
|
||||
const newValue = flag in query.value && query.value[flag] === null
|
||||
// console.log("GET isOpen", flag, "in", query, newValue)
|
||||
return newValue
|
||||
},
|
||||
set(newValue: boolean) {
|
||||
// console.log("SET isOpen", query, "+", newValue, ", was", isOpen.value)
|
||||
router.push({ query: { ...query.value, [flag]: newValue ? null : undefined }}).catch((err) => {
|
||||
throw new Error(`Problem pushing route query: ${err}.`);
|
||||
});
|
||||
// console.log("DONE SET isOpen", query)
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
*
|
||||
* @param flag Identifier for the modal
|
||||
* @returns a `RouteLocationRaw` object to use in the `to` prop of a RouterLink component
|
||||
*/
|
||||
const to: RouteLocationRaw = {
|
||||
query: { ...query.value, [flag]: null }
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this function to bind a modal to an on/off attribute such as `aria-expanded` or `aria-pressed`
|
||||
*
|
||||
* @param flag Identifier for the modal
|
||||
* @returns a `ref`<true | undefined>
|
||||
*/
|
||||
const asAttribute = computed(() =>
|
||||
isOpen.value || undefined
|
||||
)
|
||||
|
||||
/**
|
||||
* Toggle the visibility of this modal
|
||||
*
|
||||
*
|
||||
* @param flag Identifier for the modal
|
||||
*/
|
||||
const toggle = () =>
|
||||
isOpen.value = !isOpen.value
|
||||
|
||||
return { isOpen, to, asAttribute, toggle }
|
||||
}
|
|
@ -1,27 +1,16 @@
|
|||
<script setup lang="ts">
|
||||
import { SUPPORTED_LOCALES, setI18nLanguage } from '~/init/locale'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useStore } from '~/store'
|
||||
|
||||
import { useModal } from '~/ui/composables/useModal.ts'
|
||||
|
||||
import Modal from '~/components/ui/Modal.vue'
|
||||
import Button from '~/components/ui/Button.vue'
|
||||
import Layout from '~/components/ui/Layout.vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const store = useStore()
|
||||
|
||||
const modalName = 'languages'
|
||||
|
||||
const isOpen = computed({
|
||||
get() {
|
||||
return store.state.ui.modalsOpen.has(modalName);
|
||||
},
|
||||
set(value) {
|
||||
store.commit('ui/setModal', [modalName, value]);
|
||||
}
|
||||
})
|
||||
|
||||
const isOpen = useModal('languages').isOpen
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,29 +1,20 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useStore } from '~/store'
|
||||
import onKeyboardShortcut from '~/composables/onKeyboardShortcut';
|
||||
|
||||
import { useModal } from '~/ui/composables/useModal.ts'
|
||||
|
||||
import Modal from '~/components/ui/Modal.vue'
|
||||
import Button from '~/components/ui/Button.vue'
|
||||
import Layout from '~/components/ui/Layout.vue'
|
||||
import Spacer from '~/components/ui/Spacer.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
const store = useStore()
|
||||
|
||||
const modalName = 'shortcuts'
|
||||
const isOpen = useModal('shortcuts').isOpen
|
||||
|
||||
const isOpen = computed({
|
||||
get() {
|
||||
return store.state.ui.modalsOpen.has(modalName);
|
||||
},
|
||||
set(value) {
|
||||
store.commit('ui/setModal', [modalName, value]);
|
||||
}
|
||||
})
|
||||
|
||||
onKeyboardShortcut('h', () => store.commit('ui/toggleModal', modalName))
|
||||
onKeyboardShortcut('h', useModal('shortcuts').toggle)
|
||||
|
||||
const general = computed(() => [
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue