import { customRef, nextTick, toValue, watch } from 'vue' import type { MaybeRef, MaybeRefOrGetter, Ref } from 'vue' import { useRoute, useRouter } from 'vuepress/client' import type { RouteParamValueRaw, Router } from 'vue-router' import { tryOnScopeDispose } from '@vueuse/core' export type RouteQueryValueRaw = RouteParamValueRaw | string[] export interface ReactiveRouteOptions { /** * Mode to update the router query, ref is also acceptable * * @default 'replace' */ mode?: MaybeRef<'replace' | 'push'> /** * Route instance, use `useRoute()` if not given */ route?: ReturnType /** * Router instance, use `useRouter()` if not given */ router?: ReturnType } export interface ReactiveRouteOptionsWithTransform extends ReactiveRouteOptions { /** * Function to transform data before return */ transform?: (val: V) => R } const _queue = new WeakMap>() export function useRouteQuery( name: string ): Ref export function useRouteQuery< T extends RouteQueryValueRaw = RouteQueryValueRaw, K = T, >( name: string, defaultValue?: MaybeRefOrGetter, options?: ReactiveRouteOptionsWithTransform ): Ref export function useRouteQuery< T extends RouteQueryValueRaw = RouteQueryValueRaw, K = T, >( name: string, defaultValue?: MaybeRefOrGetter, options: ReactiveRouteOptionsWithTransform = {}, ): Ref { const { mode = 'replace', route = useRoute(), router = useRouter(), transform = value => value as any as K, } = options if (!_queue.has(router)) _queue.set(router, new Map()) const _queriesQueue = _queue.get(router)! let query = route.query[name] as any tryOnScopeDispose(() => { query = undefined }) let _trigger: () => void const proxy = customRef((track, trigger) => { _trigger = trigger return { get() { track() return transform(query !== undefined ? query : toValue(defaultValue)) }, set(v) { if (query === v) return query = v _queriesQueue.set(name, v) trigger() nextTick(() => { if (_queriesQueue.size === 0) return const newQueries = Object.fromEntries(_queriesQueue.entries()) _queriesQueue.clear() const { params, query, hash } = route router[toValue(mode)]({ params, query: { ...query, ...newQueries }, hash, }) }) }, } }) watch( () => route.query[name], (v) => { query = v _trigger() }, { flush: 'sync' }, ) return proxy as any as Ref }