From fb63ced7371565502add354d2cc610451a7a98ac Mon Sep 17 00:00:00 2001 From: qwacko Date: Fri, 15 Sep 2023 08:56:11 +0000 Subject: [PATCH 1/3] First Update --- src/lib/skRoutes.ts | 76 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/src/lib/skRoutes.ts b/src/lib/skRoutes.ts index e5991da..a62ff2e 100644 --- a/src/lib/skRoutes.ts +++ b/src/lib/skRoutes.ts @@ -1,5 +1,7 @@ import { isArray, merge, mergeWith } from 'lodash-es'; import { getUrlParams, objectToSearchParams } from './helpers.js'; +import type { Readable } from 'svelte/store'; +import { writable, get } from 'svelte/store'; // Define the types for the route configuration type ValidationFunction = (input: T) => T; @@ -145,6 +147,78 @@ export function skRoutes({ } }; + const pageInfoStore = < + Address extends keyof Config, + PageInfo extends { params: Record; url: { search: string } } + >({ + routeId, + pageInfo, + updateDelay, + onUpdate + }: { + routeId: Address; + pageInfo: Readable; + updateDelay?: number; + onUpdate: (newUrl: string) => undefined; + }) => { + // Initial values + let timeoutId: NodeJS.Timeout | null = null; + const initialData = get(pageInfo); + //@ts-expect-error This has uncertainty about what should be available + const initialURLData = urlGenerator({ + address: routeId, + paramsValue: initialData.params, + searchParamsValue: getUrlParams(initialData.url.search) + }); + + // Subscribe to pageInfo changes to reinitialize the user store + const unsubscribeFromPageInfo = pageInfo.subscribe((data) => { + //@ts-expect-error This has uncertainty about what should be available + const current = urlGenerator({ + address: routeId, + paramsValue: data.params, + searchParamsValue: getUrlParams(data.url.search) + }); + + store.set({ + params: current.params, + searchParams: current.searchParams + }); + }); + + const store = writable( + { + params: initialURLData.params, + searchParams: initialURLData.searchParams + }, + () => { + const unsubscribeFromStore = store.subscribe(({ params, searchParams }) => { + if (timeoutId) { + clearTimeout(timeoutId); + } + + timeoutId = setTimeout(() => { + //@ts-expect-error This has uncertainty about what should be available + const generatedUrl = urlGenerator({ + address: routeId, + paramsValue: params, + searchParamsValue: searchParams + }); + + onUpdate(generatedUrl.url); + }, updateDelay); // 1 second delay, adjust as needed + }); + + return () => { + unsubscribeFromStore(); + unsubscribeFromPageInfo(); + }; + } + ); + + return store; + }; + const pageInfo = < Address extends keyof Config, PageInfo extends { params: Record; url: { search: string } } @@ -229,5 +303,5 @@ export function skRoutes({ return { current, updateParams }; }; - return { urlGenerator, pageInfo, serverPageInfo }; + return { urlGenerator, pageInfo, serverPageInfo, pageInfoStore }; } From 33ab84fe786cf2ad964784f71dcdc4715b04b385 Mon Sep 17 00:00:00 2001 From: qwacko Date: Fri, 15 Sep 2023 09:39:31 +0000 Subject: [PATCH 2/3] Made Functionality Work --- README.md | 3 +- package.json | 2 +- src/lib/skRoutes.ts | 83 +++++++++++++++--------------- src/routes/routeConfig.ts | 14 ++++- src/routes/store/[id]/+page.svelte | 39 ++++++++++++++ 5 files changed, 97 insertions(+), 44 deletions(-) create mode 100644 src/routes/store/[id]/+page.svelte diff --git a/README.md b/README.md index 5f76721..88cce35 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ pnpm add skroutes - Validation of route parameters and search parameters. - Fully typed params and search params for use throughout the application. - Use of nested search pararms. -- Typescript validation of URL addresses (changing URLs will cause typescript errors) +- Typescript validation of URL addresses (changing URLs will cause typescript errors). +- Ability to access params and searchParams as a store, with automatic navigation (with debouncing) ## Usage diff --git a/package.json b/package.json index ba8cf61..1d0c29f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "skroutes", - "version": "0.0.8", + "version": "0.0.9", "scripts": { "dev": "vite dev", "build": "vite build && npm run package", diff --git a/src/lib/skRoutes.ts b/src/lib/skRoutes.ts index a62ff2e..bdf5cda 100644 --- a/src/lib/skRoutes.ts +++ b/src/lib/skRoutes.ts @@ -153,15 +153,14 @@ export function skRoutes({ >({ routeId, pageInfo, - updateDelay, + updateDelay = 1000, // Default to 1 second if not provided onUpdate }: { routeId: Address; pageInfo: Readable; updateDelay?: number; - onUpdate: (newUrl: string) => undefined; + onUpdate: (newUrl: string) => unknown; }) => { - // Initial values let timeoutId: NodeJS.Timeout | null = null; const initialData = get(pageInfo); //@ts-expect-error This has uncertainty about what should be available @@ -171,50 +170,52 @@ export function skRoutes({ searchParamsValue: getUrlParams(initialData.url.search) }); - // Subscribe to pageInfo changes to reinitialize the user store - const unsubscribeFromPageInfo = pageInfo.subscribe((data) => { - //@ts-expect-error This has uncertainty about what should be available - const current = urlGenerator({ - address: routeId, - paramsValue: data.params, - searchParamsValue: getUrlParams(data.url.search) - }); - - store.set({ - params: current.params, - searchParams: current.searchParams - }); + const store = writable({ + params: initialURLData.params, + searchParams: initialURLData.searchParams }); - const store = writable( - { - params: initialURLData.params, - searchParams: initialURLData.searchParams - }, - () => { - const unsubscribeFromStore = store.subscribe(({ params, searchParams }) => { - if (timeoutId) { - clearTimeout(timeoutId); - } + const originalSubscribe = store.subscribe; - timeoutId = setTimeout(() => { - //@ts-expect-error This has uncertainty about what should be available - const generatedUrl = urlGenerator({ - address: routeId, - paramsValue: params, - searchParamsValue: searchParams - }); + store.subscribe = (run, invalidate) => { + const unsubscribeFromPageInfo = pageInfo.subscribe((data) => { + //@ts-expect-error This has uncertainty about what should be available + const current = urlGenerator({ + address: routeId, + paramsValue: data.params, + searchParamsValue: getUrlParams(data.url.search) + }); - onUpdate(generatedUrl.url); - }, updateDelay); // 1 second delay, adjust as needed + store.set({ + params: current.params, + searchParams: current.searchParams }); + }); - return () => { - unsubscribeFromStore(); - unsubscribeFromPageInfo(); - }; - } - ); + const unsubscribeFromStore = originalSubscribe((updatedData) => { + run(updatedData); // Call the original subscriber + + if (timeoutId) { + clearTimeout(timeoutId); + } + + timeoutId = setTimeout(() => { + //@ts-expect-error This has uncertainty about what should be available + const generatedUrl = urlGenerator({ + address: routeId, + paramsValue: updatedData.params, + searchParamsValue: updatedData.searchParams + }); + + onUpdate(generatedUrl.url); + }, updateDelay); + }, invalidate); + + return () => { + unsubscribeFromStore(); + unsubscribeFromPageInfo(); + }; + }; return store; }; diff --git a/src/routes/routeConfig.ts b/src/routes/routeConfig.ts index 6caec61..dfe3c09 100644 --- a/src/routes/routeConfig.ts +++ b/src/routes/routeConfig.ts @@ -1,7 +1,7 @@ import { skRoutes } from '$lib/skRoutes.js'; import { z } from 'zod'; -export const { pageInfo, urlGenerator, serverPageInfo } = skRoutes({ +export const { pageInfo, urlGenerator, serverPageInfo, pageInfoStore } = skRoutes({ config: { '/[id]': { paramsValidation: z.object({ id: z.string() }).parse @@ -9,6 +9,18 @@ export const { pageInfo, urlGenerator, serverPageInfo } = skRoutes({ '/server/[id]': { paramsValidation: z.object({ id: z.string() }).parse, searchParamsValidation: z.object({ data: z.string().optional() }).parse + }, + '/store/[id]/': { + paramsValidation: z.object({ id: z.string() }).parse, + searchParamsValidation: z + .object({ + topLevel: z.string().optional(), + nested: z + .object({ item1: z.string().optional(), item2: z.string().optional() }) + .optional() + }) + .optional() + .catch({}).parse } }, errorURL: '/error' diff --git a/src/routes/store/[id]/+page.svelte b/src/routes/store/[id]/+page.svelte new file mode 100644 index 0000000..2b29929 --- /dev/null +++ b/src/routes/store/[id]/+page.svelte @@ -0,0 +1,39 @@ + + +{#if $pageStore.params} + + +{/if} + +{#if $pageStore.searchParams} + + { + if (newValue.target) { + const newString = newValue.target.value; + console.log({ newString }); + const newObject = set($pageStore, 'searchParams.nested.item1', newString); + $pageStore = newObject; + } + }} + /> +{/if} + +
{JSON.stringify($pageStore, null, 2)}
From a3606c46a3730a38ccea8394253aceea61eaa315 Mon Sep 17 00:00:00 2001 From: qwacko Date: Fri, 15 Sep 2023 09:44:26 +0000 Subject: [PATCH 3/3] Fixed Type Issue --- src/routes/store/[id]/+page.svelte | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/routes/store/[id]/+page.svelte b/src/routes/store/[id]/+page.svelte index 2b29929..bf856a1 100644 --- a/src/routes/store/[id]/+page.svelte +++ b/src/routes/store/[id]/+page.svelte @@ -26,9 +26,8 @@ type="string" value={$pageStore.searchParams?.nested?.item1} on:input={(newValue) => { - if (newValue.target) { + if (newValue.target && 'value' in newValue.target) { const newString = newValue.target.value; - console.log({ newString }); const newObject = set($pageStore, 'searchParams.nested.item1', newString); $pageStore = newObject; }