@vctqs1/nav-progress-bar 0.0.2-4 → 0.1.0
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/README.md +70 -26
- package/dist/index.cjs +2 -2
- package/dist/index.js +16 -15
- package/dist/lib/nav-progress-bar.d.ts +3 -1
- package/dist/lib/nav-progress-bar.d.ts.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -4,6 +4,25 @@ A zero-dependency, CSP-safe top-of-page progress bar built as a native Web Compo
|
|
|
4
4
|
|
|
5
5
|
> Originally built to solve the [Next.js App Router `loading.js` dead gap](https://github.com/vercel/next.js/issues/43548), but the underlying mechanism (the browser [Navigation API](https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API)) works anywhere.
|
|
6
6
|
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Features](#features)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Quick Start](#quick-start)
|
|
12
|
+
- [Next.js App Router](#nextjs-app-router)
|
|
13
|
+
- [Nuxt](#nuxt)
|
|
14
|
+
- [SvelteKit](#sveltekit)
|
|
15
|
+
- [Astro](#astro)
|
|
16
|
+
- [Manual start (non-Next.js)](#manual-start-non-nextjs)
|
|
17
|
+
- [Vanilla HTML](#vanilla-html-snippet)
|
|
18
|
+
- [ES Module](#es-module)
|
|
19
|
+
- [Color Configuration](#color-configuration)
|
|
20
|
+
- [API](#api)
|
|
21
|
+
- [How It Works](#how-it-works)
|
|
22
|
+
- [Browser Support](#browser-support)
|
|
23
|
+
- [Debugging](#debugging)
|
|
24
|
+
- [License](#license)
|
|
25
|
+
|
|
7
26
|
## Features
|
|
8
27
|
|
|
9
28
|
- **Zero dependencies** — pure Web Component, no React, no Vue, nothing
|
|
@@ -25,31 +44,7 @@ yarn add @vctqs1/nav-progress-bar
|
|
|
25
44
|
|
|
26
45
|
## Quick Start
|
|
27
46
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
```html
|
|
31
|
-
<script type="module">
|
|
32
|
-
import { registerNavProgressBar } from '@vctqs1/nav-progress-bar';
|
|
33
|
-
registerNavProgressBar();
|
|
34
|
-
</script>
|
|
35
|
-
|
|
36
|
-
<vctqs1-nav-progress-bar></vctqs1-nav-progress-bar>
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### ES Module
|
|
40
|
-
|
|
41
|
-
```ts
|
|
42
|
-
import { registerNavProgressBar, getNavProgressBar } from '@vctqs1/nav-progress-bar';
|
|
43
|
-
|
|
44
|
-
registerNavProgressBar();
|
|
45
|
-
|
|
46
|
-
// The bar auto-starts on navigate and auto-finishes on navigatesuccess.
|
|
47
|
-
// You can also control it manually:
|
|
48
|
-
getNavProgressBar()?.start();
|
|
49
|
-
getNavProgressBar()?.finish();
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Framework Guides
|
|
47
|
+
Pick your framework below. For most SPA routers the Navigation API fires automatically after `registerNavProgressBar()` — no extra wiring needed. If you need to force a manual start signal, see [Manual start (non-Next.js)](#manual-start-non-nextjs) below.
|
|
53
48
|
|
|
54
49
|
### Next.js App Router
|
|
55
50
|
|
|
@@ -116,6 +111,11 @@ User clicks <Link>
|
|
|
116
111
|
→ "navigatesuccess" fires → bar finishes
|
|
117
112
|
```
|
|
118
113
|
|
|
114
|
+
> 🎬 **Demo** — [watch the bar in action with Next.js App Router →](https://github.com/vctqs1/nav-progress-bar#nextjs-app-router)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
<video src="https://github.com/user-attachments/assets/4144ed95-8c25-4aa9-b804-905ac24805b4" controls width="100%"></video>
|
|
118
|
+
|
|
119
119
|
---
|
|
120
120
|
|
|
121
121
|
### Nuxt
|
|
@@ -181,7 +181,51 @@ registerNavProgressBar();
|
|
|
181
181
|
|
|
182
182
|
---
|
|
183
183
|
|
|
184
|
-
###
|
|
184
|
+
### Manual start (non-Next.js)
|
|
185
|
+
|
|
186
|
+
Most SPA routers trigger browser navigation events automatically, so the bar starts/finishes on its own after `registerNavProgressBar()`.
|
|
187
|
+
If you need to force an immediate start signal, add a guarded listener:
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
import { registerNavProgressBar, getNavProgressBar } from '@vctqs1/nav-progress-bar';
|
|
191
|
+
|
|
192
|
+
registerNavProgressBar();
|
|
193
|
+
|
|
194
|
+
const nav = (globalThis as { navigation?: EventTarget }).navigation;
|
|
195
|
+
if (nav?.addEventListener) {
|
|
196
|
+
nav.addEventListener('navigate', () => getNavProgressBar()?.start());
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### Vanilla HTML (snippet)
|
|
203
|
+
|
|
204
|
+
```html
|
|
205
|
+
<script type="module">
|
|
206
|
+
import { registerNavProgressBar } from '@vctqs1/nav-progress-bar';
|
|
207
|
+
registerNavProgressBar();
|
|
208
|
+
</script>
|
|
209
|
+
|
|
210
|
+
<vctqs1-nav-progress-bar></vctqs1-nav-progress-bar>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### ES Module
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { registerNavProgressBar, getNavProgressBar } from '@vctqs1/nav-progress-bar';
|
|
217
|
+
|
|
218
|
+
registerNavProgressBar();
|
|
219
|
+
|
|
220
|
+
// The bar auto-starts on navigate and auto-finishes on navigatesuccess.
|
|
221
|
+
// You can also control it manually:
|
|
222
|
+
getNavProgressBar()?.start();
|
|
223
|
+
getNavProgressBar()?.finish();
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
### Vanilla HTML (full page)
|
|
185
229
|
|
|
186
230
|
```html
|
|
187
231
|
<!DOCTYPE html>
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=globalThis.HTMLElement??class{};function t(){let e=globalThis.navigation;if(!(!e||typeof e!=`object`)&&!(!(`addEventListener`in e)||!(`removeEventListener`in e)))return e}var n=class extends e{_layoutSheet=void 0;_sheet=void 0;_progressSheet=void 0;_autoTimer=void 0;_startTime=void 0;_options;static get observedAttributes(){return[`primary`]}constructor(e={}){super(),this._options=e}_onNavigateSuccess=()=>this.finish();_onNavigate=()=>this.start();connectedCallback(){this._render();let e=t();e&&(e.addEventListener(`navigate`,this._onNavigate),e.addEventListener(`navigatesuccess`,this._onNavigateSuccess))}disconnectedCallback(){let e=t();e&&(e.removeEventListener(`navigate`,this._onNavigate),e.removeEventListener(`navigatesuccess`,this._onNavigateSuccess)),this._clearTimer()}attributeChangedCallback(){this._sheet&&this._applyColorSheet()}_resolveColor(e,t,n){let r=e??t;return r?r.startsWith(`--`)?`var(${r}, ${n})`:r:n}_buildColorSheet(){let e=this._resolveColor(this.getAttribute(`primary`),this._options.primary,`#006bde`),t=new CSSStyleSheet;return t.insertRule(`
|
|
2
2
|
.bar {
|
|
3
3
|
height: 3px;
|
|
4
4
|
background: ${e};
|
|
@@ -20,4 +20,4 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(){
|
|
|
20
20
|
z-index: 9999;
|
|
21
21
|
pointer-events: none;
|
|
22
22
|
}
|
|
23
|
-
`),this._layoutSheet=t;let n=new CSSStyleSheet;if(n.insertRule(`.bar { width: 0%; }`),this._progressSheet=n,this._sheet=this._buildColorSheet(),e.adoptedStyleSheets=[this._layoutSheet,this._sheet,this._progressSheet],!e.querySelector(`.bar`)){let t=document.createElement(`div`);t.className=`bar`,e.append(t)}}_setWidth(e){this._progressSheet&&this._progressSheet.replaceSync(`.bar { width: ${e}%; }`)}start(){this._clearTimer(),this._setWidth(0),this._applyActive(!0);let e=0;return this._autoTimer=setInterval(()=>{e+=(85-e)*.08,this._setWidth(Math.min(e,85))},200),this}finish(){this._clearTimer();let e=this.shadowRoot?.querySelector(`.bar`);return e?(e.classList.remove(`active`),e.classList.add(`complete`),this._setWidth(100),setTimeout(()=>{e.classList.remove(`complete`),this._setWidth(0),this._applyActive(!1)},700),this):this}_applyActive(e){let t=this.shadowRoot?.querySelector(`.bar`);if(t&&(t.classList.toggle(`active`,e),e&&this._startTime===void 0&&(this._startTime=performance.now()),!e&&this._startTime!==void 0)){let e=performance.now()-this._startTime;console.log(`[top-loading-bar] navigation took ${e.toFixed(0)}ms`),this._startTime=void 0}}_clearTimer(){this._autoTimer!==void 0&&(clearInterval(this._autoTimer),this._autoTimer=void 0)}},
|
|
23
|
+
`),this._layoutSheet=t;let n=new CSSStyleSheet;if(n.insertRule(`.bar { width: 0%; }`),this._progressSheet=n,this._sheet=this._buildColorSheet(),e.adoptedStyleSheets=[this._layoutSheet,this._sheet,this._progressSheet],!e.querySelector(`.bar`)){let t=document.createElement(`div`);t.className=`bar`,e.append(t)}}_setWidth(e){this._progressSheet&&this._progressSheet.replaceSync(`.bar { width: ${e}%; }`)}start(){this._clearTimer(),this._setWidth(0),this._applyActive(!0);let e=0;return this._autoTimer=setInterval(()=>{e+=(85-e)*.08,this._setWidth(Math.min(e,85))},200),this}finish(){this._clearTimer();let e=this.shadowRoot?.querySelector(`.bar`);return e?(e.classList.remove(`active`),e.classList.add(`complete`),this._setWidth(100),setTimeout(()=>{e.classList.remove(`complete`),this._setWidth(0),this._applyActive(!1)},700),this):this}_applyActive(e){let t=this.shadowRoot?.querySelector(`.bar`);if(t&&(t.classList.toggle(`active`,e),e&&this._startTime===void 0&&(this._startTime=performance.now()),!e&&this._startTime!==void 0)){let e=performance.now()-this._startTime;console.log(`[top-loading-bar] navigation took ${e.toFixed(0)}ms`),this._startTime=void 0}}_clearTimer(){this._autoTimer!==void 0&&(clearInterval(this._autoTimer),this._autoTimer=void 0)}},r=`vctqs1-nav-progress-bar`;function i(e){if(!(typeof customElements>`u`)&&t()&&!customElements.get(`vctqs1-nav-progress-bar`)){class t extends n{constructor(){super(e)}}customElements.define(r,t)}}function a(){return document.querySelector(r)}exports.NavProgressBar=n,exports.TAG_NAME=r,exports.getNavProgressBar=a,exports.registerNavProgressBar=i;
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
//#region src/lib/nav-progress-bar.ts
|
|
2
|
-
|
|
2
|
+
var e = globalThis.HTMLElement ?? class {};
|
|
3
|
+
function t() {
|
|
3
4
|
let e = globalThis.navigation;
|
|
4
5
|
if (!(!e || typeof e != "object") && !(!("addEventListener" in e) || !("removeEventListener" in e))) return e;
|
|
5
6
|
}
|
|
6
|
-
var
|
|
7
|
+
var n = class extends e {
|
|
7
8
|
_layoutSheet = void 0;
|
|
8
9
|
_sheet = void 0;
|
|
9
10
|
_progressSheet = void 0;
|
|
@@ -20,12 +21,12 @@ var t = class extends HTMLElement {
|
|
|
20
21
|
_onNavigate = () => this.start();
|
|
21
22
|
connectedCallback() {
|
|
22
23
|
this._render();
|
|
23
|
-
let
|
|
24
|
-
|
|
24
|
+
let e = t();
|
|
25
|
+
e && (e.addEventListener("navigate", this._onNavigate), e.addEventListener("navigatesuccess", this._onNavigateSuccess));
|
|
25
26
|
}
|
|
26
27
|
disconnectedCallback() {
|
|
27
|
-
let
|
|
28
|
-
|
|
28
|
+
let e = t();
|
|
29
|
+
e && (e.removeEventListener("navigate", this._onNavigate), e.removeEventListener("navigatesuccess", this._onNavigateSuccess)), this._clearTimer();
|
|
29
30
|
}
|
|
30
31
|
attributeChangedCallback() {
|
|
31
32
|
this._sheet && this._applyColorSheet();
|
|
@@ -93,19 +94,19 @@ var t = class extends HTMLElement {
|
|
|
93
94
|
_clearTimer() {
|
|
94
95
|
this._autoTimer !== void 0 && (clearInterval(this._autoTimer), this._autoTimer = void 0);
|
|
95
96
|
}
|
|
96
|
-
},
|
|
97
|
-
function
|
|
98
|
-
if (!(typeof customElements > "u") &&
|
|
99
|
-
class
|
|
97
|
+
}, r = "vctqs1-nav-progress-bar";
|
|
98
|
+
function i(e) {
|
|
99
|
+
if (!(typeof customElements > "u") && t() && !customElements.get("vctqs1-nav-progress-bar")) {
|
|
100
|
+
class t extends n {
|
|
100
101
|
constructor() {
|
|
101
|
-
super(
|
|
102
|
+
super(e);
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
|
-
customElements.define(
|
|
105
|
+
customElements.define(r, t);
|
|
105
106
|
}
|
|
106
107
|
}
|
|
107
|
-
function
|
|
108
|
-
return document.querySelector(
|
|
108
|
+
function a() {
|
|
109
|
+
return document.querySelector(r);
|
|
109
110
|
}
|
|
110
111
|
//#endregion
|
|
111
|
-
export {
|
|
112
|
+
export { n as NavProgressBar, r as TAG_NAME, a as getNavProgressBar, i as registerNavProgressBar };
|
|
@@ -5,6 +5,7 @@ export interface NavProgressBarOptions {
|
|
|
5
5
|
/** Primary color of the progress bar (hex, rgb, or CSS custom property) */
|
|
6
6
|
primary?: string;
|
|
7
7
|
}
|
|
8
|
+
declare const HTMLElementBase: typeof HTMLElement;
|
|
8
9
|
/**
|
|
9
10
|
* A web component that displays a progress bar during navigation.
|
|
10
11
|
* Integrates with the Navigation API to automatically start/finish the progress bar.
|
|
@@ -35,7 +36,7 @@ export interface NavProgressBarOptions {
|
|
|
35
36
|
* <nav-progress-bar primary="#006bde"></nav-progress-bar>
|
|
36
37
|
* ```
|
|
37
38
|
*/
|
|
38
|
-
export declare class NavProgressBar extends
|
|
39
|
+
export declare class NavProgressBar extends HTMLElementBase {
|
|
39
40
|
private _layoutSheet;
|
|
40
41
|
private _sheet;
|
|
41
42
|
private _progressSheet;
|
|
@@ -112,4 +113,5 @@ export declare function registerNavProgressBar(options?: NavProgressBarOptions):
|
|
|
112
113
|
* ```
|
|
113
114
|
*/
|
|
114
115
|
export declare function getNavProgressBar(): NavProgressBar | null;
|
|
116
|
+
export {};
|
|
115
117
|
//# sourceMappingURL=nav-progress-bar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nav-progress-bar.d.ts","sourceRoot":"","sources":["../../src/lib/nav-progress-bar.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"nav-progress-bar.d.ts","sourceRoot":"","sources":["../../src/lib/nav-progress-bar.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAOD,QAAA,MAAM,eAAe,EAAE,OAAO,WAEe,CAAC;AAc9C;;;;;;;;;;;;;;GAcG;AACH;;;;;;;;;;;;;;GAcG;AACH,qBAAa,cAAe,SAAQ,eAAe;IACjD,OAAO,CAAC,YAAY,CAAwC;IAC5D,OAAO,CAAC,MAAM,CAAwC;IACtD,OAAO,CAAC,cAAc,CAAwC;IAC9D,OAAO,CAAC,UAAU,CAAyD;IAC3E,OAAO,CAAC,UAAU,CAAiC;IACnD,OAAO,CAAC,QAAQ,CAAwB;IAExC,MAAM,KAAK,kBAAkB,aAE5B;IAED;;;OAGG;IACH;;;OAGG;gBACS,OAAO,GAAE,qBAA0B;IAK/C,OAAO,CAAC,kBAAkB,CAAuB;IACjD,OAAO,CAAC,WAAW,CAAsB;IAEzC,iBAAiB;IAajB,oBAAoB;IAYpB,wBAAwB;IAMxB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,gBAAgB;IA2BxB,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,OAAO;IAwCf,OAAO,CAAC,SAAS;IAMjB;;;;OAIG;IACH;;;;OAIG;IACH,KAAK,IAAI,IAAI;IAgBb;;;OAGG;IACH;;;OAGG;IACH,MAAM,IAAI,IAAI;IAmBd,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,WAAW;CAMpB;AAED,8DAA8D;AAC9D,eAAO,MAAM,QAAQ,EAAG,yBAAkC,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE,qBAAqB,QAgBrE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,IAAI,CAEzD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vctqs1/nav-progress-bar",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"repository": "https://github.com/vctqs1/nav-progress-bar",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"main": "./dist/index.js",
|
|
6
7
|
"module": "./dist/index.js",
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
],
|
|
20
21
|
"license": "MIT",
|
|
21
22
|
"keywords": [
|
|
23
|
+
"toploader",
|
|
22
24
|
"progress-bar",
|
|
23
25
|
"navigation",
|
|
24
26
|
"web-component",
|