From 8e2bdac4c2441c5126202d1638df7926a8fb1c78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ramirez=20Vargas=2C=20Jos=C3=A9=20Pablo?= Date: Sun, 31 Aug 2025 01:06:52 -0600 Subject: [PATCH 1/5] feat: Major navigation improvements Fixes #25. LOC delta: +166 --- README.md | 99 ++++++-- src/lib/Link/Link.svelte | 57 ++--- src/lib/Link/README.md | 2 +- src/lib/LinkContext/LinkContext.svelte | 3 +- src/lib/LinkContext/README.md | 2 +- src/lib/core/Location.ts | 9 +- src/lib/core/LocationLite.svelte.test.ts | 131 ++++++++--- src/lib/core/LocationLite.svelte.ts | 106 +++++---- src/lib/core/LocationState.svelte.test.ts | 267 ++++++++++++++++++++++ src/lib/core/LocationState.svelte.ts | 13 +- src/lib/core/RouterEngine.svelte.ts | 13 +- src/lib/core/calculateHref.test.ts | 174 ++++++++++++++ src/lib/core/calculateHref.ts | 84 +++++++ src/lib/core/calculateState.svelte.ts | 35 +++ src/lib/core/calculateState.test.ts | 213 +++++++++++++++++ src/lib/core/dissectHrefs.test.ts | 92 ++++++++ src/lib/core/dissectHrefs.ts | 34 +++ src/lib/core/index.test.ts | 3 + src/lib/core/index.ts | 3 + src/lib/core/preserveQuery.test.ts | 74 ++++++ src/lib/core/preserveQuery.ts | 77 +++++++ src/lib/index.test.ts | 3 + src/lib/index.ts | 5 +- src/lib/types.ts | 59 ++++- 24 files changed, 1401 insertions(+), 157 deletions(-) create mode 100644 src/lib/core/LocationState.svelte.test.ts create mode 100644 src/lib/core/calculateHref.test.ts create mode 100644 src/lib/core/calculateHref.ts create mode 100644 src/lib/core/calculateState.svelte.ts create mode 100644 src/lib/core/calculateState.test.ts create mode 100644 src/lib/core/dissectHrefs.test.ts create mode 100644 src/lib/core/dissectHrefs.ts create mode 100644 src/lib/core/preserveQuery.test.ts create mode 100644 src/lib/core/preserveQuery.ts diff --git a/README.md b/README.md index f360e20..8908ff5 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ ## Features -> [!NOTE] -> #### Small and Unique! +> **📝 Small and Unique!** > > + Less than **1,050** lines of code, including TypeScript typing. > + Always-on path and hash routing. Simultaneous and independent routing modes. @@ -219,8 +218,7 @@ Nothing prevents you to add transitions to anything. ``` -> [!NOTE] -> This one item might be worthwhile revisiting for the cases where synchronized transitions are desired. This, +> **📝 Note:** This one item might be worthwhile revisiting for the cases where synchronized transitions are desired. This, > however, won't be looked at until Svelte attachments become a thing. ### Guarded Routes @@ -351,26 +349,100 @@ property of router engines (which is reactive) by binding to a router's `router` ## Navigation +> Since **v0.4.0** + +> **💥 BREAKING CHANGE:** Navigation has been re-done in v0.4.0. + The recommended way of navigating is to create `` component instances to render links on the document(s). If -needed, however, there's a programmatic way of navigating: `location.navigate()`. +needed, however, there are 2 navigation functions in the `location` object: `navigate()` and `goTo()`. + +### `navigate(url, options)` - Routing Universe Aware + +This is the preferred method for programmatic navigation as it understands routing universes and properly manages state: ```typescript import { location } from "@wjfe/n-savant"; // Path routing navigation: -location.navigate('/new/path', { replace: true, state: { custom: 'Hi' }}); +location.navigate('/new/path', { + replace: true, + state: { custom: 'Hi' }, + hash: false +}); // Hash routing navigation: -location.navigate('#/new/path', { replace: true, state: { custom: 'Hi' }}); +location.navigate('/new/path', { + replace: true, + state: { custom: 'Hi' }, + hash: true +}); // Multi-hash routing navigation: -location.navigate('/new/path', 'path1', { replace: true, state: { custom: 'Hi' }}); +location.navigate('/new/path', { + replace: true, + state: { custom: 'Hi' }, + hash: 'path1' +}); + +// Preserve existing query parameters: +location.navigate('/new/path', { + preserveQuery: true, + hash: false +}); +``` + +The `navigate()` method automatically: +- Associates state with the correct routing universe based on the `hash` option +- Preserves other routing universe states (e.g., when navigating `path1`, other named paths remain intact) +- Handles URL construction using the robust `calculateHref()` logic + +### `goTo(url, options)` - Direct URL Navigation + +This method provides direct URL navigation without routing universe awareness: + +```typescript +import { location } from "@wjfe/n-savant"; + +// Direct URL navigation: +location.goTo('https://example.com/new/path', { + replace: true, + state: { path: undefined, hash: {} } // Must provide complete State object +}); + +// Shallow routing (navigate to current URL): +location.goTo('', { replace: true }); + +// Preserve query parameters: +location.goTo('/new/path', { + preserveQuery: ['param1', 'param2'] +}); ``` -Navigation in multi-hash scenarios is tricky: One must make sure other paths remain untouched, and the information -about these other paths is stored in `location.hashPaths`. You could use the second form above for multi-hash routing -as long as you understand that it is your responsibility to (possibly) ensure the integrity of other paths defined in -the URL's hash value. +**⚠️ Important:** `goTo()` requires you to provide a complete `State` object and does not understand routing universes. Use `navigate()` unless you specifically need direct URL control. + +### Options Reference + +Both methods support these common options: + +- **`replace?: boolean`** - Replace current URL instead of pushing new entry (default: `false`) +- **`preserveQuery?: PreserveQuery`** - Preserve current query parameters + - `true` - Preserve all query parameters + - `string` - Preserve specific parameter by name + - `string[]` - Preserve multiple specific parameters + +Additional `navigate()` options: +- **`hash?: Hash`** - Routing universe to associate with (`false`, `true`, or named hash) +- **`state?: any`** - State data to associate with the navigation + +Additional `goTo()` options: +- **`state?: State`** - Complete state object conforming to library expectations + +### Navigation Best Practices + +1. **Use `` components** for user-triggered navigation +2. **Use `navigate()`** for programmatic navigation within routing universes +3. **Use `goTo()`** only for direct URL manipulation or external navigation +4. **Always specify `hash`** in `navigate()` to ensure proper state management Just in case you are wondering: The navigation logic is already there in `` components: @@ -386,8 +458,7 @@ Just in case you are wondering: The navigation logic is already there in `Click Me! ``` -> [!IMPORTANT] -> Not setting the `hash` property is **not the same** as setting it to `false`. When `hash` is `undefined`, either +> **⚠️ Important:** Not setting the `hash` property is **not the same** as setting it to `false`. When `hash` is `undefined`, either > because the property is not specified at all, or its value is set to `undefined` explicitly, the value of the > `implicitMode` routing option, which is set when the library is initialized, will be used to resolve a `true` or > `false` value. diff --git a/src/lib/Link/Link.svelte b/src/lib/Link/Link.svelte index 202dd5e..0bc12c6 100644 --- a/src/lib/Link/Link.svelte +++ b/src/lib/Link/Link.svelte @@ -1,6 +1,8 @@