forked from tsai/budibase
3 changed files with 142 additions and 11 deletions
@ -0,0 +1,106 @@ |
|||
import { get } from "svelte/store" |
|||
|
|||
export const syncURLToState = options => { |
|||
const { keys, params, store, goto } = options || {} |
|||
if ( |
|||
!keys?.length || |
|||
!params?.subscribe || |
|||
!store?.subscribe || |
|||
!goto?.subscribe |
|||
) { |
|||
return |
|||
} |
|||
|
|||
// We can't dynamically fetch the value of routify stores so we need to
|
|||
// just subscribe and cache the latest versions.
|
|||
// We can grab their initial values as this is during component
|
|||
// initialisation.
|
|||
let cachedParams = get(params) |
|||
let cachedGoto = get(goto) |
|||
|
|||
// Updates state with new URL params
|
|||
const mapUrlToState = params => { |
|||
// Determine any required state updates
|
|||
let stateUpdates = [] |
|||
const state = get(store) |
|||
for (let key of keys) { |
|||
const urlValue = params?.[key.url] |
|||
const stateValue = state?.[key.state] |
|||
if (urlValue !== stateValue) { |
|||
console.log( |
|||
`state.${key.state} (${stateValue}) <= url.${key.url} (${urlValue})` |
|||
) |
|||
stateUpdates.push(state => { |
|||
state[key.state] = urlValue |
|||
}) |
|||
} |
|||
} |
|||
|
|||
// Avoid updating the store at all if not necessary to prevent a wasted
|
|||
// store invalidation
|
|||
if (!stateUpdates.length) { |
|||
return |
|||
} |
|||
|
|||
// Apply the required state updates
|
|||
console.log("Performing", stateUpdates.length, "state updates") |
|||
store.update(state => { |
|||
for (let update of stateUpdates) { |
|||
update(state) |
|||
} |
|||
return state |
|||
}) |
|||
} |
|||
|
|||
// Updates the URL with new state values
|
|||
const mapStateToUrl = state => { |
|||
// Determine new URL while checking for changes
|
|||
let url = "." |
|||
let needsUpdate = false |
|||
for (let key of keys) { |
|||
const urlValue = cachedParams?.[key.url] |
|||
const stateValue = state?.[key.state] |
|||
url += `/${stateValue}` |
|||
if (stateValue !== urlValue) { |
|||
console.log( |
|||
`url.${key.url} (${urlValue}) <= state.${key.state} (${stateValue})` |
|||
) |
|||
needsUpdate = true |
|||
} |
|||
} |
|||
|
|||
// Avoid updating the URL if not necessary to prevent a wasted render
|
|||
// cycle
|
|||
if (!needsUpdate) { |
|||
return |
|||
} |
|||
|
|||
// Navigate to the new URL
|
|||
console.log("Navigating to", url) |
|||
cachedGoto(url) |
|||
} |
|||
|
|||
// Initially hydrate state from URL
|
|||
mapUrlToState(cachedParams) |
|||
|
|||
// Subscribe to URL changes and cache them
|
|||
const unsubscribeParams = params.subscribe($urlParams => { |
|||
cachedParams = $urlParams |
|||
mapUrlToState($urlParams) |
|||
}) |
|||
|
|||
// Subscribe to goto changes and cache them
|
|||
const unsubscribeGoto = goto.subscribe($goto => { |
|||
cachedGoto = $goto |
|||
}) |
|||
|
|||
// Subscribe to store changes and keep URL up to date
|
|||
const unsubscribeStore = store.subscribe(mapStateToUrl) |
|||
|
|||
// Return an unsync function to cancel subscriptions
|
|||
return () => { |
|||
unsubscribeParams() |
|||
unsubscribeGoto() |
|||
unsubscribeStore() |
|||
} |
|||
} |
|||
Loading…
Reference in new issue