@vctqs1/nav-progress-bar-react 0.0.2-4

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 ADDED
@@ -0,0 +1,153 @@
1
+ # @vctqs1/nav-progress-bar-react
2
+
3
+ React wrapper for [`@vctqs1/nav-progress-bar`](https://www.npmjs.com/package/@vctqs1/nav-progress-bar) — a zero-dependency, CSP-safe top-of-page progress bar built as a native Web Component.
4
+
5
+ This package provides a thin React component that renders the `<vctqs1-nav-progress-bar>` custom element with proper TypeScript JSX types and SSR support via Declarative Shadow DOM.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @vctqs1/nav-progress-bar @vctqs1/nav-progress-bar-react
11
+ # or
12
+ pnpm add @vctqs1/nav-progress-bar @vctqs1/nav-progress-bar-react
13
+ ```
14
+
15
+ ## Peer Dependencies
16
+
17
+ | Package | Version |
18
+ |---------|---------|
19
+ | `react` | `>=18.0.0` |
20
+ | `@vctqs1/nav-progress-bar` | `>=1.0.0` |
21
+
22
+ ## Usage
23
+
24
+ ### Next.js App Router (recommended setup)
25
+
26
+ **1. Add to root layout** (`app/layout.tsx`):
27
+
28
+ ```tsx
29
+ import NavProgressBar from '@vctqs1/nav-progress-bar-react';
30
+
31
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
32
+ return (
33
+ <html>
34
+ <body>
35
+ <NavProgressBar />
36
+ {children}
37
+ </body>
38
+ </html>
39
+ );
40
+ }
41
+ ```
42
+
43
+ **2. Create `instrumentation-client.ts`** at the project root:
44
+
45
+ ```ts
46
+ import { registerNavProgressBar, getNavProgressBar } from '@vctqs1/nav-progress-bar-react';
47
+
48
+ registerNavProgressBar();
49
+
50
+ export function onRouterTransitionStart(
51
+ url: string,
52
+ navigationType: 'push' | 'replace' | 'traverse',
53
+ ) {
54
+ getNavProgressBar()?.start();
55
+ }
56
+ ```
57
+
58
+ **3. Enable instrumentation** in `next.config.ts`:
59
+
60
+ ```ts
61
+ const nextConfig = {
62
+ experimental: {
63
+ instrumentationHook: true,
64
+ },
65
+ };
66
+
67
+ export default nextConfig;
68
+ ```
69
+
70
+ That's it. The bar starts on every route departure and finishes automatically when the new page commits.
71
+
72
+ ---
73
+
74
+ ### Custom color
75
+
76
+ ```tsx
77
+ <NavProgressBar primary="#ff6600" />
78
+ ```
79
+
80
+ ```tsx
81
+ <NavProgressBar primary="--your-brand-color" />
82
+ ```
83
+
84
+ ---
85
+
86
+ ### React SPA (Vite, CRA, etc.)
87
+
88
+ For non-SSR React apps, register and place the component anywhere above your routes:
89
+
90
+ ```tsx
91
+ // main.tsx
92
+ import { registerNavProgressBar } from '@vctqs1/nav-progress-bar-react';
93
+ registerNavProgressBar();
94
+ ```
95
+
96
+ ```tsx
97
+ // App.tsx
98
+ import NavProgressBar from '@vctqs1/nav-progress-bar-react';
99
+
100
+ export default function App() {
101
+ return (
102
+ <>
103
+ <NavProgressBar />
104
+ <RouterProvider router={router} />
105
+ </>
106
+ );
107
+ }
108
+ ```
109
+
110
+ In a plain SPA the bar auto-starts and auto-finishes via the browser Navigation API — no extra wiring needed.
111
+
112
+ ## Props
113
+
114
+ | Prop | Type | Default | Description |
115
+ |------|------|---------|-------------|
116
+ | `primary` | `string` | `#006bde` | Bar color — accepts any CSS color or a `--css-variable` name |
117
+
118
+ ## How SSR Works
119
+
120
+ The component renders a `<vctqs1-nav-progress-bar>` custom element server-side. It uses `<template shadowrootmode="open">` (Declarative Shadow DOM) to pre-render the bar div so the element has visual structure before JavaScript runs — preventing a flash of invisible bar during hydration.
121
+
122
+ When the custom element upgrades in the browser it detects the existing shadow root:
123
+
124
+ ```ts
125
+ const shadow = this.shadowRoot ?? this.attachShadow({ mode: 'open' });
126
+ ```
127
+
128
+ And skips creating a duplicate bar div if one already exists:
129
+
130
+ ```ts
131
+ if (!shadow.querySelector('.bar')) {
132
+ const bar = document.createElement('div');
133
+ bar.className = 'bar';
134
+ shadow.append(bar);
135
+ }
136
+ ```
137
+
138
+ ## Why a Separate Package?
139
+
140
+ The core `@vctqs1/nav-progress-bar` package has zero dependencies and works in any environment. This wrapper exists solely to:
141
+
142
+ 1. Provide correct TypeScript JSX types for `<vctqs1-nav-progress-bar>` so React doesn't complain about an unknown element
143
+ 2. Ship a typed React component with a clean import
144
+ 3. Handle the Declarative Shadow DOM template for SSR without pulling React into the core package
145
+
146
+ ## Related
147
+
148
+ - [`@vctqs1/nav-progress-bar`](https://www.npmjs.com/package/@vctqs1/nav-progress-bar) — core Web Component, framework-agnostic
149
+ - [Next.js issue #43548](https://github.com/vercel/next.js/issues/43548) — the problem this solves
150
+
151
+ ## License
152
+
153
+ MIT
@@ -0,0 +1,3 @@
1
+ export * from './lib/nav-progress-bar-react';
2
+ export * from '@vctqs1/nav-progress-bar';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,112 @@
1
+ import "react/jsx-runtime";
2
+ //#region ../nav-progress-bar/dist/index.js
3
+ function e() {
4
+ let e = globalThis.navigation;
5
+ if (!(!e || typeof e != "object") && !(!("addEventListener" in e) || !("removeEventListener" in e))) return e;
6
+ }
7
+ var t = class extends HTMLElement {
8
+ _layoutSheet = void 0;
9
+ _sheet = void 0;
10
+ _progressSheet = void 0;
11
+ _autoTimer = void 0;
12
+ _startTime = void 0;
13
+ _options;
14
+ static get observedAttributes() {
15
+ return ["primary"];
16
+ }
17
+ constructor(e = {}) {
18
+ super(), this._options = e;
19
+ }
20
+ _onNavigateSuccess = () => this.finish();
21
+ _onNavigate = () => this.start();
22
+ connectedCallback() {
23
+ this._render();
24
+ let t = e();
25
+ t && (t.addEventListener("navigate", this._onNavigate), t.addEventListener("navigatesuccess", this._onNavigateSuccess));
26
+ }
27
+ disconnectedCallback() {
28
+ let t = e();
29
+ t && (t.removeEventListener("navigate", this._onNavigate), t.removeEventListener("navigatesuccess", this._onNavigateSuccess)), this._clearTimer();
30
+ }
31
+ attributeChangedCallback() {
32
+ this._sheet && this._applyColorSheet();
33
+ }
34
+ _resolveColor(e, t, n) {
35
+ let r = e ?? t;
36
+ return r ? r.startsWith("--") ? `var(${r}, ${n})` : r : n;
37
+ }
38
+ _buildColorSheet() {
39
+ let e = this._resolveColor(this.getAttribute("primary"), this._options.primary, "#006bde"), t = new CSSStyleSheet();
40
+ return t.insertRule(`
41
+ .bar {
42
+ height: 3px;
43
+ background: ${e};
44
+ opacity: 0;
45
+ transition: width 0.3s ease, opacity 0.4s ease;
46
+ box-shadow: 0 0 8px color-mix(in srgb, ${e} 53%, transparent);
47
+ }
48
+ `), t.insertRule(".bar.active { opacity: 1; }"), t.insertRule("\n .bar.complete {\n opacity: 0;\n transition: width 0.2s ease, opacity 0.5s ease 0.2s;\n }\n "), t;
49
+ }
50
+ _applyColorSheet() {
51
+ !this.shadowRoot || !this._layoutSheet || !this._progressSheet || (this._sheet = this._buildColorSheet(), this.shadowRoot.adoptedStyleSheets = [
52
+ this._layoutSheet,
53
+ this._sheet,
54
+ this._progressSheet
55
+ ]);
56
+ }
57
+ _render() {
58
+ let e = this.shadowRoot ?? this.attachShadow({ mode: "open" }), t = new CSSStyleSheet();
59
+ t.insertRule("\n :host {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n z-index: 9999;\n pointer-events: none;\n }\n "), this._layoutSheet = t;
60
+ let n = new CSSStyleSheet();
61
+ if (n.insertRule(".bar { width: 0%; }"), this._progressSheet = n, this._sheet = this._buildColorSheet(), e.adoptedStyleSheets = [
62
+ this._layoutSheet,
63
+ this._sheet,
64
+ this._progressSheet
65
+ ], !e.querySelector(".bar")) {
66
+ let t = document.createElement("div");
67
+ t.className = "bar", e.append(t);
68
+ }
69
+ }
70
+ _setWidth(e) {
71
+ this._progressSheet && this._progressSheet.replaceSync(`.bar { width: ${e}%; }`);
72
+ }
73
+ start() {
74
+ this._clearTimer(), this._setWidth(0), this._applyActive(!0);
75
+ let e = 0;
76
+ return this._autoTimer = setInterval(() => {
77
+ e += (85 - e) * .08, this._setWidth(Math.min(e, 85));
78
+ }, 200), this;
79
+ }
80
+ finish() {
81
+ this._clearTimer();
82
+ let e = this.shadowRoot?.querySelector(".bar");
83
+ return e ? (e.classList.remove("active"), e.classList.add("complete"), this._setWidth(100), setTimeout(() => {
84
+ e.classList.remove("complete"), this._setWidth(0), this._applyActive(!1);
85
+ }, 700), this) : this;
86
+ }
87
+ _applyActive(e) {
88
+ let t = this.shadowRoot?.querySelector(".bar");
89
+ if (t && (t.classList.toggle("active", e), e && this._startTime === void 0 && (this._startTime = performance.now()), !e && this._startTime !== void 0)) {
90
+ let e = performance.now() - this._startTime;
91
+ console.log(`[top-loading-bar] navigation took ${e.toFixed(0)}ms`), this._startTime = void 0;
92
+ }
93
+ }
94
+ _clearTimer() {
95
+ this._autoTimer !== void 0 && (clearInterval(this._autoTimer), this._autoTimer = void 0);
96
+ }
97
+ }, n = "vctqs1-nav-progress-bar";
98
+ function r(r) {
99
+ if (!(typeof customElements > "u") && e() && !customElements.get("vctqs1-nav-progress-bar")) {
100
+ class e extends t {
101
+ constructor() {
102
+ super(r);
103
+ }
104
+ }
105
+ customElements.define(n, e);
106
+ }
107
+ }
108
+ function i() {
109
+ return document.querySelector(n);
110
+ }
111
+ //#endregion
112
+ export { t as NavProgressBar, n as TAG_NAME, i as getNavProgressBar, r as registerNavProgressBar };
@@ -0,0 +1,11 @@
1
+ import { NavProgressBarOptions } from '@vctqs1/nav-progress-bar';
2
+ declare module "react" {
3
+ namespace JSX {
4
+ interface IntrinsicElements {
5
+ "vctqs1-nav-progress-bar": import("react").HTMLAttributes<HTMLElement> & NavProgressBarOptions;
6
+ }
7
+ }
8
+ }
9
+ declare function NavProgressBar({ primary }: NavProgressBarOptions): import("react/jsx-runtime").JSX.Element;
10
+ export default NavProgressBar;
11
+ //# sourceMappingURL=nav-progress-bar-react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav-progress-bar-react.d.ts","sourceRoot":"","sources":["../../src/lib/nav-progress-bar-react.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAA;AAErE,OAAO,QAAQ,OAAO,CAAC;IACrB,UAAU,GAAG,CAAC;QACZ,UAAU,iBAAiB;YACzB,yBAAyB,EAAE,OAAO,OAAO,EAAE,cAAc,CAAC,WAAW,CAAC,GAAG,qBAAqB,CAAC;SAChG;KACF;CACF;AAED,iBAAS,cAAc,CAAC,EACtB,OAAmB,EACpB,EAAE,qBAAqB,2CAEvB;AAED,eAAe,cAAc,CAAC"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@vctqs1/nav-progress-bar-react",
3
+ "version": "0.0.2-4",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ "./package.json": "./package.json",
10
+ ".": {
11
+ "source": "./src/index.ts",
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "!**/*.tsbuildinfo"
20
+ ],
21
+ "license": "MIT",
22
+ "keywords": [
23
+ "progress-bar",
24
+ "navigation",
25
+ "react",
26
+ "web-component",
27
+ "custom-element",
28
+ "nextjs",
29
+ "app-router",
30
+ "loading",
31
+ "csp",
32
+ "shadow-dom"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "dependencies": {
38
+ "@vctqs1/nav-progress-bar": "0.0.2-4"
39
+ },
40
+ "nx": {
41
+ "name": "@vctqs1/nav-progress-bar-react",
42
+ "projectType": "library"
43
+ }
44
+ }