settingsNavItem.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import type {ReactElement} from 'react';
  2. import {Fragment} from 'react';
  3. import {Link as RouterLink} from 'react-router';
  4. import {NavLink as Router6NavLink} from 'react-router-dom';
  5. import styled from '@emotion/styled';
  6. import type {LocationDescriptor} from 'history';
  7. import Badge from 'sentry/components/badge/badge';
  8. import FeatureBadge from 'sentry/components/badge/featureBadge';
  9. import HookOrDefault from 'sentry/components/hookOrDefault';
  10. import {Tooltip} from 'sentry/components/tooltip';
  11. import {t} from 'sentry/locale';
  12. import {space} from 'sentry/styles/space';
  13. import {locationDescriptorToTo} from 'sentry/utils/reactRouter6Compat/location';
  14. type Props = {
  15. label: React.ReactNode;
  16. to: LocationDescriptor;
  17. badge?: string | number | null | ReactElement;
  18. id?: string;
  19. index?: boolean;
  20. onClick?: (e: React.MouseEvent) => void;
  21. };
  22. function SettingsNavItem({badge, label, index, id, to, ...props}: Props) {
  23. const LabelHook = HookOrDefault({
  24. hookName: 'sidebar:item-label',
  25. defaultComponent: ({children}) => <Fragment>{children}</Fragment>,
  26. });
  27. let renderedBadge: React.ReactNode;
  28. if (badge === 'new') {
  29. renderedBadge = <FeatureBadge type="new" />;
  30. } else if (badge === 'beta') {
  31. renderedBadge = <FeatureBadge type="beta" />;
  32. } else if (badge === 'warning') {
  33. renderedBadge = (
  34. <Tooltip title={t('This setting needs review')} position="right">
  35. <StyledBadge text={badge} type="warning" />
  36. </Tooltip>
  37. );
  38. } else if (typeof badge === 'string' || typeof badge === 'number') {
  39. renderedBadge = <StyledBadge text={badge} />;
  40. } else {
  41. renderedBadge = badge;
  42. }
  43. if (window.__SENTRY_USING_REACT_ROUTER_SIX) {
  44. return (
  45. <StyledNavItem6 end={index} to={locationDescriptorToTo(to)} {...props}>
  46. <LabelHook id={id}>{label}</LabelHook>
  47. {badge ? renderedBadge : null}
  48. </StyledNavItem6>
  49. );
  50. }
  51. return (
  52. <StyledNavItem to={to} onlyActiveOnIndex={index} activeClassName="active" {...props}>
  53. <LabelHook id={id}>{label}</LabelHook>
  54. {badge ? renderedBadge : null}
  55. </StyledNavItem>
  56. );
  57. }
  58. const StyledNavItem6 = styled(Router6NavLink)`
  59. display: block;
  60. color: ${p => p.theme.gray300};
  61. font-size: 14px;
  62. line-height: 30px;
  63. position: relative;
  64. &.active {
  65. color: ${p => p.theme.textColor};
  66. &:before {
  67. background: ${p => p.theme.active};
  68. }
  69. }
  70. &:hover,
  71. &:focus,
  72. &:active {
  73. color: ${p => p.theme.textColor};
  74. outline: none;
  75. }
  76. &:focus-visible {
  77. outline: none;
  78. background: ${p => p.theme.backgroundSecondary};
  79. padding-left: 15px;
  80. margin-left: -15px;
  81. border-radius: 3px;
  82. &:before {
  83. left: -15px;
  84. }
  85. }
  86. &:before {
  87. position: absolute;
  88. content: '';
  89. display: block;
  90. top: 4px;
  91. left: -30px;
  92. height: 20px;
  93. width: 4px;
  94. background: transparent;
  95. border-radius: 0 2px 2px 0;
  96. }
  97. `;
  98. const StyledNavItem = styled(RouterLink)`
  99. display: block;
  100. color: ${p => p.theme.gray300};
  101. font-size: 14px;
  102. line-height: 30px;
  103. position: relative;
  104. &.active {
  105. color: ${p => p.theme.textColor};
  106. &:before {
  107. background: ${p => p.theme.active};
  108. }
  109. }
  110. &:hover,
  111. &:focus,
  112. &:active {
  113. color: ${p => p.theme.textColor};
  114. outline: none;
  115. }
  116. &:focus-visible {
  117. outline: none;
  118. background: ${p => p.theme.backgroundSecondary};
  119. padding-left: 15px;
  120. margin-left: -15px;
  121. border-radius: 3px;
  122. &:before {
  123. left: -15px;
  124. }
  125. }
  126. &:before {
  127. position: absolute;
  128. content: '';
  129. display: block;
  130. top: 4px;
  131. left: -30px;
  132. height: 20px;
  133. width: 4px;
  134. background: transparent;
  135. border-radius: 0 2px 2px 0;
  136. }
  137. `;
  138. const StyledBadge = styled(Badge)`
  139. font-weight: ${p => p.theme.fontWeightNormal};
  140. height: auto;
  141. line-height: 1;
  142. font-size: ${p => p.theme.fontSizeExtraSmall};
  143. padding: 3px ${space(0.75)};
  144. vertical-align: middle;
  145. `;
  146. export default SettingsNavItem;