import { ref, watch } from "vue"; import type { Ref } from "vue"; import type { RouteLocationNormalizedLoaded } from "vue-router"; interface UseOpenedMenusOptions { route: RouteLocationNormalizedLoaded; accordion: Ref; } export const useOpenedMenus = ({ route, accordion }: UseOpenedMenusOptions) => { const openedMenus = ref([]); const stringify = (arr: string[]) => JSON.stringify([...arr].sort()); const normalizePath = (path: string) => (path.startsWith("/") ? path : `/${path}`); const collectAncestorPaths = (path?: string) => { if (!path) return []; const normalized = normalizePath(path); const segments = normalized.split("/").filter(Boolean); return segments.reduce((acc, _, index) => { if (index === segments.length - 1) return acc; acc.push(`/${segments.slice(0, index + 1).join("/")}`); return acc; }, []); }; const buildTargetOpenedPaths = () => { const targetSet = new Set(); collectAncestorPaths(route.path).forEach(path => targetSet.add(path)); const activeMenuPath = route.meta.activeMenu as string | undefined; if (activeMenuPath) { const normalizedActive = normalizePath(activeMenuPath); collectAncestorPaths(normalizedActive).forEach(path => targetSet.add(path)); targetSet.add(normalizedActive); } return Array.from(targetSet); }; const calculateOpenedMenus = () => { const targetPaths = buildTargetOpenedPaths(); if (!targetPaths.length) { return accordion.value ? [] : [...openedMenus.value]; } if (accordion.value) return targetPaths; const menuSet = new Set(openedMenus.value); targetPaths.forEach(path => menuSet.add(path)); return Array.from(menuSet); }; watch( () => [route.path, route.meta.activeMenu, accordion.value], () => { const newOpenedMenus = calculateOpenedMenus(); if (stringify(newOpenedMenus) !== stringify(openedMenus.value)) { openedMenus.value = newOpenedMenus; } }, { immediate: true } ); return { openedMenus }; };