1+ /* eslint-disable max-len */
2+ /* eslint-disable @typescript-eslint/no-explicit-any */
13/**
24 *
35 * NextTopLoader
1012import * as PropTypes from 'prop-types' ;
1113import * as React from 'react' ;
1214import * as NProgress from 'nprogress' ;
13- export interface NextTopLoaderProps {
15+ export type NextTopLoaderProps = {
1416 /**
1517 * Color for the TopLoader.
1618 * @default "#29d"
@@ -27,7 +29,7 @@ export interface NextTopLoaderProps {
2729 */
2830 crawlSpeed ?: number ;
2931 /**
30- * The height for the TopLoader.
32+ * The height for the TopLoader in pixels (px) .
3133 * @default 3
3234 */
3335 height ?: number ;
@@ -53,99 +55,120 @@ export interface NextTopLoaderProps {
5355 speed ?: number ;
5456}
5557
56- const NextTopLoader = ( props : NextTopLoaderProps ) => {
57- const color = '#29d' ;
58- const height = 3 ;
58+ const NextTopLoader = ( {
59+ color,
60+ height,
61+ showSpinner,
62+ crawl,
63+ crawlSpeed,
64+ initialPosition,
65+ easing,
66+ speed,
67+ } : NextTopLoaderProps ) => {
68+ const defaultColor = '#29d' ;
69+ const defaultHeight = 3 ;
5970
6071 const styles = (
6172 < style >
62- { `#nprogress{pointer-events:none}#nprogress .bar{background:${
63- props . color ? props . color : color
64- } ;position:fixed;z-index:1031;top:0;left:0;width:100%;height:${
65- props . height ? props . height : height
66- } px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px ${
67- props . color ? props . color : color
68- } ,0 0 5px ${
69- props . color ? props . color : color
70- } ;opacity:1;-webkit-transform:rotate(3deg) translate(0px,-4px);-ms-transform:rotate(3deg) translate(0px,-4px);transform:rotate(3deg) translate(0px,-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:${
71- props . color ? props . color : color
72- } ;border-left-color:${
73- props . color ? props . color : color
74- } ;border-radius:50%;-webkit-animation:nprogress-spinner 400ms linear infinite;animation:nprogress-spinner 400ms linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}`}
73+ {
74+ `#nprogress{pointer-events:none}#nprogress .bar{background:${
75+ color ?? defaultColor
76+ } ;position:fixed;z-index:1031;top:0;left:0;width:100%;height:${
77+ height ?? defaultHeight
78+ } px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px ${
79+ color ?? defaultColor
80+ } ,0 0 5px ${
81+ color ?? defaultColor
82+ } ;opacity:1;-webkit-transform:rotate(3deg) translate(0px,-4px);-ms-transform:rotate(3deg) translate(0px,-4px);transform:rotate(3deg) translate(0px,-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:${
83+ color ?? defaultColor
84+ } ;border-left-color:${
85+ color ?? defaultColor
86+ } ;border-radius:50%;-webkit-animation:nprogress-spinner 400ms linear infinite;animation:nprogress-spinner 400ms linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}`
87+ }
7588 </ style >
7689 ) ;
7790
7891 React . useEffect ( ( ) => {
79- if ( props . showSpinner !== undefined ) {
80- NProgress . configure ( { showSpinner : props . showSpinner } ) ;
81- }
82- if ( props . crawl !== undefined ) {
83- NProgress . configure ( { trickle : props . crawl } ) ;
84- }
85- if ( props . crawlSpeed !== undefined ) {
86- NProgress . configure ( { trickleSpeed : props . crawlSpeed } ) ;
87- }
88- if ( props . initialPosition !== undefined ) {
89- NProgress . configure ( { minimum : props . initialPosition } ) ;
90- }
91- if ( props . easing !== undefined ) {
92- NProgress . configure ( { easing : props . easing } ) ;
93- }
94- if ( props . speed !== undefined ) {
95- NProgress . configure ( { speed : props . speed } ) ;
92+ NProgress . configure ( {
93+ showSpinner : showSpinner ?? true ,
94+ trickle : crawl ?? true ,
95+ trickleSpeed : crawlSpeed ?? 200 ,
96+ minimum : initialPosition ?? 0.08 ,
97+ easing : easing ?? 'ease' ,
98+ speed : speed ?? 200 ,
99+ } ) ;
100+
101+ function isAnchorOfCurrentUrl ( currentUrl : string , newUrl : string ) {
102+ const currentUrlObj = new URL ( currentUrl ) ;
103+ const newUrlObj = new URL ( newUrl ) ;
104+ // Compare hostname, pathname, and search parameters
105+ if (
106+ currentUrlObj . hostname === newUrlObj . hostname &&
107+ currentUrlObj . pathname === newUrlObj . pathname &&
108+ currentUrlObj . search === newUrlObj . search
109+ ) {
110+ // Check if the new URL is just an anchor of the current URL page
111+ const currentHash = currentUrlObj . hash ;
112+ const newHash = newUrlObj . hash ;
113+ return (
114+ currentHash !== newHash && currentUrlObj . href . replace ( currentHash , '' ) === newUrlObj . href . replace ( newHash , '' )
115+ ) ;
116+ }
117+ return false ;
96118 }
119+
97120 // eslint-disable-next-line no-var
98121 var npgclass = document . querySelectorAll ( 'html' ) ;
99- let navLinks = document . querySelectorAll ( 'a' ) ;
100- navLinks . forEach ( ( navLink ) => {
101- navLink . addEventListener ( 'click' , ( event : MouseEvent ) => {
102- let currentUrl = window . location . href ;
103- let newUrl = ( event . currentTarget as HTMLAnchorElement ) . href ;
104- let isExternalLink = ( event . currentTarget as HTMLAnchorElement ) . target === "_blank" ;
105- function isAnchorOfCurrentUrl ( currentUrl : string , newUrl : string ) {
106- const currentUrlObj = new URL ( currentUrl ) ;
107- const newUrlObj = new URL ( newUrl ) ;
108- // Compare hostname, pathname, and search parameters
109- if (
110- currentUrlObj . hostname === newUrlObj . hostname &&
111- currentUrlObj . pathname === newUrlObj . pathname &&
112- currentUrlObj . search === newUrlObj . search
113- ) {
114- // Check if the new URL is just an anchor of the current URL page
115- const currentHash = currentUrlObj . hash ;
116- const newHash = newUrlObj . hash ;
117- return (
118- currentHash !== newHash &&
119- currentUrlObj . href . replace ( currentHash , '' ) === newUrlObj . href . replace ( newHash , '' )
120- ) ;
122+ function findClosestAnchor ( element : HTMLElement | null ) : HTMLAnchorElement | null {
123+ while ( element && element . tagName . toLowerCase ( ) !== 'a' ) {
124+ element = element . parentElement ;
125+ }
126+ return element as HTMLAnchorElement ;
127+ }
128+ function handleClick ( event : MouseEvent ) {
129+ try {
130+ const target = event . target as HTMLElement ;
131+ const anchor = findClosestAnchor ( target ) ;
132+ if ( anchor ) {
133+ const currentUrl = window . location . href ;
134+ const newUrl = ( anchor as HTMLAnchorElement ) . href ;
135+ const isExternalLink = ( anchor as HTMLAnchorElement ) . target === "_blank" ;
136+ const isAnchor = isAnchorOfCurrentUrl ( currentUrl , newUrl ) ;
137+ if ( newUrl === currentUrl || isAnchor || isExternalLink ) {
138+ NProgress . start ( ) ;
139+ NProgress . done ( ) ;
140+ [ ] . forEach . call ( npgclass , function ( el : Element ) {
141+ el . classList . remove ( "nprogress-busy" ) ;
142+ } ) ;
143+ } else {
144+ NProgress . start ( ) ;
145+ ( function ( history ) {
146+ const pushState = history . pushState ;
147+ history . pushState = function ( ) {
148+ NProgress . done ( ) ;
149+ [ ] . forEach . call ( npgclass , function ( el : Element ) {
150+ el . classList . remove ( "nprogress-busy" ) ;
151+ } ) ;
152+ return pushState . apply ( history , arguments as any ) ;
153+ } ;
154+ } ) ( window . history ) ;
121155 }
122- return false ;
123- }
124- const isAnchor = isAnchorOfCurrentUrl ( currentUrl , newUrl ) ;
125- if ( newUrl === currentUrl || isAnchor || isExternalLink ) {
126- NProgress . start ( ) ;
127- NProgress . done ( ) ;
128- [ ] . forEach . call ( npgclass , function ( el : Element ) {
129- el . classList . remove ( 'nprogress-busy' ) ;
130- } ) ;
131- } else {
132- NProgress . start ( ) ;
133- ( function ( history ) {
134- // eslint-disable-next-line no-var
135- var pushState = history . pushState ;
136- history . pushState = function ( ) {
137- NProgress . done ( ) ;
138- [ ] . forEach . call ( npgclass , function ( el : Element ) {
139- el . classList . remove ( 'nprogress-busy' ) ;
140- } ) ;
141- // eslint-disable-next-line prefer-rest-params
142- return pushState . apply ( history , arguments ) ;
143- } ;
144- } ) ( window . history ) ;
145156 }
146- } ) ;
147- } ) ;
148- } ) ;
157+ } catch ( err ) {
158+ console . log ( 'NextTopLoader error: ' , err ) ;
159+ }
160+ }
161+
162+ // Add the global click event listener
163+ document . addEventListener ( "click" , handleClick ) ;
164+
165+ // Clean up the global click event listener when the component is unmounted
166+ return ( ) => {
167+ document . removeEventListener ( "click" , handleClick ) ;
168+ } ;
169+
170+ // eslint-disable-next-line react-hooks/exhaustive-deps
171+ } , [ ] ) ;
149172
150173 return styles ;
151174} ;
0 commit comments