listLink.tsx 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import {Link as RouterLink} from 'react-router';
  2. import styled from '@emotion/styled';
  3. import classNames from 'classnames';
  4. import {LocationDescriptor} from 'history';
  5. import * as qs from 'query-string';
  6. import useRouter from 'sentry/utils/useRouter';
  7. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  8. type LinkProps = Omit<React.ComponentProps<typeof RouterLink>, 'to'>;
  9. type Props = LinkProps & {
  10. /**
  11. * Link target. We don't want to expose the ToLocationFunction on this component.
  12. */
  13. to: LocationDescriptor;
  14. /**
  15. * The class to apply when the link is 'active'
  16. */
  17. activeClassName?: string;
  18. disabled?: boolean;
  19. index?: boolean;
  20. /**
  21. * Should be should be supplied by the parent component
  22. */
  23. isActive?: (location: LocationDescriptor, indexOnly?: boolean) => boolean;
  24. query?: string;
  25. };
  26. function ListLink({
  27. children,
  28. className,
  29. isActive,
  30. query,
  31. to,
  32. activeClassName = 'active',
  33. index = false,
  34. disabled = false,
  35. ...props
  36. }: Props) {
  37. const router = useRouter();
  38. const queryData = query ? qs.parse(query) : undefined;
  39. const targetLocation = typeof to === 'string' ? {pathname: to, query: queryData} : to;
  40. const target = normalizeUrl(targetLocation);
  41. const active = isActive?.(target, index) ?? router.isActive(target, index);
  42. return (
  43. <StyledLi
  44. className={classNames({[activeClassName]: active}, className)}
  45. disabled={disabled}
  46. >
  47. <RouterLink {...props} onlyActiveOnIndex={index} to={disabled ? '' : target}>
  48. {children}
  49. </RouterLink>
  50. </StyledLi>
  51. );
  52. }
  53. export default ListLink;
  54. const StyledLi = styled('li', {
  55. shouldForwardProp: prop => prop !== 'disabled',
  56. })<{disabled?: boolean}>`
  57. ${p =>
  58. p.disabled &&
  59. `
  60. a {
  61. color:${p.theme.disabled} !important;
  62. pointer-events: none;
  63. :hover {
  64. color: ${p.theme.disabled} !important;
  65. }
  66. }
  67. `}
  68. `;