From fcd7cb60b0da8050795cf9732dce32f7e1800207 Mon Sep 17 00:00:00 2001 From: tglide <26071571+TGlide@users.noreply.github.com> Date: Tue, 25 Jun 2024 20:21:51 +0100 Subject: [PATCH 1/2] dumb POC --- .../runed/src/lib/internal/utils/object.ts | 7 +++ .../SearchParams/SearchParams.svelte.ts | 45 +++++++++++++++++++ .../src/lib/utilities/SearchParams/index.ts | 1 + packages/runed/src/lib/utilities/index.ts | 1 + sites/docs/content/utilities/search-params.md | 15 +++++++ .../lib/components/demos/search-params.svelte | 29 ++++++++++++ 6 files changed, 98 insertions(+) create mode 100644 packages/runed/src/lib/internal/utils/object.ts create mode 100644 packages/runed/src/lib/utilities/SearchParams/SearchParams.svelte.ts create mode 100644 packages/runed/src/lib/utilities/SearchParams/index.ts create mode 100644 sites/docs/content/utilities/search-params.md create mode 100644 sites/docs/src/lib/components/demos/search-params.svelte diff --git a/packages/runed/src/lib/internal/utils/object.ts b/packages/runed/src/lib/internal/utils/object.ts new file mode 100644 index 00000000..9e0dc159 --- /dev/null +++ b/packages/runed/src/lib/internal/utils/object.ts @@ -0,0 +1,7 @@ +export function keys(obj: T): (keyof T)[] { + return Object.keys(obj) as (keyof T)[]; +} + +export function entries(obj: T): [keyof T, T[keyof T]][] { + return Object.entries(obj) as [keyof T, T[keyof T]][]; +} diff --git a/packages/runed/src/lib/utilities/SearchParams/SearchParams.svelte.ts b/packages/runed/src/lib/utilities/SearchParams/SearchParams.svelte.ts new file mode 100644 index 00000000..aba155be --- /dev/null +++ b/packages/runed/src/lib/utilities/SearchParams/SearchParams.svelte.ts @@ -0,0 +1,45 @@ +import { Store } from "../Store/Store.svelte.js"; +import { useEventListener } from "../useEventListener/useEventListener.svelte.js"; +import { watch } from "../watch/watch.svelte.js"; +import { page } from "$app/stores"; +import { entries } from "$lib/internal/utils/object.js"; +import { goto } from "$app/navigation"; + +export class SearchParams { + #params: Record = $state({}); + #page = new Store(page); + + // Prohibit using this constructor + private constructor() { + // Two-way sync + watch( + () => this.#page.current.url.href, + () => { + this.#page.current.url.searchParams.forEach((v, k) => { + if (this.#params[k] === v) return; + this.#params[k] = v; + }); + } + ); + + watch( + () => JSON.stringify(this.#params), + () => { + const currentUrl = this.#page.current.url.href.split("?")[0]; + const queryString = entries(this.#params) + .map(([k, v]) => `${k}=${v}`) + .join("&"); + goto(`${currentUrl}?${queryString}`); + } + ); + } + + get params() { + return this.#params; + } + + static all() { + const instance = new SearchParams(); + return instance.params; + } +} diff --git a/packages/runed/src/lib/utilities/SearchParams/index.ts b/packages/runed/src/lib/utilities/SearchParams/index.ts new file mode 100644 index 00000000..ddb89888 --- /dev/null +++ b/packages/runed/src/lib/utilities/SearchParams/index.ts @@ -0,0 +1 @@ +export * from "./SearchParams.svelte.js"; \ No newline at end of file diff --git a/packages/runed/src/lib/utilities/index.ts b/packages/runed/src/lib/utilities/index.ts index ddeeac18..6a5de0b1 100644 --- a/packages/runed/src/lib/utilities/index.ts +++ b/packages/runed/src/lib/utilities/index.ts @@ -18,3 +18,4 @@ export * from "./useMutationObserver/index.js"; export * from "./useResizeObserver/index.js"; export * from "./AnimationFrames/index.js"; export * from "./useIntersectionObserver/index.js"; +export * from "./SearchParams/index.js"; \ No newline at end of file diff --git a/sites/docs/content/utilities/search-params.md b/sites/docs/content/utilities/search-params.md new file mode 100644 index 00000000..4859f582 --- /dev/null +++ b/sites/docs/content/utilities/search-params.md @@ -0,0 +1,15 @@ +--- +title: SearchParams +description: N/A +category: New +--- + + + +## Demo + + + +## Usage diff --git a/sites/docs/src/lib/components/demos/search-params.svelte b/sites/docs/src/lib/components/demos/search-params.svelte new file mode 100644 index 00000000..839c2779 --- /dev/null +++ b/sites/docs/src/lib/components/demos/search-params.svelte @@ -0,0 +1,29 @@ + + + +
{page.current.url.href}
+
{JSON.stringify(params, null, 2)}
+ + +
From 407a2634d94d1149015e74fc82824feae3085aa8 Mon Sep 17 00:00:00 2001 From: tglide <26071571+TGlide@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:16:03 +0100 Subject: [PATCH 2/2] checkpoint --- .../SearchParams/SearchParams.svelte.ts | 32 +++++++++++++++++-- .../lib/components/demos/search-params.svelte | 7 ++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/packages/runed/src/lib/utilities/SearchParams/SearchParams.svelte.ts b/packages/runed/src/lib/utilities/SearchParams/SearchParams.svelte.ts index aba155be..86e0df72 100644 --- a/packages/runed/src/lib/utilities/SearchParams/SearchParams.svelte.ts +++ b/packages/runed/src/lib/utilities/SearchParams/SearchParams.svelte.ts @@ -1,12 +1,21 @@ import { Store } from "../Store/Store.svelte.js"; -import { useEventListener } from "../useEventListener/useEventListener.svelte.js"; import { watch } from "../watch/watch.svelte.js"; import { page } from "$app/stores"; import { entries } from "$lib/internal/utils/object.js"; import { goto } from "$app/navigation"; +export type SearchParamsSingleOptions = { + encode?: (value: T) => string; + decode?: (value: string | null) => T; + defaultValue?: T; +}; + +export type SearchParamsAllOptions = { + [key: string]: SearchParamsSingleOptions; +}; + export class SearchParams { - #params: Record = $state({}); + #params: Record = $state({}); #page = new Store(page); // Prohibit using this constructor @@ -35,6 +44,7 @@ export class SearchParams { } get params() { + // TODO: use dynamic setters and getters instead to increase efficiency? return this.#params; } @@ -42,4 +52,22 @@ export class SearchParams { const instance = new SearchParams(); return instance.params; } + + static single(key: string, options: SearchParamsSingleOptions = {}) { + // TODO: make encode and decode type safe + const instance = new SearchParams(); + const decode = options.decode ?? ((v) => v); + // eslint-disable-next-line ts/no-explicit-any + const encode: (v: any) => string = options.encode ?? ((v: unknown) => `${v}`); + instance.params[key] = encode(options.defaultValue ?? null) + + return { + get current() { + return decode(instance.params[key] ?? null) as T + }, + set current(value: T) { + instance.params[key] = options.encode?.(value) ?? `${value}`; + }, + }; + } } diff --git a/sites/docs/src/lib/components/demos/search-params.svelte b/sites/docs/src/lib/components/demos/search-params.svelte index 839c2779..1f78a386 100644 --- a/sites/docs/src/lib/components/demos/search-params.svelte +++ b/sites/docs/src/lib/components/demos/search-params.svelte @@ -14,16 +14,19 @@ } function setParams() { - console.log(params) params.a = 2; params.b = Math.random(); params.c = Math.random(); } + + const pageParams = $derived(page.current.url.href.split("?")[1]); + const count = SearchParams.single("count", { defaultValue: 0, decode: (v) => Number(v ?? 0) }); -
{page.current.url.href}
+
{JSON.stringify(pageParams, null, 2)}
{JSON.stringify(params, null, 2)}
+