next-nprogress-bar
Advanced tools
Comparing version 2.1.2 to 2.2.0
import React from 'react'; | ||
import { ProgressBarProps } from '.'; | ||
import { NavigateOptions } from 'next/dist/shared/lib/app-router-context'; | ||
export declare const AppProgressBar: React.MemoExoticComponent<({ color, height, options, shallowRouting, delay, style, }: ProgressBarProps) => React.JSX.Element>; | ||
export declare const AppProgressBar: (props: ProgressBarProps) => React.JSX.Element; | ||
export declare function useRouter(): { | ||
@@ -9,2 +9,5 @@ push: (href: string, options?: NavigateOptions, NProgressOptions?: { | ||
}) => void; | ||
replace: (href: string, options?: NavigateOptions, NProgressOptions?: { | ||
showProgressBar?: boolean; | ||
}) => void; | ||
back: (NProgressOptions?: { | ||
@@ -15,4 +18,3 @@ showProgressBar?: boolean; | ||
refresh(): void; | ||
replace(href: string, options?: NavigateOptions | undefined): void; | ||
prefetch(href: string, options?: import("next/dist/shared/lib/app-router-context").PrefetchOptions | undefined): void; | ||
}; |
@@ -21,2 +21,4 @@ export interface NProgressOptions { | ||
style?: string; | ||
shouldCompareComplexProps?: boolean; | ||
targetPreprocessor?: (url: URL) => URL; | ||
} | ||
@@ -30,4 +32,6 @@ /** | ||
* @param style Custom css - @default undefined | ||
* @param shouldCompareComplexProps If you want to compare props in the React.memo return - @default false | ||
* @param targetPreprocessor If you want to preprocess the target URL - @default undefined | ||
*/ | ||
export { AppProgressBar, useRouter } from './appDir'; | ||
export { PagesProgressBar } from './pagesDir'; |
@@ -37,2 +37,7 @@ 'use strict'; | ||
function isSameURL(target, current) { | ||
var cleanTarget = target.protocol + '//' + target.host + target.pathname + target.search; | ||
var cleanCurrent = current.protocol + '//' + current.host + current.pathname + current.search; | ||
return cleanTarget === cleanCurrent; | ||
} | ||
function isSameURLWithoutSearch(target, current) { | ||
var cleanTarget = target.protocol + '//' + target.host + target.pathname; | ||
@@ -43,4 +48,42 @@ var cleanCurrent = current.protocol + '//' + current.host + current.pathname; | ||
var AppProgressBar = React.memo(function (_a) { | ||
var _b = _a.color, color = _b === void 0 ? '#0A2FFF' : _b, _c = _a.height, height = _c === void 0 ? '2px' : _c, options = _a.options, _d = _a.shallowRouting, shallowRouting = _d === void 0 ? false : _d, _e = _a.delay, delay = _e === void 0 ? 0 : _e, style = _a.style; | ||
function parsePath(path) { | ||
var hashIndex = path.indexOf('#'); | ||
var queryIndex = path.indexOf('?'); | ||
var hasQuery = queryIndex > -1 && (hashIndex < 0 || queryIndex < hashIndex); | ||
if (hasQuery || hashIndex > -1) { | ||
return { | ||
pathname: path.substring(0, hasQuery ? queryIndex : hashIndex), | ||
query: hasQuery | ||
? path.substring(queryIndex, hashIndex > -1 ? hashIndex : undefined) | ||
: '', | ||
hash: hashIndex > -1 ? path.slice(hashIndex) : '', | ||
}; | ||
} | ||
return { pathname: path, query: '', hash: '' }; | ||
} | ||
function addPathPrefix(path, prefix) { | ||
if (!path.startsWith('/') || !prefix) { | ||
return path; | ||
} | ||
var _a = parsePath(path), pathname = _a.pathname, query = _a.query, hash = _a.hash; | ||
return "".concat(prefix).concat(pathname).concat(query).concat(hash); | ||
} | ||
function getAnchorProperty(a, key) { | ||
if (typeof key === 'string' && key === 'data-disable-nprogress') { | ||
var dataKey = key.substring(5); | ||
return a.dataset[dataKey]; | ||
} | ||
var prop = a[key]; | ||
if (prop instanceof SVGAnimatedString) { | ||
var value = prop.baseVal; | ||
if (key === 'href') { | ||
return addPathPrefix(value, location.origin); | ||
} | ||
return value; | ||
} | ||
return prop; | ||
} | ||
var AppProgressBarComponent = React.memo(function (_a) { | ||
var _b = _a.color, color = _b === void 0 ? '#0A2FFF' : _b, _c = _a.height, height = _c === void 0 ? '2px' : _c, options = _a.options, _d = _a.shallowRouting, shallowRouting = _d === void 0 ? false : _d, _e = _a.delay, delay = _e === void 0 ? 0 : _e, style = _a.style, targetPreprocessor = _a.targetPreprocessor; | ||
var styles = (React.createElement("style", null, style || | ||
@@ -66,3 +109,3 @@ "\n #nprogress {\n pointer-events: none;\n }\n\n #nprogress .bar {\n background: ".concat(color, ";\n\n position: fixed;\n z-index: 1031;\n top: 0;\n left: 0;\n\n width: 100%;\n height: ").concat(height, ";\n }\n\n /* Fancy blur effect */\n #nprogress .peg {\n display: block;\n position: absolute;\n right: 0px;\n width: 100px;\n height: 100%;\n box-shadow: 0 0 10px ").concat(color, ", 0 0 5px ").concat(color, ";\n opacity: 1.0;\n\n -webkit-transform: rotate(3deg) translate(0px, -4px);\n -ms-transform: rotate(3deg) translate(0px, -4px);\n transform: rotate(3deg) translate(0px, -4px);\n }\n\n /* Remove these to get rid of the spinner */\n #nprogress .spinner {\n display: block;\n position: fixed;\n z-index: 1031;\n top: 15px;\n right: 15px;\n }\n\n #nprogress .spinner-icon {\n width: 18px;\n height: 18px;\n box-sizing: border-box;\n\n border: solid 2px transparent;\n border-top-color: ").concat(color, ";\n border-left-color: ").concat(color, ";\n border-radius: 50%;\n\n -webkit-animation: nprogress-spinner 400ms linear infinite;\n animation: nprogress-spinner 400ms linear infinite;\n }\n\n .nprogress-custom-parent {\n overflow: hidden;\n position: relative;\n }\n\n .nprogress-custom-parent #nprogress .spinner,\n .nprogress-custom-parent #nprogress .bar {\n position: absolute;\n }\n\n @-webkit-keyframes nprogress-spinner {\n 0% { -webkit-transform: rotate(0deg); }\n 100% { -webkit-transform: rotate(360deg); }\n }\n @keyframes nprogress-spinner {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n "))); | ||
// Skip anchors with target="_blank" | ||
if (anchorElement.target === '_blank') | ||
if (getAnchorProperty(anchorElement, 'target') === '_blank') | ||
return; | ||
@@ -72,5 +115,8 @@ // Skip control/command+click | ||
return; | ||
var targetUrl = new URL(anchorElement.href); | ||
var targetHref = getAnchorProperty(anchorElement, 'href'); | ||
var targetUrl = targetPreprocessor | ||
? targetPreprocessor(new URL(targetHref)) | ||
: new URL(targetHref); | ||
var currentUrl = new URL(location.href); | ||
if (shallowRouting && isSameURL(targetUrl, currentUrl)) | ||
if (shallowRouting && isSameURLWithoutSearch(targetUrl, currentUrl)) | ||
return; | ||
@@ -82,8 +128,14 @@ if ((targetUrl === null || targetUrl === void 0 ? void 0 : targetUrl.href) === (currentUrl === null || currentUrl === void 0 ? void 0 : currentUrl.href)) | ||
var handleMutation = function () { | ||
var anchorElements = document.querySelectorAll('a'); | ||
// Skip anchors with target="_blank" and anchors without href | ||
var validAnchorELes = Array.from(anchorElements).filter(function (anchor) { return anchor.href && anchor.target !== '_blank'; }); | ||
validAnchorELes.forEach(function (anchor) { | ||
return anchor.addEventListener('click', handleAnchorClick); | ||
var anchorElements = Array.from(document.querySelectorAll('a')); | ||
var validAnchorElements = anchorElements.filter(function (anchor) { | ||
var href = getAnchorProperty(anchor, 'href'); | ||
var isNProgressDisabled = anchor.getAttribute('data-disable-nprogress') === 'true'; | ||
var isNotTelOrMailto = href && !href.startsWith('tel:') && !href.startsWith('mailto:'); | ||
return (!isNProgressDisabled && | ||
isNotTelOrMailto && | ||
getAnchorProperty(anchor, 'target') !== '_blank'); | ||
}); | ||
validAnchorElements.forEach(function (anchor) { | ||
anchor.addEventListener('click', handleAnchorClick); | ||
}); | ||
}; | ||
@@ -100,17 +152,35 @@ var mutationObserver = new MutationObserver(handleMutation); | ||
return styles; | ||
}, function () { return true; }); | ||
}, function (prevProps, nextProps) { | ||
if (!nextProps.shouldCompareComplexProps) { | ||
return true; | ||
} | ||
return (prevProps.color === nextProps.color && | ||
prevProps.height === nextProps.height && | ||
prevProps.shallowRouting === nextProps.shallowRouting && | ||
prevProps.delay === nextProps.delay && | ||
JSON.stringify(prevProps.options) === JSON.stringify(nextProps.options) && | ||
prevProps.style === nextProps.style); | ||
}); | ||
var AppProgressBar = function (props) { return (React.createElement(React.Suspense, { fallback: React.createElement(React.Fragment, null) }, | ||
React.createElement(AppProgressBarComponent, __assign({}, props)))); }; | ||
function useRouter() { | ||
var router = navigation.useRouter(); | ||
var pathname = navigation.usePathname(); | ||
function push(href, options, NProgressOptions) { | ||
var startProgress = React.useCallback(function (href, options, NProgressOptions) { | ||
if ((NProgressOptions === null || NProgressOptions === void 0 ? void 0 : NProgressOptions.showProgressBar) === false) | ||
return router.push(href, options); | ||
var currentUrl = new URL(pathname, location.href); | ||
var currentUrl = new URL(location.href); | ||
var targetUrl = new URL(href, location.href); | ||
if (isSameURL(targetUrl, currentUrl) || href === pathname) | ||
if (isSameURL(targetUrl, currentUrl)) | ||
return router.push(href, options); | ||
NProgress.start(); | ||
}, [router]); | ||
var push = React.useCallback(function (href, options, NProgressOptions) { | ||
startProgress(href, options, NProgressOptions); | ||
return router.push(href, options); | ||
} | ||
function back(NProgressOptions) { | ||
}, [router, startProgress]); | ||
var replace = React.useCallback(function (href, options, NProgressOptions) { | ||
startProgress(href, options, NProgressOptions); | ||
return router.replace(href, options); | ||
}, [router, startProgress]); | ||
var back = React.useCallback(function (NProgressOptions) { | ||
if ((NProgressOptions === null || NProgressOptions === void 0 ? void 0 : NProgressOptions.showProgressBar) === false) | ||
@@ -120,4 +190,7 @@ return router.back(); | ||
return router.back(); | ||
} | ||
return __assign(__assign({}, router), { push: push, back: back }); | ||
}, [router]); | ||
var enhancedRouter = React.useMemo(function () { | ||
return __assign(__assign({}, router), { push: push, replace: replace, back: back }); | ||
}, [router, push, replace, back]); | ||
return enhancedRouter; | ||
} | ||
@@ -124,0 +197,0 @@ |
import React from 'react'; | ||
import { ProgressBarProps } from '.'; | ||
export declare const PagesProgressBar: React.MemoExoticComponent<({ color, height, options, shallowRouting, delay, style, }: ProgressBarProps) => React.JSX.Element>; | ||
export declare const PagesProgressBar: React.MemoExoticComponent<({ color, height, options, shallowRouting, delay, style, }: Omit<ProgressBarProps, 'shouldCompareComplexProps' | 'targetPreprocessor'>) => React.JSX.Element>; |
export declare function isSameURL(target: URL, current: URL): boolean; | ||
export declare function isSameURLWithoutSearch(target: URL, current: URL): boolean; |
{ | ||
"name": "next-nprogress-bar", | ||
"version": "2.1.2", | ||
"version": "2.2.0", | ||
"description": "NextJS progress bar compatible with new app directory", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -23,3 +23,5 @@ <div align="center"> | ||
- [Second approach wrap in a use client Providers component](#second-approach-wrap-in-a-use-client-providers-component) | ||
- [Props](#other-solutions) | ||
- [Tips](#tips) | ||
- [Disable progress bar on specific links](#disable-progress-bar-on-specific-links) | ||
- [Props](#props) | ||
- [height](#height) | ||
@@ -32,2 +34,4 @@ - [color](#color) | ||
- [style](#style) | ||
- [shouldCompareComplexProps](#shouldcomparecomplexprops) | ||
- [targetPreprocessor](#targetpreprocessor) | ||
- [App directory router](#app-directory-router) | ||
@@ -148,2 +152,4 @@ - [Import](#import) | ||
##### /components/ProgressBarProvider.jsx | ||
```jsx | ||
@@ -170,3 +176,7 @@ // Create a Providers component to wrap your application with all the components requiring 'use client', such as next-nprogress-bar or your different contexts... | ||
export default Providers; | ||
``` | ||
##### /app/layout.jsx | ||
```jsx | ||
// Import and use it in /app/layout.jsx | ||
@@ -226,2 +236,4 @@ import Providers from './providers'; | ||
##### /components/ProgressBarProvider.tsx | ||
```tsx | ||
@@ -248,3 +260,7 @@ // Create a Providers component to wrap your application with all the components requiring 'use client', such as next-nprogress-bar or your different contexts... | ||
export default Providers; | ||
``` | ||
##### /app/layout.tsx | ||
```tsx | ||
// Import and use it in /app/layout.tsx | ||
@@ -273,2 +289,16 @@ import Providers from './providers'; | ||
## Tips | ||
### Disable progress bar on specific links | ||
You can disable the progress bar on specific links by adding the `data-disable-nprogress={true}` attribute. | ||
_/!\ This will not work for Link in svg elements._ | ||
```jsx | ||
<Link href="#features" data-disable-nprogress={true}> | ||
Features | ||
</Link> | ||
``` | ||
## Props | ||
@@ -292,3 +322,3 @@ | ||
If the progress bar is not displayed when you use shallow routing - **by default false** | ||
If the progress bar is not displayed when you only change URL parameters without changing route - **by default false** | ||
@@ -305,2 +335,16 @@ See [Next.js docs](https://nextjs.org/docs/pages/building-your-application/routing/linking-and-navigating#shallow-routing) | ||
### shouldCompareComplexProps _optional_ - _boolean_ - (_only for app directory progress bar_) | ||
Activates a detailed comparison of component props to determine if a rerender is necessary. | ||
When `true`, the component will only rerender if there are changes in key props such as `color`, `height`, `shallowRouting`, `delay`, `options`, and `style`. | ||
This is useful for optimizing performance in scenarios where these props change infrequently. If not provided or set to `false`, the component will assume props have not changed and will not rerender, which can enhance performance in scenarios where the props remain static. - **by default undefined** | ||
### targetPreprocessor _optional_ - _(url: URL) => URL_ - (_only for app directory progress bar_) | ||
Provides a custom function to preprocess the target URL before comparing it with the current URL. | ||
This is particularly useful in scenarios where URLs undergo transformations, such as localization or path modifications, after navigation. | ||
The function takes a `URL` object as input and should return a modified `URL` object. | ||
If this prop is provided, the preprocessed URL will be used for comparisons, ensuring accurate determination of whether the navigation target is equivalent to the current URL. | ||
This can prevent unnecessary display of the progress bar when the target URL is effectively the same as the current URL after preprocessing. - **by default undefined** | ||
## App directory router | ||
@@ -323,2 +367,3 @@ | ||
router.push('/about'); | ||
router.replace('/?counter=10'); | ||
router.back(); | ||
@@ -334,2 +379,9 @@ | ||
); | ||
router.replace( | ||
'/?counter=10', | ||
{}, | ||
{ | ||
showProgressBar: false, | ||
}, | ||
); | ||
router.back({ showProgressBar: false }); | ||
@@ -336,0 +388,0 @@ ``` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
53253
10
288
439