index.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import {Link as RouterLink} from 'react-router-dom';
  2. import styled from '@emotion/styled';
  3. import {t} from 'sentry/locale';
  4. import {space} from 'sentry/styles/space';
  5. import getRouteStringFromRoutes from 'sentry/utils/getRouteStringFromRoutes';
  6. import recreateRoute from 'sentry/utils/recreateRoute';
  7. import {useBreadcrumbsPathmap} from './context';
  8. import Crumb from './crumb';
  9. import Divider from './divider';
  10. import {OrganizationCrumb} from './organizationCrumb';
  11. import ProjectCrumb from './projectCrumb';
  12. import TeamCrumb from './teamCrumb';
  13. import type {RouteWithName} from './types';
  14. const MENUS = {
  15. Organization: OrganizationCrumb,
  16. Project: ProjectCrumb,
  17. Team: TeamCrumb,
  18. } as const;
  19. type Props = {
  20. params: {[param: string]: string | undefined};
  21. route: any;
  22. routes: RouteWithName[];
  23. className?: string;
  24. };
  25. function SettingsBreadcrumb({className, routes, params}: Props) {
  26. const pathMap = useBreadcrumbsPathmap();
  27. const lastRouteIndex = routes.map(r => !!r.name).lastIndexOf(true);
  28. return (
  29. <Breadcrumbs aria-label={t('Settings Breadcrumbs')} className={className}>
  30. {routes.map((route, i) => {
  31. if (!route.name) {
  32. return null;
  33. }
  34. const pathTitle = pathMap[getRouteStringFromRoutes(routes.slice(0, i + 1))];
  35. const isLast = i === lastRouteIndex;
  36. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  37. const createMenu = MENUS[route.name];
  38. const Menu = typeof createMenu === 'function' && createMenu;
  39. const hasMenu = !!Menu;
  40. if (hasMenu) {
  41. return (
  42. <Menu
  43. key={`${route.name}:${route.path}`}
  44. routes={routes}
  45. params={params}
  46. route={route}
  47. isLast={isLast}
  48. />
  49. );
  50. }
  51. return (
  52. <Crumb key={`${route.name}:${route.path}`}>
  53. <CrumbLink to={recreateRoute(route, {routes, params})}>
  54. {pathTitle || route.name}
  55. </CrumbLink>
  56. <Divider isLast={isLast} />
  57. </Crumb>
  58. );
  59. })}
  60. </Breadcrumbs>
  61. );
  62. }
  63. // Uses Link directly from react-router-dom to avoid the URL normalization
  64. // that happens in the internal Link component. It is unncessary because we
  65. // get routes from the router, and will actually cause issues because the
  66. // routes do not have organization information.
  67. const CrumbLink = styled(RouterLink)`
  68. display: block;
  69. color: ${p => p.theme.subText};
  70. &:hover {
  71. color: ${p => p.theme.textColor};
  72. }
  73. `;
  74. const Breadcrumbs = styled('nav')`
  75. display: flex;
  76. gap: ${space(0.75)};
  77. align-items: center;
  78. `;
  79. export {CrumbLink};
  80. export default SettingsBreadcrumb;