navgo 3.0.6 → 3.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +10 -0
- package/index.js +20 -6
- package/package.json +5 -2
- package/readme.md +42 -20
package/index.d.ts
CHANGED
|
@@ -75,6 +75,8 @@ export interface Options {
|
|
|
75
75
|
preload_delay?: number
|
|
76
76
|
/** Disable hover/touch preloading when `false`. Default true. */
|
|
77
77
|
preload_on_hover?: boolean
|
|
78
|
+
/** Attach instance to window as `window.navgo`. Default true. */
|
|
79
|
+
attach_to_window?: boolean
|
|
78
80
|
/** Global hook fired after per-route `before_route_leave`, before loaders/history change. Can cancel. */
|
|
79
81
|
before_navigate?(_nav: Navigation): void
|
|
80
82
|
/** Global hook fired after routing completes (data loaded, URL updated, handlers run). */
|
|
@@ -110,6 +112,14 @@ export default class Navgo<T = unknown> {
|
|
|
110
112
|
init(): Promise<void>
|
|
111
113
|
/** Remove listeners installed by `init()`. */
|
|
112
114
|
destroy(): void
|
|
115
|
+
/** Writable store with current { url, route, params }. */
|
|
116
|
+
readonly route: import('svelte/store').Writable<{
|
|
117
|
+
url: URL
|
|
118
|
+
route: RouteTuple<T> | null
|
|
119
|
+
params: Params
|
|
120
|
+
}>
|
|
121
|
+
/** Writable store indicating active navigation. */
|
|
122
|
+
readonly is_navigating: import('svelte/store').Writable<boolean>
|
|
113
123
|
/** Built-in validator helpers (namespaced). */
|
|
114
124
|
static validators: ValidatorHelpers
|
|
115
125
|
}
|
package/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { parse } from 'regexparam'
|
|
2
|
+
import { tick } from 'svelte'
|
|
3
|
+
import { writable } from 'svelte/store'
|
|
2
4
|
|
|
3
5
|
const ℹ = (...args) => {}
|
|
4
6
|
|
|
@@ -11,7 +13,8 @@ export default class Navgo {
|
|
|
11
13
|
before_navigate: undefined,
|
|
12
14
|
after_navigate: undefined,
|
|
13
15
|
url_changed: undefined,
|
|
14
|
-
tick
|
|
16
|
+
tick,
|
|
17
|
+
attach_to_window: true,
|
|
15
18
|
}
|
|
16
19
|
/** @type {Array<{ pattern: RegExp, keys: string[]|null, data: RouteTuple }>} */
|
|
17
20
|
#routes = []
|
|
@@ -36,6 +39,8 @@ export default class Navgo {
|
|
|
36
39
|
#nav_active = 0
|
|
37
40
|
/** @type {(e: Event) => void | null} */
|
|
38
41
|
#scroll_handler = null
|
|
42
|
+
route = writable({ url: new URL(location.href), route: null, params: {} })
|
|
43
|
+
is_navigating = writable(false)
|
|
39
44
|
|
|
40
45
|
//
|
|
41
46
|
// Event listeners
|
|
@@ -99,7 +104,7 @@ export default class Navgo {
|
|
|
99
104
|
this.#current.url = target
|
|
100
105
|
ℹ(' - [🧭 event:popstate]', 'same path+search; skip loaders')
|
|
101
106
|
this.#apply_scroll(ev)
|
|
102
|
-
this
|
|
107
|
+
this.route.set(this.#current)
|
|
103
108
|
return
|
|
104
109
|
}
|
|
105
110
|
// Explicit shallow entries (pushState/replaceState) regardless of path
|
|
@@ -107,7 +112,7 @@ export default class Navgo {
|
|
|
107
112
|
this.#current.url = target
|
|
108
113
|
ℹ(' - [🧭 event:popstate]', 'shallow entry; skip loaders')
|
|
109
114
|
this.#apply_scroll(ev)
|
|
110
|
-
this
|
|
115
|
+
this.route.set(this.#current)
|
|
111
116
|
return
|
|
112
117
|
}
|
|
113
118
|
|
|
@@ -142,7 +147,7 @@ export default class Navgo {
|
|
|
142
147
|
}
|
|
143
148
|
// update current URL snapshot and notify
|
|
144
149
|
this.#current.url = new URL(location.href)
|
|
145
|
-
this
|
|
150
|
+
this.route.set(this.#current)
|
|
146
151
|
}
|
|
147
152
|
|
|
148
153
|
/** @type {any} */
|
|
@@ -296,6 +301,7 @@ export default class Navgo {
|
|
|
296
301
|
ℹ('[🧭 goto]', 'invalid url', { url: url_raw })
|
|
297
302
|
return
|
|
298
303
|
}
|
|
304
|
+
this.is_navigating.set(true)
|
|
299
305
|
const { url, path } = info
|
|
300
306
|
|
|
301
307
|
const is_popstate = nav_type === 'popstate'
|
|
@@ -325,6 +331,7 @@ export default class Navgo {
|
|
|
325
331
|
}
|
|
326
332
|
}
|
|
327
333
|
}
|
|
334
|
+
if (nav_id === this.#nav_active) this.is_navigating.set(false)
|
|
328
335
|
ℹ('[🧭 goto]', 'cancelled by before_route_leave')
|
|
329
336
|
return
|
|
330
337
|
}
|
|
@@ -417,7 +424,8 @@ export default class Navgo {
|
|
|
417
424
|
await this.#opts.tick?.()
|
|
418
425
|
|
|
419
426
|
this.#apply_scroll(nav)
|
|
420
|
-
this
|
|
427
|
+
this.route.set(this.#current)
|
|
428
|
+
if (nav_id === this.#nav_active) this.is_navigating.set(false)
|
|
421
429
|
}
|
|
422
430
|
|
|
423
431
|
/**
|
|
@@ -455,7 +463,7 @@ export default class Navgo {
|
|
|
455
463
|
if (!replace) this.#clear_onward_history()
|
|
456
464
|
// update current URL snapshot and notify
|
|
457
465
|
this.#current.url = u
|
|
458
|
-
this
|
|
466
|
+
this.route.set(this.#current)
|
|
459
467
|
}
|
|
460
468
|
|
|
461
469
|
/** @param {string|URL} [url] @param {any} [state] */
|
|
@@ -563,6 +571,11 @@ export default class Navgo {
|
|
|
563
571
|
return pat
|
|
564
572
|
})
|
|
565
573
|
|
|
574
|
+
// TODO: deprecated, remove later
|
|
575
|
+
this.route.subscribe(() => {
|
|
576
|
+
this.#opts.url_changed?.(this.#current)
|
|
577
|
+
})
|
|
578
|
+
|
|
566
579
|
ℹ('[🧭 init]', {
|
|
567
580
|
base: this.#base,
|
|
568
581
|
routes: this.#routes.length,
|
|
@@ -608,6 +621,7 @@ export default class Navgo {
|
|
|
608
621
|
}
|
|
609
622
|
|
|
610
623
|
ℹ('[🧭 init]', 'initial goto')
|
|
624
|
+
if (this.#opts.attach_to_window) window.navgo = this
|
|
611
625
|
return this.goto()
|
|
612
626
|
}
|
|
613
627
|
destroy() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "navgo",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.8",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/mustafa0x/navgo.git"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "perl -0777 -i -pe 's/console.debug\\(...args\\)/{}/g' index.js",
|
|
19
|
-
"prepublishOnly": "pnpm
|
|
19
|
+
"prepublishOnly": "pnpm run build",
|
|
20
20
|
"test": "vitest run index.test.js",
|
|
21
21
|
"test:e2e": "playwright test",
|
|
22
22
|
"start:testsite": "pnpm vite dev test/site --port 5714",
|
|
@@ -34,6 +34,9 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"regexparam": "^3.0.0"
|
|
36
36
|
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"svelte": "5"
|
|
39
|
+
},
|
|
37
40
|
"devDependencies": {
|
|
38
41
|
"@eslint/js": "^9.37.0",
|
|
39
42
|
"@playwright/test": "^1.56.0",
|
package/readme.md
CHANGED
|
@@ -103,16 +103,38 @@ Notes:
|
|
|
103
103
|
- `tick`: `() => void | Promise<void>`
|
|
104
104
|
- Awaited after `after_navigate` and before scroll handling; useful for frameworks to flush DOM so anchor/top scrolling lands correctly.
|
|
105
105
|
- `url_changed`: `(snapshot: any) => void`
|
|
106
|
-
- Fires on every URL change
|
|
106
|
+
- Fires on every URL change -- shallow `push_state`/`replace_state`, hash changes, `popstate` shallow entries, 404s, and full navigations. (deprecated; subscribe to `.route` instead.)
|
|
107
107
|
- Receives the router's current snapshot: an object like `{ url: URL, route: RouteTuple|null, params: Params }`.
|
|
108
108
|
- The snapshot type is intentionally `any` and may evolve without a breaking change.
|
|
109
109
|
- `preload_delay`: `number` (default `20`)
|
|
110
110
|
- Delay in ms before hover preloading triggers.
|
|
111
111
|
- `preload_on_hover`: `boolean` (default `true`)
|
|
112
112
|
- When `false`, disables hover/touch preloading.
|
|
113
|
+
- `attach_to_window`: `boolean` (default `true`)
|
|
114
|
+
- When `true`, `init()` attaches the instance to `window.navgo` for convenience.
|
|
113
115
|
|
|
114
116
|
Important: Navgo only processes routes that match your `base` path.
|
|
115
117
|
|
|
118
|
+
### Instance stores
|
|
119
|
+
|
|
120
|
+
- `router.route` -- `Writable<{ url: URL; route: RouteTuple|null; params: Params }>`
|
|
121
|
+
- Readonly property that holds the current snapshot.
|
|
122
|
+
- Subscribe to react to changes; Navgo updates it on every URL change.
|
|
123
|
+
- `router.is_navigating` -- `Writable<boolean>`
|
|
124
|
+
- `true` while a navigation is in flight (between start and completion/cancel).
|
|
125
|
+
|
|
126
|
+
Example:
|
|
127
|
+
|
|
128
|
+
```svelte
|
|
129
|
+
Current path: {$route.path}
|
|
130
|
+
<div class="request-indicator" class:active={$is_navigating}></div>
|
|
131
|
+
|
|
132
|
+
<script>
|
|
133
|
+
const router = new Navgo(...)
|
|
134
|
+
const {route, is_navigating} = router
|
|
135
|
+
</script>
|
|
136
|
+
```
|
|
137
|
+
|
|
116
138
|
### Route Hooks
|
|
117
139
|
|
|
118
140
|
- param_validators?: `Record<string, (value: string|null|undefined) => boolean>`
|
|
@@ -225,8 +247,8 @@ While listening, link clicks are intercepted and translated into `goto()` naviga
|
|
|
225
247
|
|
|
226
248
|
In addition, `init()` wires preloading listeners (enabled by default) so route data can be fetched early:
|
|
227
249
|
|
|
228
|
-
- `mousemove` (hover)
|
|
229
|
-
- `touchstart` and `mousedown` (tap)
|
|
250
|
+
- `mousemove` (hover) -- after a short delay, hovering an in-app link triggers `preload(href)`.
|
|
251
|
+
- `touchstart` and `mousedown` (tap) -- tapping or pressing on an in-app link also triggers `preload(href)`.
|
|
230
252
|
|
|
231
253
|
Preloading applies only to in-app anchors that match the configured [`base`](#base). You can tweak this behavior with the `preload_delay` and `preload_on_hover` options.
|
|
232
254
|
|
|
@@ -287,10 +309,10 @@ This section explains, in detail, how navigation is processed: matching, hooks,
|
|
|
287
309
|
|
|
288
310
|
### Navigation Types
|
|
289
311
|
|
|
290
|
-
- `link`
|
|
291
|
-
- `goto`
|
|
292
|
-
- `popstate`
|
|
293
|
-
- `leave`
|
|
312
|
+
- `link` -- user clicked an in-app `<a>` that matches `base`.
|
|
313
|
+
- `goto` -- programmatic navigation via `router.goto(...)`.
|
|
314
|
+
- `popstate` -- browser back/forward.
|
|
315
|
+
- `leave` -- page is unloading (refresh, external navigation, tab close) via `beforeunload`.
|
|
294
316
|
|
|
295
317
|
The router passes the type to your route-level `before_route_leave(nav)` hook.
|
|
296
318
|
|
|
@@ -360,19 +382,19 @@ scroll flow
|
|
|
360
382
|
|
|
361
383
|
### Method-by-Method Semantics
|
|
362
384
|
|
|
363
|
-
- `format(uri)`
|
|
364
|
-
- `match(uri)`
|
|
365
|
-
- `goto(uri, { replace? })`
|
|
366
|
-
- `init()`
|
|
367
|
-
- `destroy()`
|
|
368
|
-
- `preload(uri)`
|
|
369
|
-
- `push_state(url?, state?)`
|
|
370
|
-
- `replace_state(url?, state?)`
|
|
385
|
+
- `format(uri)` -- normalizes a path relative to `base`. Returns `false` when `uri` is outside of `base`.
|
|
386
|
+
- `match(uri)` -- returns a Promise of `{ route, params } | null` using string/RegExp patterns and validators. Awaits an async `validate(params)` if provided.
|
|
387
|
+
- `goto(uri, { replace? })` -- fires route-level `before_route_leave('goto')`, calls global `before_navigate`, saves scroll, runs loaders, pushes/replaces, and completes via `after_navigate`.
|
|
388
|
+
- `init()` -- wires global listeners (`popstate`, `pushstate`, `replacestate`, click) and optional hover/tap preloading; immediately processes the current location.
|
|
389
|
+
- `destroy()` -- removes listeners added by `init()`.
|
|
390
|
+
- `preload(uri)` -- pre-executes a route's `loaders` for a path and caches the result; concurrent calls are deduped.
|
|
391
|
+
- `push_state(url?, state?)` -- shallow push that updates the URL and `history.state` without route processing.
|
|
392
|
+
- `replace_state(url?, state?)` -- shallow replace that updates the URL and `history.state` without route processing.
|
|
371
393
|
|
|
372
394
|
### Built-in Validators
|
|
373
395
|
|
|
374
|
-
- `Navgo.validators.int({ min?, max? })`
|
|
375
|
-
- `Navgo.validators.one_of(iterable)`
|
|
396
|
+
- `Navgo.validators.int({ min?, max? })` -- `true` iff the value is an integer within optional bounds.
|
|
397
|
+
- `Navgo.validators.one_of(iterable)` -- `true` iff the value is in the provided set.
|
|
376
398
|
|
|
377
399
|
Attach validators via a route tuple's `data.param_validators` to constrain matches.
|
|
378
400
|
|
|
@@ -380,6 +402,6 @@ Attach validators via a route tuple's `data.param_validators` to constrain match
|
|
|
380
402
|
|
|
381
403
|
This router integrates ideas and small portions of code from these fantastic projects:
|
|
382
404
|
|
|
383
|
-
- SvelteKit
|
|
384
|
-
- navaid
|
|
385
|
-
- TanStack Router
|
|
405
|
+
- SvelteKit -- https://github.com/sveltejs/kit
|
|
406
|
+
- navaid -- https://github.com/lukeed/navaid
|
|
407
|
+
- TanStack Router -- https://github.com/TanStack/router
|