// Utilities import { computed, nextTick, onScopeDispose, resolveDynamicComponent, toRef, } from 'vue' import { getCurrentInstance, hasEvent, IN_BROWSER, propsFactory } from '@/utils' // Types import type { ComputedRef, PropType, Ref, SetupContext } from 'vue' import type { RouterLink as _RouterLink, useLink as _useLink, NavigationGuardNext, RouteLocationNormalizedLoaded, RouteLocationRaw, Router, UseLinkOptions, } from 'vue-router' import type { EventProp } from '@/utils' export function useRoute(): Ref { const vm = getCurrentInstance('useRoute') return computed(() => vm?.proxy?.$route) } export function useRouter(): Router | undefined { return getCurrentInstance('useRouter')?.proxy?.$router } export interface LinkProps { href: string | undefined replace: boolean | undefined to: RouteLocationRaw | undefined exact: boolean | undefined } export interface LinkListeners { onClick?: EventProp | undefined onClickOnce?: EventProp | undefined } export interface UseLink extends Omit>, 'href'> { isLink: ComputedRef isClickable: ComputedRef href: Ref } export function useLink( props: LinkProps & LinkListeners, attrs: SetupContext['attrs'] ): UseLink { const RouterLink = resolveDynamicComponent('RouterLink') as | typeof _RouterLink | string const isLink = computed(() => !!(props.href || props.to)) const isClickable = computed(() => { return isLink?.value || hasEvent(attrs, 'click') || hasEvent(props, 'click') }) if (typeof RouterLink === 'string') { return { isLink, isClickable, href: toRef(props, 'href'), } } const link = props.to ? RouterLink.useLink(props as UseLinkOptions) : undefined return { isLink, isClickable, route: link?.route, navigate: link?.navigate, isActive: link && computed(() => props.exact ? link.isExactActive?.value : link.isActive?.value ), href: computed(() => (props.to ? link?.route.value.href : props.href)), } } export const makeRouterProps = propsFactory( { href: String, replace: Boolean, to: [String, Object] as PropType, exact: Boolean, }, 'router' ) let inTransition = false export function useBackButton( router: Router | undefined, cb: (next: NavigationGuardNext) => void ) { let popped = false let removeBefore: (() => void) | undefined let removeAfter: (() => void) | undefined if (IN_BROWSER) { nextTick(() => { window.addEventListener('popstate', onPopstate) removeBefore = router?.beforeEach((to, from, next) => { if (!inTransition) { setTimeout(() => (popped ? cb(next) : next())) } else { popped ? cb(next) : next() } inTransition = true }) removeAfter = router?.afterEach(() => { inTransition = false }) }) onScopeDispose(() => { window.removeEventListener('popstate', onPopstate) removeBefore?.() removeAfter?.() }) } function onPopstate(e: PopStateEvent) { if (e.state?.replaced) return popped = true setTimeout(() => (popped = false)) } }