Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

use-react-router-breadcrumbs

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

use-react-router-breadcrumbs - npm Package Compare versions

Comparing version 2.0.2 to 3.0.0

216

dist/cjs/index.js

@@ -5,3 +5,2 @@ 'use strict';

var _toConsumableArray = require('@babel/runtime/helpers/toConsumableArray');
var React = require('react');

@@ -12,37 +11,112 @@ var reactRouter = require('react-router');

var _toConsumableArray__default = /*#__PURE__*/_interopDefaultLegacy(_toConsumableArray);
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
var joinPaths = function joinPaths(paths) {
return paths.join('/').replace(/\/\/+/g, '/');
};
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
var paramRe = /^:\w+$/;
var dynamicSegmentValue = 3;
var indexRouteValue = 2;
var emptySegmentValue = 1;
var staticSegmentValue = 10;
var splatPenalty = -2;
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var isSplat = function isSplat(s) {
return s === '*';
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
function computeScore(path, index) {
var segments = path.split('/');
var initialScore = segments.length;
if (segments.some(isSplat)) {
initialScore += splatPenalty;
}
if (index) {
initialScore += indexRouteValue;
}
return segments.filter(function (s) {
return !isSplat(s);
}).reduce(function (score, segment) {
if (paramRe.test(segment)) {
return score + dynamicSegmentValue;
}
if (segment === '') {
return score + emptySegmentValue;
}
return score + staticSegmentValue;
}, initialScore);
}
var DEFAULT_MATCH_OPTIONS = {
exact: true
};
var NO_BREADCRUMB = 'NO_BREADCRUMB';
function compareIndexes(a, b) {
var siblings = a.length === b.length && a.slice(0, -1).every(function (n, i) {
return n === b[i];
});
return siblings ? a[a.length - 1] - b[b.length - 1] : 0;
}
function flattenRoutes(routes) {
var branches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var parentsMeta = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
var parentPath = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '';
routes.forEach(function (route, index) {
if (typeof route.path !== 'string' && !route.index) {
throw new Error('useBreadcrumbs: `path` or `index` must be provided in every route object');
}
if (route.path && route.index) {
throw new Error('useBreadcrumbs: `path` and `index` cannot be provided at the same time');
}
var meta = {
relativePath: route.path || '',
childrenIndex: index,
route: route
};
if (meta.relativePath.charAt(0) === '/') {
if (!meta.relativePath.startsWith(parentPath)) {
throw new Error('useBreadcrumbs: The absolute path of the child route must start with the parent path');
}
meta.relativePath = meta.relativePath.slice(parentPath.length);
}
var path = joinPaths([parentPath, meta.relativePath]);
var routesMeta = parentsMeta.concat(meta);
if (route.children && route.children.length > 0) {
if (route.index) {
throw new Error('useBreadcrumbs: Index route cannot have child routes');
}
flattenRoutes(route.children, branches, routesMeta, path);
}
branches.push({
path: path,
score: computeScore(path, route.index),
routesMeta: routesMeta
});
});
return branches;
}
function rankRouteBranches(branches) {
return branches.sort(function (a, b) {
return a.score !== b.score ? b.score - a.score : compareIndexes(a.routesMeta.map(function (meta) {
return meta.childrenIndex;
}), b.routesMeta.map(function (meta) {
return meta.childrenIndex;
}));
});
}
var NO_BREADCRUMB = Symbol('NO_BREADCRUMB');
var humanize = function humanize(str) {

@@ -62,3 +136,3 @@ return str.replace(/^[\s_]+|[\s_]+$/g, '').replace(/[_\s]+/g, ' ').replace(/^[a-z]/, function (m) {

location: location,
key: match.url
key: match.pathname
}, props || {});

@@ -76,7 +150,6 @@ return Object.assign(Object.assign({}, componentProps), {

pathSection = _ref2.pathSection;
var match = reactRouter.matchPath(pathSection, Object.assign(Object.assign({}, DEFAULT_MATCH_OPTIONS), {
var match = reactRouter.matchPath({
end: true,
path: pathSection
})) || {
url: 'not-found'
};
}, pathSection);
return render({

@@ -95,11 +168,10 @@ breadcrumb: humanize(currentSection),

pathSection = _ref3.pathSection,
routes = _ref3.routes;
branches = _ref3.branches;
var breadcrumb;
var getIsPathExcluded = function getIsPathExcluded(path) {
return reactRouter.matchPath(pathSection, {
return reactRouter.matchPath({
path: path,
exact: true,
strict: false
}) != null;
end: true
}, pathSection) != null;
};

@@ -111,17 +183,25 @@

routes.some(function (_a) {
var userProvidedBreadcrumb = _a.breadcrumb,
matchOptions = _a.matchOptions,
path = _a.path,
rest = __rest(_a, ["breadcrumb", "matchOptions", "path"]);
branches.some(function (_ref4) {
var path = _ref4.path,
routesMeta = _ref4.routesMeta;
var route = routesMeta[routesMeta.length - 1].route;
var userProvidedBreadcrumb = route.breadcrumb;
if (!path) {
throw new Error('useBreadcrumbs: `path` must be provided in every route object');
if (!userProvidedBreadcrumb && route.index) {
var parentMeta = routesMeta[routesMeta.length - 2];
if (parentMeta && parentMeta.route.breadcrumb) {
userProvidedBreadcrumb = parentMeta.route.breadcrumb;
}
}
var match = reactRouter.matchPath(pathSection, Object.assign(Object.assign({}, matchOptions || DEFAULT_MATCH_OPTIONS), {
path: path
}));
var caseSensitive = route.caseSensitive,
props = route.props;
var match = reactRouter.matchPath({
path: path,
end: true,
caseSensitive: caseSensitive
}, pathSection);
if (match && userProvidedBreadcrumb === null || !match && matchOptions) {
if (match && userProvidedBreadcrumb === null) {
breadcrumb = NO_BREADCRUMB;

@@ -137,7 +217,8 @@ return true;

breadcrumb = render(Object.assign({
breadcrumb = render({
breadcrumb: userProvidedBreadcrumb || humanize(currentSection),
match: match,
location: location
}, rest));
location: location,
props: props
});
return true;

@@ -164,9 +245,10 @@ }

var getBreadcrumbs = function getBreadcrumbs(_ref4) {
var routes = _ref4.routes,
location = _ref4.location,
_ref4$options = _ref4.options,
options = _ref4$options === void 0 ? {} : _ref4$options;
var matches = [];
var getBreadcrumbs = function getBreadcrumbs(_ref5) {
var routes = _ref5.routes,
location = _ref5.location,
_ref5$options = _ref5.options,
options = _ref5$options === void 0 ? {} : _ref5$options;
var pathname = location.pathname;
var branches = rankRouteBranches(flattenRoutes(routes));
var breadcrumbs = [];
pathname.split('?')[0].split('/').reduce(function (previousSection, currentSection, index) {

@@ -183,7 +265,7 @@ var pathSection = !currentSection ? '/' : "".concat(previousSection, "/").concat(currentSection);

pathSection: pathSection,
routes: routes
branches: branches
}, options));
if (breadcrumb !== NO_BREADCRUMB) {
matches.push(breadcrumb);
breadcrumbs.push(breadcrumb);
}

@@ -193,18 +275,8 @@

}, '');
return matches;
return breadcrumbs;
};
var flattenRoutes = function flattenRoutes(routes) {
return routes.reduce(function (arr, route) {
if (route.routes) {
return arr.concat([route].concat(_toConsumableArray__default['default'](flattenRoutes(route.routes))));
}
return arr.concat(route);
}, []);
};
var useReactRouterBreadcrumbs = function useReactRouterBreadcrumbs(routes, options) {
return getBreadcrumbs({
routes: flattenRoutes(routes || []),
routes: routes || [],
location: reactRouter.useLocation(),

@@ -211,0 +283,0 @@ options: options

@@ -1,37 +0,112 @@

import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
import React, { createElement } from 'react';
import { useLocation, matchPath } from 'react-router';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
var joinPaths = function joinPaths(paths) {
return paths.join('/').replace(/\/\/+/g, '/');
};
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
var paramRe = /^:\w+$/;
var dynamicSegmentValue = 3;
var indexRouteValue = 2;
var emptySegmentValue = 1;
var staticSegmentValue = 10;
var splatPenalty = -2;
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var isSplat = function isSplat(s) {
return s === '*';
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
function computeScore(path, index) {
var segments = path.split('/');
var initialScore = segments.length;
if (segments.some(isSplat)) {
initialScore += splatPenalty;
}
if (index) {
initialScore += indexRouteValue;
}
return segments.filter(function (s) {
return !isSplat(s);
}).reduce(function (score, segment) {
if (paramRe.test(segment)) {
return score + dynamicSegmentValue;
}
if (segment === '') {
return score + emptySegmentValue;
}
return score + staticSegmentValue;
}, initialScore);
}
var DEFAULT_MATCH_OPTIONS = {
exact: true
};
var NO_BREADCRUMB = 'NO_BREADCRUMB';
function compareIndexes(a, b) {
var siblings = a.length === b.length && a.slice(0, -1).every(function (n, i) {
return n === b[i];
});
return siblings ? a[a.length - 1] - b[b.length - 1] : 0;
}
function flattenRoutes(routes) {
var branches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var parentsMeta = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
var parentPath = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '';
routes.forEach(function (route, index) {
if (typeof route.path !== 'string' && !route.index) {
throw new Error('useBreadcrumbs: `path` or `index` must be provided in every route object');
}
if (route.path && route.index) {
throw new Error('useBreadcrumbs: `path` and `index` cannot be provided at the same time');
}
var meta = {
relativePath: route.path || '',
childrenIndex: index,
route: route
};
if (meta.relativePath.charAt(0) === '/') {
if (!meta.relativePath.startsWith(parentPath)) {
throw new Error('useBreadcrumbs: The absolute path of the child route must start with the parent path');
}
meta.relativePath = meta.relativePath.slice(parentPath.length);
}
var path = joinPaths([parentPath, meta.relativePath]);
var routesMeta = parentsMeta.concat(meta);
if (route.children && route.children.length > 0) {
if (route.index) {
throw new Error('useBreadcrumbs: Index route cannot have child routes');
}
flattenRoutes(route.children, branches, routesMeta, path);
}
branches.push({
path: path,
score: computeScore(path, route.index),
routesMeta: routesMeta
});
});
return branches;
}
function rankRouteBranches(branches) {
return branches.sort(function (a, b) {
return a.score !== b.score ? b.score - a.score : compareIndexes(a.routesMeta.map(function (meta) {
return meta.childrenIndex;
}), b.routesMeta.map(function (meta) {
return meta.childrenIndex;
}));
});
}
var NO_BREADCRUMB = Symbol('NO_BREADCRUMB');
var humanize = function humanize(str) {

@@ -51,3 +126,3 @@ return str.replace(/^[\s_]+|[\s_]+$/g, '').replace(/[_\s]+/g, ' ').replace(/^[a-z]/, function (m) {

location: location,
key: match.url
key: match.pathname
}, props || {});

@@ -65,7 +140,6 @@ return Object.assign(Object.assign({}, componentProps), {

pathSection = _ref2.pathSection;
var match = matchPath(pathSection, Object.assign(Object.assign({}, DEFAULT_MATCH_OPTIONS), {
var match = matchPath({
end: true,
path: pathSection
})) || {
url: 'not-found'
};
}, pathSection);
return render({

@@ -84,11 +158,10 @@ breadcrumb: humanize(currentSection),

pathSection = _ref3.pathSection,
routes = _ref3.routes;
branches = _ref3.branches;
var breadcrumb;
var getIsPathExcluded = function getIsPathExcluded(path) {
return matchPath(pathSection, {
return matchPath({
path: path,
exact: true,
strict: false
}) != null;
end: true
}, pathSection) != null;
};

@@ -100,17 +173,25 @@

routes.some(function (_a) {
var userProvidedBreadcrumb = _a.breadcrumb,
matchOptions = _a.matchOptions,
path = _a.path,
rest = __rest(_a, ["breadcrumb", "matchOptions", "path"]);
branches.some(function (_ref4) {
var path = _ref4.path,
routesMeta = _ref4.routesMeta;
var route = routesMeta[routesMeta.length - 1].route;
var userProvidedBreadcrumb = route.breadcrumb;
if (!path) {
throw new Error('useBreadcrumbs: `path` must be provided in every route object');
if (!userProvidedBreadcrumb && route.index) {
var parentMeta = routesMeta[routesMeta.length - 2];
if (parentMeta && parentMeta.route.breadcrumb) {
userProvidedBreadcrumb = parentMeta.route.breadcrumb;
}
}
var match = matchPath(pathSection, Object.assign(Object.assign({}, matchOptions || DEFAULT_MATCH_OPTIONS), {
path: path
}));
var caseSensitive = route.caseSensitive,
props = route.props;
var match = matchPath({
path: path,
end: true,
caseSensitive: caseSensitive
}, pathSection);
if (match && userProvidedBreadcrumb === null || !match && matchOptions) {
if (match && userProvidedBreadcrumb === null) {
breadcrumb = NO_BREADCRUMB;

@@ -126,7 +207,8 @@ return true;

breadcrumb = render(Object.assign({
breadcrumb = render({
breadcrumb: userProvidedBreadcrumb || humanize(currentSection),
match: match,
location: location
}, rest));
location: location,
props: props
});
return true;

@@ -153,9 +235,10 @@ }

var getBreadcrumbs = function getBreadcrumbs(_ref4) {
var routes = _ref4.routes,
location = _ref4.location,
_ref4$options = _ref4.options,
options = _ref4$options === void 0 ? {} : _ref4$options;
var matches = [];
var getBreadcrumbs = function getBreadcrumbs(_ref5) {
var routes = _ref5.routes,
location = _ref5.location,
_ref5$options = _ref5.options,
options = _ref5$options === void 0 ? {} : _ref5$options;
var pathname = location.pathname;
var branches = rankRouteBranches(flattenRoutes(routes));
var breadcrumbs = [];
pathname.split('?')[0].split('/').reduce(function (previousSection, currentSection, index) {

@@ -172,7 +255,7 @@ var pathSection = !currentSection ? '/' : "".concat(previousSection, "/").concat(currentSection);

pathSection: pathSection,
routes: routes
branches: branches
}, options));
if (breadcrumb !== NO_BREADCRUMB) {
matches.push(breadcrumb);
breadcrumbs.push(breadcrumb);
}

@@ -182,18 +265,8 @@

}, '');
return matches;
return breadcrumbs;
};
var flattenRoutes = function flattenRoutes(routes) {
return routes.reduce(function (arr, route) {
if (route.routes) {
return arr.concat([route].concat(_toConsumableArray(flattenRoutes(route.routes))));
}
return arr.concat(route);
}, []);
};
var useReactRouterBreadcrumbs = function useReactRouterBreadcrumbs(routes, options) {
return getBreadcrumbs({
routes: flattenRoutes(routes || []),
routes: routes || [],
location: useLocation(),

@@ -200,0 +273,0 @@ options: options

import React from 'react';
import { useLocation } from 'react-router';
import { useLocation, RouteObject, Params, PathPattern } from 'react-router';
declare type Location = ReturnType<typeof useLocation>;

@@ -8,12 +8,17 @@ export interface Options {

}
export interface MatchOptions {
exact?: boolean;
strict?: boolean;
sensitive?: boolean;
export interface BreadcrumbMatch<ParamKey extends string = string> {
params: Params<ParamKey>;
pathname: string;
pattern: PathPattern;
route?: BreadcrumbsRoute;
}
export interface BreadcrumbsRoute {
path: string;
breadcrumb?: React.ComponentType | React.ElementType | string | null;
matchOptions?: MatchOptions;
routes?: BreadcrumbsRoute[];
export interface BreadcrumbComponentProps<ParamKey extends string = string> {
key: string;
match: BreadcrumbMatch<ParamKey>;
location: Location;
}
export declare type BreadcrumbComponentType<ParamKey extends string = string> = React.ComponentType<BreadcrumbComponentProps<ParamKey>>;
export interface BreadcrumbsRoute<ParamKey extends string = string> extends RouteObject {
children?: BreadcrumbsRoute[];
breadcrumb?: BreadcrumbComponentType<ParamKey> | string | null;
props?: {

@@ -23,6 +28,4 @@ [x: string]: unknown;

}
export interface BreadcrumbData {
match: {
url: string;
};
export interface BreadcrumbData<ParamKey extends string = string> {
match: BreadcrumbMatch<ParamKey>;
location: Location;

@@ -37,3 +40,3 @@ key: string;

}) => BreadcrumbData[];
declare const useReactRouterBreadcrumbs: (routes?: BreadcrumbsRoute[] | undefined, options?: Options | undefined) => BreadcrumbData[];
declare const useReactRouterBreadcrumbs: (routes?: BreadcrumbsRoute<string>[] | undefined, options?: Options | undefined) => BreadcrumbData[];
export default useReactRouterBreadcrumbs;

@@ -1,1 +0,1 @@

!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@babel/runtime/helpers/toConsumableArray"),require("react"),require("react-router")):"function"==typeof define&&define.amd?define(["exports","@babel/runtime/helpers/toConsumableArray","react","react-router"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self)["react-router-breadcrumbs-hoc"]={},e._toConsumableArray,e.React,e.ReactRouter)}(this,(function(e,t,r,n){"use strict";function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var a=o(t),c=o(r);var u={exact:!0},i="NO_BREADCRUMB",s=function(e){return e.replace(/^[\s_]+|[\s_]+$/g,"").replace(/[_\s]+/g," ").replace(/^[a-z]/,(function(e){return e.toUpperCase()}))},l=function(e){var t=e.breadcrumb,n=e.match,o=e.location,a=e.props,u=Object.assign({match:n,location:o,key:n.url},a||{});return Object.assign(Object.assign({},u),{breadcrumb:"string"==typeof t?r.createElement("span",{key:u.key},t):c.default.createElement(t,Object.assign({},u))})},p=function(e){var t,r=e.currentSection,o=e.disableDefaults,a=e.excludePaths,c=e.location,p=e.pathSection,b=e.routes;return a&&a.some((function(e){return null!=n.matchPath(p,{path:e,exact:!0,strict:!1})}))?i:(b.some((function(e){var a=e.breadcrumb,b=e.matchOptions,f=e.path,d=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(n=Object.getOwnPropertySymbols(e);o<n.length;o++)t.indexOf(n[o])<0&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]])}return r}(e,["breadcrumb","matchOptions","path"]);if(!f)throw new Error("useBreadcrumbs: `path` must be provided in every route object");var m=n.matchPath(p,Object.assign(Object.assign({},b||u),{path:f}));return m&&null===a||!m&&b?(t=i,!0):!!m&&(!a&&o?(t=i,!0):(t=l(Object.assign({breadcrumb:a||s(r),match:m,location:c},d)),!0))})),t||(o?i:function(e){var t=e.currentSection,r=e.location,o=e.pathSection,a=n.matchPath(o,Object.assign(Object.assign({},u),{path:o}))||{url:"not-found"};return l({breadcrumb:s(t),match:a,location:r})}({pathSection:p,currentSection:"/"===p?"Home":r,location:c})))},b=function(e){var t=e.routes,r=e.location,n=e.options,o=void 0===n?{}:n,a=[];return r.pathname.split("?")[0].split("/").reduce((function(e,n,c){var u=n?"".concat(e,"/").concat(n):"/";if("/"===u&&0!==c)return"";var s=p(Object.assign({currentSection:n,location:r,pathSection:u,routes:t},o));return s!==i&&a.push(s),"/"===u?"":u}),""),a},f=function e(t){return t.reduce((function(t,r){return r.routes?t.concat([r].concat(a.default(e(r.routes)))):t.concat(r)}),[])};e.default=function(e,t){return b({routes:f(e||[]),location:n.useLocation(),options:t})},e.getBreadcrumbs=b,Object.defineProperty(e,"__esModule",{value:!0})}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("react-router")):"function"==typeof define&&define.amd?define(["exports","react","react-router"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self)["react-router-breadcrumbs-hoc"]={},e.React,e.ReactRouter)}(this,(function(e,t,r){"use strict";function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var a=n(t),o=function(e){return e.join("/").replace(/\/\/+/g,"/")},c=/^:\w+$/,i=function(e){return"*"===e};function u(e,t){var r=e.split("/"),n=r.length;return r.some(i)&&(n+=-2),t&&(n+=2),r.filter((function(e){return!i(e)})).reduce((function(e,t){return c.test(t)?e+3:""===t?e+1:e+10}),n)}function s(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"";return e.forEach((function(e,a){if("string"!=typeof e.path&&!e.index)throw new Error("useBreadcrumbs: `path` or `index` must be provided in every route object");if(e.path&&e.index)throw new Error("useBreadcrumbs: `path` and `index` cannot be provided at the same time");var c={relativePath:e.path||"",childrenIndex:a,route:e};if("/"===c.relativePath.charAt(0)){if(!c.relativePath.startsWith(n))throw new Error("useBreadcrumbs: The absolute path of the child route must start with the parent path");c.relativePath=c.relativePath.slice(n.length)}var i=o([n,c.relativePath]),h=r.concat(c);if(e.children&&e.children.length>0){if(e.index)throw new Error("useBreadcrumbs: Index route cannot have child routes");s(e.children,t,h,i)}t.push({path:i,score:u(i,e.index),routesMeta:h})})),t}var h=Symbol("NO_BREADCRUMB"),l=function(e){return e.replace(/^[\s_]+|[\s_]+$/g,"").replace(/[_\s]+/g," ").replace(/^[a-z]/,(function(e){return e.toUpperCase()}))},d=function(e){var r=e.breadcrumb,n=e.match,o=e.location,c=e.props,i=Object.assign({match:n,location:o,key:n.pathname},c||{});return Object.assign(Object.assign({},i),{breadcrumb:"string"==typeof r?t.createElement("span",{key:i.key},r):a.default.createElement(r,Object.assign({},i))})},f=function(e){var t,n=e.currentSection,a=e.disableDefaults,o=e.excludePaths,c=e.location,i=e.pathSection,u=e.branches;return o&&o.some((function(e){return null!=r.matchPath({path:e,end:!0},i)}))?h:(u.some((function(e){var o=e.path,u=e.routesMeta,s=u[u.length-1].route,f=s.breadcrumb;if(!f&&s.index){var p=u[u.length-2];p&&p.route.breadcrumb&&(f=p.route.breadcrumb)}var b=s.caseSensitive,m=s.props,v=r.matchPath({path:o,end:!0,caseSensitive:b},i);return v&&null===f?(t=h,!0):!!v&&(!f&&a?(t=h,!0):(t=d({breadcrumb:f||l(n),match:v,location:c,props:m}),!0))})),t||(a?h:function(e){var t=e.currentSection,n=e.location,a=e.pathSection,o=r.matchPath({end:!0,path:a},a);return d({breadcrumb:l(t),match:o,location:n})}({pathSection:i,currentSection:"/"===i?"Home":n,location:c})))},p=function(e){var t=e.routes,r=e.location,n=e.options,a=void 0===n?{}:n,o=r.pathname,c=function(e){return e.sort((function(e,t){return e.score!==t.score?t.score-e.score:function(e,t){return e.length===t.length&&e.slice(0,-1).every((function(e,r){return e===t[r]}))?e[e.length-1]-t[t.length-1]:0}(e.routesMeta.map((function(e){return e.childrenIndex})),t.routesMeta.map((function(e){return e.childrenIndex})))}))}(s(t)),i=[];return o.split("?")[0].split("/").reduce((function(e,t,n){var o=t?"".concat(e,"/").concat(t):"/";if("/"===o&&0!==n)return"";var u=f(Object.assign({currentSection:t,location:r,pathSection:o,branches:c},a));return u!==h&&i.push(u),"/"===o?"":o}),""),i};e.default=function(e,t){return p({routes:e||[],location:r.useLocation(),options:t})},e.getBreadcrumbs=p,Object.defineProperty(e,"__esModule",{value:!0})}));
{
"name": "use-react-router-breadcrumbs",
"version": "2.0.2",
"version": "3.0.0",
"description": "A hook for displaying and setting breadcrumbs for react router",

@@ -14,3 +14,3 @@ "main": "dist/cjs/index.js",

"react": ">=16.8",
"react-router": ">=5.1.0"
"react-router": ">=6.0.0"
},

@@ -40,3 +40,2 @@ "scripts": {

"@types/react-dom": "^17.0.7",
"@types/react-router": "^5.1.15",
"@typescript-eslint/eslint-plugin": "^4.26.1",

@@ -60,3 +59,3 @@ "@typescript-eslint/parser": "^4.26.1",

"react-dom": "^17.0.2",
"react-router": "^5.2.0",
"react-router": "^6.0.0",
"rollup": "^2.51.1",

@@ -72,4 +71,4 @@ "rollup-plugin-size": "^0.2.2",

"react-router",
"react-router 5"
"react-router 6"
]
}

@@ -54,3 +54,3 @@ <h3 align="center">

- Render, map, and wrap breadcrumbs any way you want.
- Compatible with existing [route configs](https://reacttraining.com/react-router/web/example/route-config).
- Compatible with existing [route objects](https://reactrouter.com/docs/en/v6/examples/route-objects).

@@ -118,4 +118,4 @@ ## Install

}) => (
<span key={match.url}>
<NavLink to={match.url}>{breadcrumb}</NavLink>
<span key={match.pathname}>
<NavLink to={match.pathname}>{breadcrumb}</NavLink>
</span>

@@ -136,5 +136,5 @@ ))}

## [Route config](https://reacttraining.com/react-router/web/example/route-config) compatibility
## [Route object](https://reactrouter.com/docs/en/v6/examples/route-objects) compatibility
Add breadcrumbs to your existing [route config](https://reacttraining.com/react-router/web/example/route-config). This is a great way to keep all routing config paths in a single place! If a path ever changes, you'll only have to change it in your main route config rather than maintaining a _separate_ config for `use-react-router-breadcrumbs`.
Add breadcrumbs to your existing [route object](https://reactrouter.com/docs/en/v6/examples/route-objects). This is a great way to keep all routing config paths in a single place! If a path ever changes, you'll only have to change it in your main route config rather than maintaining a _separate_ config for `use-react-router-breadcrumbs`.

@@ -144,6 +144,6 @@ For example...

```js
const routeConfig = [
const routes = [
{
path: "/sandwiches",
component: Sandwiches
element: <Sandwiches />
}

@@ -156,6 +156,6 @@ ];

```js
const routeConfig = [
const routes = [
{
path: "/sandwiches",
component: Sandwiches,
element: <Sandwiches />,
breadcrumb: 'I love sandwiches'

@@ -166,6 +166,6 @@ }

then you can just pass the whole route config right into the hook:
then you can just pass the whole routes right into the hook:
```js
const breadcrumbs = useBreadcrumbs(routeConfig);
const breadcrumbs = useBreadcrumbs(routes);
```

@@ -175,5 +175,5 @@

If you pass a component as the `breadcrumb` prop it will be injected with react-router's [match](https://reacttraining.com/react-router/web/api/match) and [location](https://reacttraining.com/react-router/web/api/location) objects as props. These objects contain ids, hashes, queries, etc... from the route that will allow you to map back to whatever you want to display in the breadcrumb.
If you pass a component as the `breadcrumb` prop it will be injected with react-router's [match](https://reactrouter.com/docs/en/v6/api#matchpath) and [location](https://reactrouter.com/docs/en/v6/api#location) objects as props. These objects contain ids, hashes, queries, etc... from the route that will allow you to map back to whatever you want to display in the breadcrumb.
Let's use `redux` as an example with the [match](https://reacttraining.com/react-router/web/api/match) object:
Let's use `redux` as an example with the [match](https://reactrouter.com/docs/en/v6/api#matchpath) object:

@@ -204,5 +204,15 @@ ```js

You cannot use hooks that rely on `RouteContext` like `useParams`, because the breadcrumbs are not in the context, you should use `match.params` instead:
```tsx
import type { BreadcrumbComponentType } from 'use-react-router-breadcrumbs';
const UserBreadcrumb: BreadcrumbComponentType<'id'> = ({ match }) => {
return <div>{match.params.id}</div>;
}
```
----
Similarly, the [location](https://reacttraining.com/react-router/web/api/location) object could be useful for displaying dynamic breadcrumbs based on the route's state:
Similarly, the [location](https://reactrouter.com/docs/en/v6/api#location) object could be useful for displaying dynamic breadcrumbs based on the route's state:

@@ -261,2 +271,4 @@ ```jsx

`use-react-router-breadcrumbs` uses the same strategy as `React Router v6` to calculate the routing order.
... in certain cases. Consider the following:

@@ -266,47 +278,68 @@

[
{ path: '/users/:id', breadcrumb: 'id-breadcrumb' },
{ path: '/users/create', breadcrumb: 'create-breadcrumb' },
{
path: 'users',
children: [
{ path: ':id', breadcrumb: 'id-breadcrumb' },
{ path: 'create', breadcrumb: 'create-breadcrumb' },
],
},
]
```
If the user visits `example.com/users/create` they will see `id-breadcrumb` because `/users/:id` will match _before_ `/users/create`.
If the user visits `example.com/users/create` they will see `create-breadcrumb`.
To fix the issue above, just adjust the order of your routes:
In addition, if the index route and the parent route provide breadcrumb at the same time, the index route provided will be used first:
```js
[
{ path: '/users/create', breadcrumb: 'create-breadcrumb' },
{ path: '/users/:id', breadcrumb: 'id-breadcrumb' },
{
path: 'users',
breadcrumb: 'parent-breadcrumb',
children: [
{ index: true, breadcrumb: 'child-breadcrumb' },
],
},
]
```
Now, `example.com/users/create` will display `create-breadcrumb` as expected, because it will match first before the `/users/:id` route.
If the user visits `example.com/users` they will see `child-breadcrumb`.
## API
```js
BreadcrumbsRoute = {
path: String
breadcrumb?: React.ComponentType | React.ElementType | string | null
// see: https://reacttraining.com/react-router/web/api/matchPath
matchOptions?: {
exact?: boolean
strict?: boolean
sensitive?: boolean
}
// optional nested routes (for react-router config compatibility)
routes?: BreadcrumbsRoute[],
// optional props to be passed through directly to the breadcrumb component
```ts
interface BreadcrumbComponentProps<ParamKey extends string = string> {
key: string;
match: BreadcrumbMatch<ParamKey>;
location: Location;
}
type BreadcrumbComponentType<ParamKey extends string = string> =
React.ComponentType<BreadcrumbComponentProps<ParamKey>>;
interface BreadcrumbsRoute<ParamKey extends string = string>
extends RouteObject {
children?: BreadcrumbsRoute[];
breadcrumb?: BreadcrumbComponentType<ParamKey> | string | null;
props?: { [x: string]: unknown };
}
Options = {
interface Options {
// disable all default generation of breadcrumbs
disableDefaults?: boolean
disableDefaults?: boolean;
// exclude certain paths fom generating breadcrumbs
excludePaths?: string[]
excludePaths?: string[];
}
interface BreadcrumbData<ParamKey extends string = string> {
match: BreadcrumbMatch<ParamKey>;
location: Location;
key: string;
breadcrumb: React.ReactNode;
}
// if routes are not passed, default breadcrumbs will be returned
useBreadcrumbs(routes?: BreadcrumbsRoute[], options?: Options): Array<React.node>
function useBreadcrumbs(
routes?: BreadcrumbsRoute[],
options?: Options
): BreadcrumbData[];
```
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc