1
1
import { writable , type Writable } from 'svelte/store' ;
2
2
import { goto } from '$app/navigation' ;
3
3
import { page } from '$app/stores' ;
4
+ import { cleanObject } from './clean-object' ;
4
5
5
6
export interface QueryParamStore < T > extends Writable < T > {
6
7
remove : ( ) => void ;
@@ -12,26 +13,29 @@ export interface QueryParamStoreOptions<T> {
12
13
replaceState ?: boolean ;
13
14
persist ?: 'localStorage' | 'sessionStorage' ;
14
15
storagePrefix ?: string ;
16
+ cleanFalseValues ?: boolean ;
15
17
log ?: boolean ;
16
18
}
17
19
18
- const stringify = ( value ) => {
19
- if ( typeof value === 'undefined' || value === null ) return undefined ;
20
+ function stringify ( value , cleanFalseValues : boolean ) : string | undefined {
21
+ if ( typeof value === 'undefined' || value === null || value === '' ) return undefined ;
20
22
if ( typeof value === 'string' ) return value ;
21
- return JSON . stringify ( value ) ;
22
- } ;
23
23
24
- const parse = ( value : string ) => {
24
+ const cleanedValue = cleanObject ( value , cleanFalseValues ) ;
25
+ return cleanedValue === undefined ? undefined : JSON . stringify ( cleanedValue ) ;
26
+ }
27
+
28
+ function parse ( value : string ) {
25
29
if ( typeof value === 'undefined' ) return undefined ;
26
30
try {
27
31
return JSON . parse ( value ) ;
28
32
} catch {
29
33
return value ; // if the original input was just a string (and never JSON stringified), it will throw an error so just return the string
30
34
}
31
- } ;
35
+ }
32
36
33
37
export function createQueryParamStore < T > ( opts : QueryParamStoreOptions < T > ) {
34
- const { key, log, persist } = opts ;
38
+ const { key, log, persist, startWith , cleanFalseValues } = opts ;
35
39
const replaceState = typeof opts . replaceState === 'undefined' ? true : opts . replaceState ;
36
40
const storageKey = `${ opts . storagePrefix || '' } ${ key } `
37
41
@@ -45,10 +49,11 @@ export function createQueryParamStore<T>(opts: QueryParamStoreOptions<T>) {
45
49
46
50
const setQueryParam = ( value ) => {
47
51
if ( typeof window === 'undefined' ) return ; // safety check in case store value is assigned via $: call server side
48
- if ( value === undefined || value === null ) return removeQueryParam ( ) ;
52
+ const stringified_value = stringify ( value , cleanFalseValues ) ;
53
+ if ( stringified_value === undefined ) return removeQueryParam ( ) ;
49
54
const { hash} = window . location
50
55
const searchParams = new URLSearchParams ( window . location . search )
51
- searchParams . set ( key , stringify ( value ) ) ;
56
+ searchParams . set ( key , stringify ( value , cleanFalseValues ) ) ;
52
57
goto ( `?${ searchParams } ${ hash } ` , { keepFocus : true , noScroll : true , replaceState } ) ;
53
58
if ( log ) console . info ( `user action changed: ${ key } to ${ value } ` ) ;
54
59
} ;
@@ -69,9 +74,11 @@ export function createQueryParamStore<T>(opts: QueryParamStoreOptions<T>) {
69
74
} ;
70
75
71
76
const setStoreValue = ( value : string ) => {
72
- const parsed_value = parse ( value ) as T ;
77
+ if ( log ) console . info ( `URL set ${ key } to ${ value } ` ) ;
78
+ let parsed_value = parse ( value ) as T ;
79
+ if ( ! parsed_value && typeof startWith === 'object' )
80
+ parsed_value = { } as T ;
73
81
set ( parsed_value ) ;
74
- if ( log ) console . info ( `URL set ${ key } to ${ parsed_value } ` ) ;
75
82
storage ?. setItem ( storageKey , JSON . stringify ( parsed_value ) ) ;
76
83
if ( log && storage ) console . info ( { [ storageKey + '_to_cache' ] : parsed_value } ) ;
77
84
} ;
@@ -104,7 +111,7 @@ export function createQueryParamStore<T>(opts: QueryParamStoreOptions<T>) {
104
111
} ;
105
112
106
113
// 3rd Priority: use startWith if no query param in url nor storage value found
107
- const store = writable < T > ( opts . startWith , start ) ;
114
+ const store = writable < T > ( startWith , start ) ;
108
115
const { subscribe, set } = store ;
109
116
110
117
return {
0 commit comments