Skip to content

Commit 1a1842b

Browse files
authored
Merge pull request #10 from Ernxst/feat/array
Array support
2 parents 445085f + b079db4 commit 1a1842b

File tree

20 files changed

+802
-205
lines changed

20 files changed

+802
-205
lines changed

.changeset/spicy-carrots-sleep.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-query-params": patch
3+
---
4+
5+
Add support for multi-value params

README.md

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Svelte Query Params
22

3-
The easiest way to reactively manage query params in Svelte _and_ SvelteKit applications, both on the server and in the browser. Built on Svelte 5 [runes](https://svelte-5-preview.vercel.app/docs/runes) and integrates with existing validation libraries to parse, coerce and transform query params into the data your application needs.
3+
The easiest way to reactively manage query params in Svelte _and_ SvelteKit applications, both on the server and in the browser. Built for Svelte 5 and integrates with existing validation libraries to parse, coerce and transform query params into the data your application needs.
44

55
## Installation
66

7-
Since Svelte Query Params uses runes, [`svelte^5`](https://svelte-5-preview.vercel.app/docs/introduction) is required:
7+
[`svelte^5`](https://svelte-5-preview.vercel.app/docs/introduction) is required:
88

99
```bash
1010
npm install svelte-query-params svelte@next
@@ -26,7 +26,7 @@ By default, `svelte-query-params` uses [`URLSearchParams`](https://developer.moz
2626

2727
## Features
2828

29-
- **Reactivity**: The library leverages Svelte's new runes reactivity system, providing a reactive object that reflects the current state of query parameters.
29+
- **Reactivity**: The library providies a reactive object that reflects the current state of query parameters.
3030

3131
- **Browser and Server Support**: The utility is designed to work seamlessly in both browser and server environments.
3232

@@ -36,6 +36,10 @@ By default, `svelte-query-params` uses [`URLSearchParams`](https://developer.moz
3636

3737
- **Event Handling**: Automatically handles `popstate` events for accurate synchronisation with browser history.
3838

39+
- **Serialisation**: Control how query params are serialised into strings to the browser
40+
41+
- **Multi-value params**: Supports multi-value query parameters with ease
42+
3943
## Usage
4044

4145
In some lib file e.g., `src/lib/params.ts`:
@@ -141,6 +145,47 @@ const useQueryParams = createUseQueryParams({
141145
});
142146
```
143147

148+
### Array Values
149+
150+
With a function validator, you may receive the param as either a string, an array of strings, or undefined. As a result, you must handle all three cases to support multi-value params:
151+
152+
```javascript
153+
const validators = {
154+
categories: (value) => {
155+
if (!value) return []
156+
return Array.isArray(value) ? value : [value]
157+
}
158+
}
159+
```
160+
161+
With Zod, you need to handle the case where there's either 0 or 1 query param value as this library will not infer this as an array beforehand. You must define your array parameter like:
162+
163+
```javascript
164+
import { z } from "zod";
165+
166+
z.object({
167+
categories: z
168+
.union([z.string().array(), z.string()])
169+
.default([])
170+
.transform((c) => (Array.isArray(c) ? c : [c])),
171+
})
172+
```
173+
174+
The union between a string and array of strings handles 1 or more query params; a default is set to the empty array to allow the parameter to be omitted from the URL and it's transformed at the end to convert the single value param into an array.
175+
176+
In the same manner, with Valibot:
177+
178+
```javascript
179+
import * as v from "valibot";
180+
181+
v.object({
182+
categories: v.pipe(
183+
v.optional(v.union([v.array(v.string()), v.string()]), []),
184+
v.transform((c) => (Array.isArray(c) ? c : [c]))
185+
),
186+
});
187+
```
188+
144189
## Options
145190

146191
`createUseQueryParams` takes an options object as the second argument, with the following properties:

examples/sveltekit/src/routes/multiselect/+page.svelte

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,35 @@
22
import { page } from "$app/stores";
33
import { useMultiSelectFilters } from "$lib/hooks/multi-select";
44
5-
const [params, helpers] = useMultiSelectFilters($page.url);
5+
const [q, helpers] = useMultiSelectFilters($page.url);
6+
const CATEGORIES = ["books", "electronics", "toys"];
67
78
function updateCategories(category: string) {
8-
const categories = params.categories.includes(category)
9-
? params.categories.filter((c) => c !== category)
10-
: [...params.categories, category];
9+
const categories = q.categories.includes(category)
10+
? q.categories.filter((c) => c !== category)
11+
: [...q.categories, category];
1112
helpers.update({ categories });
1213
}
1314
</script>
1415

1516
<ul>
16-
<li>
17-
<label>
18-
<input
19-
type="checkbox"
20-
value="books"
21-
onchange={() => updateCategories("books")}
22-
checked={params.categories.includes("books")}
23-
/>
24-
Books
25-
</label>
26-
</li>
27-
<li>
28-
<label>
29-
<input
30-
type="checkbox"
31-
value="electronics"
32-
onchange={() => updateCategories("electronics")}
33-
checked={params.categories.includes("electronics")}
34-
/>
35-
Electronics
36-
</label>
37-
</li>
17+
{#each CATEGORIES as category}
18+
<li>
19+
<label>
20+
<input
21+
type="checkbox"
22+
value={category}
23+
onchange={() => updateCategories(category)}
24+
checked={q.categories.includes(category)}
25+
/>
26+
{category}
27+
</label>
28+
</li>
29+
{/each}
30+
</ul>
31+
32+
<ul>
33+
{#each q.categories as category}
34+
<li>{category}</li>
35+
{/each}
3836
</ul>

packages/core/README.md

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Svelte Query Params
22

3-
The easiest way to reactively manage query params in Svelte _and_ SvelteKit applications, both on the server and in the browser. Built on Svelte 5 [runes](https://svelte-5-preview.vercel.app/docs/runes) and integrates with existing validation libraries to parse, coerce and transform query params into the data your application needs.
3+
The easiest way to reactively manage query params in Svelte _and_ SvelteKit applications, both on the server and in the browser. Built for Svelte 5 and integrates with existing validation libraries to parse, coerce and transform query params into the data your application needs.
44

55
## Installation
66

7-
Since Svelte Query Params uses runes, [`svelte^5`](https://svelte-5-preview.vercel.app/docs/introduction) is required:
7+
[`svelte^5`](https://svelte-5-preview.vercel.app/docs/introduction) is required:
88

99
```bash
1010
npm install svelte-query-params svelte@next
@@ -26,7 +26,7 @@ By default, `svelte-query-params` uses [`URLSearchParams`](https://developer.moz
2626

2727
## Features
2828

29-
- **Reactivity**: The library leverages Svelte's new runes reactivity system, providing a reactive object that reflects the current state of query parameters.
29+
- **Reactivity**: The library providies a reactive object that reflects the current state of query parameters.
3030

3131
- **Browser and Server Support**: The utility is designed to work seamlessly in both browser and server environments.
3232

@@ -36,6 +36,10 @@ By default, `svelte-query-params` uses [`URLSearchParams`](https://developer.moz
3636

3737
- **Event Handling**: Automatically handles `popstate` events for accurate synchronisation with browser history.
3838

39+
- **Serialisation**: Control how query params are serialised into strings to the browser
40+
41+
- **Multi-value params**: Supports multi-value query parameters with ease
42+
3943
## Usage
4044

4145
In some lib file e.g., `src/lib/params.ts`:
@@ -141,6 +145,47 @@ const useQueryParams = createUseQueryParams({
141145
});
142146
```
143147

148+
### Array Values
149+
150+
With a function validator, you may receive the param as either a string, an array of strings, or undefined. As a result, you must handle all three cases to support multi-value params:
151+
152+
```javascript
153+
const validators = {
154+
categories: (value) => {
155+
if (!value) return []
156+
return Array.isArray(value) ? value : [value]
157+
}
158+
}
159+
```
160+
161+
With Zod, you need to handle the case where there's either 0 or 1 query param value as this library will not infer this as an array beforehand. You must define your array parameter like:
162+
163+
```javascript
164+
import { z } from "zod";
165+
166+
z.object({
167+
categories: z
168+
.union([z.string().array(), z.string()])
169+
.default([])
170+
.transform((c) => (Array.isArray(c) ? c : [c])),
171+
})
172+
```
173+
174+
The union between a string and array of strings handles 1 or more query params; a default is set to the empty array to allow the parameter to be omitted from the URL and it's transformed at the end to convert the single value param into an array.
175+
176+
In the same manner, with Valibot:
177+
178+
```javascript
179+
import * as v from "valibot";
180+
181+
v.object({
182+
categories: v.pipe(
183+
v.optional(v.union([v.array(v.string()), v.string()]), []),
184+
v.transform((c) => (Array.isArray(c) ? c : [c]))
185+
),
186+
});
187+
```
188+
144189
## Options
145190

146191
`createUseQueryParams` takes an options object as the second argument, with the following properties:

packages/core/package.json

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,23 @@
1616
},
1717
"keywords": [
1818
"reactive",
19+
"search",
1920
"search-params",
2021
"search params",
2122
"search-parameters",
2223
"search parameters",
2324
"query",
25+
"querystring",
2426
"query parameters",
2527
"query-parameters",
2628
"query params",
2729
"query-params",
2830
"svelte",
31+
"sveltejs",
2932
"sveltekit",
30-
"runes"
33+
"ssr",
34+
"browser",
35+
"url"
3136
],
3237
"sideEffects": false,
3338
"publishConfig": {
@@ -83,10 +88,7 @@
8388
"module": "dist/index.svelte.js",
8489
"svelte": "dist/index.svelte.js",
8590
"types": "dist/index.svelte.d.ts",
86-
"files": [
87-
"dist",
88-
"README.md"
89-
],
91+
"files": ["dist", "README.md"],
9092
"engines": {
9193
"node": ">=v20.0.0"
9294
},

packages/core/playwright.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ function playwrightDir(partialPath: string) {
88
const CI = !!process.env.CI;
99

1010
export default defineConfig({
11+
expect: {
12+
timeout: CI ? 10000 : 2000,
13+
},
1114
testDir: playwrightDir("specs"),
1215
outputDir: playwrightDir("results"),
1316
webServer: {

packages/core/src/lib/__test__/params.test-d.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ describe("Type tests", () => {
2222
{
2323
name: "function validators",
2424
schema: {
25-
id: (value: string | undefined) => Number(value),
26-
q: (value: string | undefined) => value,
25+
id: (value: string | string[] | undefined) => Number(value),
26+
q: (value: string | string[] | undefined) => value,
2727
},
2828
},
2929
{
30-
name: "mixes and matched",
30+
name: "mix and match",
3131
schema: {
3232
id: z.number(),
33-
q: (value: string | undefined) => value,
33+
q: (value: string | string[] | undefined) => value,
3434
},
3535
},
3636
];

0 commit comments

Comments
 (0)