@vctqs1/nav-progress-bar 0.0.2-4 → 0.0.2-5
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 +65 -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 +2 -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
|
|
|
@@ -181,7 +176,51 @@ registerNavProgressBar();
|
|
|
181
176
|
|
|
182
177
|
---
|
|
183
178
|
|
|
184
|
-
###
|
|
179
|
+
### Manual start (non-Next.js)
|
|
180
|
+
|
|
181
|
+
Most SPA routers trigger browser navigation events automatically, so the bar starts/finishes on its own after `registerNavProgressBar()`.
|
|
182
|
+
If you need to force an immediate start signal, add a guarded listener:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { registerNavProgressBar, getNavProgressBar } from '@vctqs1/nav-progress-bar';
|
|
186
|
+
|
|
187
|
+
registerNavProgressBar();
|
|
188
|
+
|
|
189
|
+
const nav = (globalThis as { navigation?: EventTarget }).navigation;
|
|
190
|
+
if (nav?.addEventListener) {
|
|
191
|
+
nav.addEventListener('navigate', () => getNavProgressBar()?.start());
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### Vanilla HTML (snippet)
|
|
198
|
+
|
|
199
|
+
```html
|
|
200
|
+
<script type="module">
|
|
201
|
+
import { registerNavProgressBar } from '@vctqs1/nav-progress-bar';
|
|
202
|
+
registerNavProgressBar();
|
|
203
|
+
</script>
|
|
204
|
+
|
|
205
|
+
<vctqs1-nav-progress-bar></vctqs1-nav-progress-bar>
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### ES Module
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
import { registerNavProgressBar, getNavProgressBar } from '@vctqs1/nav-progress-bar';
|
|
212
|
+
|
|
213
|
+
registerNavProgressBar();
|
|
214
|
+
|
|
215
|
+
// The bar auto-starts on navigate and auto-finishes on navigatesuccess.
|
|
216
|
+
// You can also control it manually:
|
|
217
|
+
getNavProgressBar()?.start();
|
|
218
|
+
getNavProgressBar()?.finish();
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
### Vanilla HTML (full page)
|
|
185
224
|
|
|
186
225
|
```html
|
|
187
226
|
<!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"}
|