/** * URL State Composable * Synchronizes component state with URL query parameters */ import { watch, onMounted } from 'vue'; import { useRouter, useRoute } from 'vue-router'; /** * Sync state with URL query parameters * @param {Object} stateRefs - Object of refs to sync {key: ref} * @returns {Object} Control functions */ export function useUrlState(stateRefs) { const router = useRouter(); const route = useRoute(); /** * Update URL with current state */ const updateUrl = () => { const query = {}; Object.entries(stateRefs).forEach(([key, ref]) => { const value = ref.value; if (value !== null && value !== undefined && value !== '') { if (Array.isArray(value)) { query[key] = value.join(','); } else { query[key] = String(value); } } }); router.replace({ query }); }; /** * Load state from URL */ const loadFromUrl = () => { Object.entries(stateRefs).forEach(([key, ref]) => { const value = route.query[key]; if (value) { if (Array.isArray(ref.value)) { ref.value = value.split(','); } else if (typeof ref.value === 'number') { ref.value = parseInt(value, 10) || 0; } else if (typeof ref.value === 'boolean') { ref.value = value === 'true'; } else { ref.value = value; } } }); }; /** * Handle browser back/forward */ const handlePopState = () => { loadFromUrl(); }; onMounted(() => { // Load initial state from URL loadFromUrl(); // Listen for browser back/forward window.addEventListener('popstate', handlePopState); }); // Watch for state changes and update URL Object.values(stateRefs).forEach(ref => { watch(ref, updateUrl, { deep: true }); }); return { updateUrl, loadFromUrl }; }