react-router-breadcrumbs-hoc
Version:
small, flexible, higher order component for rendering breadcrumbs with react-router 4.x
205 lines (173 loc) • 6.21 kB
JavaScript
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
import React, { createElement } from 'react';
import { useLocation, matchPath } from 'react-router-dom';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
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.
***************************************************************************** */
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;
}
var DEFAULT_MATCH_OPTIONS = {
exact: true
};
var NO_BREADCRUMB = 'NO_BREADCRUMB';
var humanize = function humanize(str) {
return str.replace(/^[\s_]+|[\s_]+$/g, '').replace(/[_\s]+/g, ' ').replace(/^[a-z]/, function (m) {
return m.toUpperCase();
});
};
var render = function render(_a) {
var Breadcrumb = _a.breadcrumb,
match = _a.match,
location = _a.location,
rest = __rest(_a, ["breadcrumb", "match", "location"]);
var componentProps = Object.assign({
match: match,
location: location,
key: match.url
}, rest);
return Object.assign(Object.assign({}, componentProps), {
breadcrumb: typeof Breadcrumb === 'string' ? /*#__PURE__*/createElement('span', {
key: componentProps.key
}, Breadcrumb) : /*#__PURE__*/React.createElement(Breadcrumb, Object.assign({}, componentProps))
});
};
var getDefaultBreadcrumb = function getDefaultBreadcrumb(_ref) {
var currentSection = _ref.currentSection,
location = _ref.location,
pathSection = _ref.pathSection;
var match = matchPath(pathSection, Object.assign(Object.assign({}, DEFAULT_MATCH_OPTIONS), {
path: pathSection
})) || {
url: 'not-found'
};
return render({
breadcrumb: humanize(currentSection),
match: match,
location: location
});
};
var getBreadcrumbMatch = function getBreadcrumbMatch(_ref2) {
var currentSection = _ref2.currentSection,
disableDefaults = _ref2.disableDefaults,
excludePaths = _ref2.excludePaths,
location = _ref2.location,
pathSection = _ref2.pathSection,
routes = _ref2.routes;
var breadcrumb;
var getIsPathExcluded = function getIsPathExcluded(path) {
return matchPath(pathSection, {
path: path,
exact: true,
strict: false
});
};
if (excludePaths && excludePaths.some(getIsPathExcluded)) {
return NO_BREADCRUMB;
}
routes.some(function (_a) {
var userProvidedBreadcrumb = _a.breadcrumb,
matchOptions = _a.matchOptions,
path = _a.path,
rest = __rest(_a, ["breadcrumb", "matchOptions", "path"]);
if (!path) {
throw new Error('withBreadcrumbs: `path` must be provided in every route object');
}
var match = matchPath(pathSection, Object.assign(Object.assign({}, matchOptions || DEFAULT_MATCH_OPTIONS), {
path: path
}));
if (match && userProvidedBreadcrumb === null || !match && matchOptions) {
breadcrumb = NO_BREADCRUMB;
return true;
}
if (match) {
if (!userProvidedBreadcrumb && disableDefaults) {
breadcrumb = NO_BREADCRUMB;
return true;
}
breadcrumb = render(Object.assign({
breadcrumb: userProvidedBreadcrumb || humanize(currentSection),
match: match,
location: location
}, rest));
return true;
}
return false;
});
if (breadcrumb) {
return breadcrumb;
}
if (disableDefaults) {
return NO_BREADCRUMB;
}
return getDefaultBreadcrumb({
pathSection: pathSection,
currentSection: pathSection === '/' ? 'Home' : currentSection,
location: location
});
};
var getBreadcrumbs = function getBreadcrumbs(_ref3) {
var routes = _ref3.routes,
location = _ref3.location,
_ref3$options = _ref3.options,
options = _ref3$options === void 0 ? {} : _ref3$options;
var matches = [];
var pathname = location.pathname;
pathname.split('?')[0].split('/').reduce(function (previousSection, currentSection, index) {
var pathSection = !currentSection ? '/' : "".concat(previousSection, "/").concat(currentSection);
if (pathSection === '/' && index !== 0) {
return '';
}
var breadcrumb = getBreadcrumbMatch(Object.assign({
currentSection: currentSection,
location: location,
pathSection: pathSection,
routes: routes
}, options));
if (breadcrumb !== NO_BREADCRUMB) {
matches.push(breadcrumb);
}
return pathSection === '/' ? '' : pathSection;
}, '');
return matches;
};
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 withBreadcrumbs = function withBreadcrumbs(routes, options) {
return function (Component) {
return function (props) {
return /*#__PURE__*/React.createElement(Component, Object.assign(Object.assign({}, props), {
breadcrumbs: getBreadcrumbs({
options: options,
routes: flattenRoutes(routes || []),
location: useLocation()
})
}));
};
};
};
export default withBreadcrumbs;
export { getBreadcrumbs };