From 0e4cef36a06446a981652d192c7d2a2cd51c135f Mon Sep 17 00:00:00 2001 From: upsiflu Date: Mon, 10 Feb 2025 10:02:46 +0100 Subject: [PATCH] feat(front): [WIP] Couple modal state with Url query --- front/src/components/ui/Modal.vue | 2 +- front/src/ui/components/UserMenu.vue | 10 ++-- front/src/ui/composables/useModal.ts | 82 ++++++++++++++++++++++++++++ front/src/ui/modals/Languages.vue | 17 +----- front/src/ui/modals/Shortcuts.vue | 17 ++---- 5 files changed, 94 insertions(+), 34 deletions(-) create mode 100644 front/src/ui/composables/useModal.ts diff --git a/front/src/components/ui/Modal.vue b/front/src/components/ui/Modal.vue index c493db6e7..3ceb0be5a 100644 --- a/front/src/components/ui/Modal.vue +++ b/front/src/components/ui/Modal.vue @@ -15,7 +15,7 @@ const props = defineProps<{ cancel?: string } & (ColorProps | DefaultProps)>() -const isOpen = defineModel({ default:false }) +const isOpen = defineModel({ default: false }) const previouslyFocusedElement = ref(); diff --git a/front/src/ui/components/UserMenu.vue b/front/src/ui/components/UserMenu.vue index f9c37ec45..edc45183c 100644 --- a/front/src/ui/components/UserMenu.vue +++ b/front/src/ui/components/UserMenu.vue @@ -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 }}
- + {{ labels.language }}... @@ -130,9 +130,7 @@ const labels = computed(() => ({ {{ labels.docs }} - + {{ labels.shortcuts }} diff --git a/front/src/ui/composables/useModal.ts b/front/src/ui/composables/useModal.ts new file mode 100644 index 000000000..0faa53cac --- /dev/null +++ b/front/src/ui/composables/useModal.ts @@ -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 + * + * ``` + * + * 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` + */ + 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` + */ + 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 } +} diff --git a/front/src/ui/modals/Languages.vue b/front/src/ui/modals/Languages.vue index 20a336a1e..9ff5e1eb3 100644 --- a/front/src/ui/modals/Languages.vue +++ b/front/src/ui/modals/Languages.vue @@ -1,27 +1,16 @@