import{useRouteQuery}from"@vueuse/router";import{watch}from"vue";import{useRoute,useRouter}from"vue-router";interfaceSyncOptions{name?: string;array?: boolean;number?:boolean;boolean?:boolean;defaultValue?: any;comma?: boolean;dynamicPrefix?: string;// e.g. "f:" for f:key=value mapping to a Record<string, string[]>
}/**
* Synchronizes Pinia store properties with URL query parameters.
* @param store The Pinia store instance.
* @param keys A map of store keys to sync options.
*/exportfunctionuseQuerySync<TextendsRecord<string,any>>(store: T,keys: Partial<Record<keyofT&string,SyncOptions>>,){constroute=useRoute();constrouter=useRouter();for(conststoreKeyinkeys){constconfig=keys[storeKey]!;if(config.dynamicPrefix){// Dynamic handling for Record<string, string[]>
constprefix=config.dynamicPrefix;// 1. URL -> Store (Watch route query)
watch(()=>route.query,(query)=>{constnewStoreValue: Record<string,string[]>={};Object.entries(query).forEach(([key,val])=>{if(key.startsWith(prefix)&&val!==null&&val!==undefined){constcleanKey=key.slice(prefix.length);// Handle potentially multiple values for the same key (though vue-router usually invalidates duplicate keys unless array mode,
// but standard query params can be arrays. useRouteQuery handles this, but here we access route.query directly).
// route.query values are string | null | (string | null)[]
constvalues: string[]=Array.isArray(val)?(val.filter((v)=>v!==null)asstring[]):[valasstring];newStoreValue[cleanKey]=values;}});// Preserve empty arrays from current store state that are missing from URL
// This prevents the UI from removing filters immediately when the last value is cleared
constcurrentStoreValue=store[storeKey]asRecord<string,string[]>;if(currentStoreValue){Object.keys(currentStoreValue).forEach((k)=>{if(!newStoreValue[k]&&Array.isArray(currentStoreValue[k])&¤tStoreValue[k].length===0){newStoreValue[k]=[];}});}// Update store if different. We do a shallow comparison of keys and values.
// Simple check: different number of keys
if(Object.keys(newStoreValue).length!==Object.keys(currentStoreValue).length){(storeasany)[storeKey]=newStoreValue;return;}// Check content
letchanged=false;for(constkinnewStoreValue){constvNew=newStoreValue[k];constvOld=currentStoreValue[k];if(!vOld||!vNew||vNew.length!==vOld.length||!vNew.every((val,i)=>val===vOld[i])){changed=true;break;}}if(changed){(storeasany)[storeKey]=newStoreValue;}},{immediate: true,deep: true},);// 2. Store -> URL (Watch store value)
watch(()=>store[storeKey],(newVal)=>{constcurrentQuery={...route.query};letqueryChanged=false;// Remove all existing keys with this prefix
Object.keys(currentQuery).forEach((k)=>{if(k.startsWith(prefix)){deletecurrentQuery[k];queryChanged=true;}});// Add new keys
constnewRecord=newValasRecord<string,string[]>;Object.entries(newRecord).forEach(([k,values])=>{if(values&&values.length>0){// We'll just set it. If there are multiple values, vue-router 4 supports arrays.
(currentQueryasany)[prefix+k]=values;queryChanged=true;}});if(queryChanged){router.push({query: currentQuery});}},{deep: true},);continue;// Skip standard handling
}// Standard handling
constqueryKey=config.name||storeKey;letdefaultValue=config.defaultValue!==undefined?config.defaultValue : store[storeKey];if(Array.isArray(defaultValue)){defaultValue=[...defaultValue];}constq=useRouteQuery(queryKey,defaultValue,{mode:"push",transform:{get:(val: any)=>{if(val===undefined||val===null)returndefaultValue;if(config.array){if(Array.isArray(val))returnval;if(typeofval==="string"){if(config.comma){returnval?val.split(","):[];}return[val];}return[val];}if(config.number){constnum=Number(val);returnisNaN(num)?defaultValue : num;}if(config.boolean){returnval==="true"||val===true;}returnval;},set:(val: any)=>{// Avoid cluttering the URL with default values
if(val===defaultValue||JSON.stringify(val)===JSON.stringify(defaultValue)){returnundefined;}if(config.array&&config.comma&&Array.isArray(val)){returnval.join(",");}returnval;},},});// 1. Initialize store from URL (immediate watch handles this via useRouteQuery internals usually, but we keep explicit sync)
// 2. Keep store in sync
watch(q,(newVal)=>{if(JSON.stringify(store[storeKey])!==JSON.stringify(newVal)){(storeasany)[storeKey]=newValasany;}},{immediate: true},);// 3. Keep URL in sync
watch(()=>store[storeKey],(newVal)=>{if(JSON.stringify(q.value)!==JSON.stringify(newVal)){q.value=newValasany;}},{deep: true},);}}