listLink.tsx 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import React from 'react';
  2. import {Link} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import classNames from 'classnames';
  5. import {LocationDescriptor} from 'history';
  6. import omit from 'lodash/omit';
  7. import PropTypes from 'prop-types';
  8. type DefaultProps = {
  9. index: boolean;
  10. activeClassName: string;
  11. disabled: boolean;
  12. };
  13. type Props = DefaultProps &
  14. React.ComponentProps<typeof Link> & {
  15. query?: string;
  16. // If supplied by parent component, decides whether link element
  17. // is "active" or not ... overriding default behavior of strict
  18. // route matching
  19. isActive?: (location: LocationDescriptor, indexOnly?: boolean) => boolean;
  20. };
  21. class ListLink extends React.Component<Props> {
  22. static displayName = 'ListLink';
  23. static contextTypes = {
  24. router: PropTypes.object.isRequired,
  25. };
  26. static defaultProps: DefaultProps = {
  27. activeClassName: 'active',
  28. index: false,
  29. disabled: false,
  30. };
  31. isActive = () => {
  32. const {isActive, to, query, index} = this.props;
  33. // TODO(ts) Removing context here results in a TypeError because `to` is not compatible
  34. // with LocationDescriptor.
  35. return (isActive || this.context.router.isActive)({pathname: to, query}, index);
  36. };
  37. getClassName = () => {
  38. const _classNames = {};
  39. const {className, activeClassName} = this.props;
  40. if (className) {
  41. _classNames[className] = true;
  42. }
  43. if (this.isActive()) {
  44. _classNames[activeClassName] = true;
  45. }
  46. return classNames(_classNames);
  47. };
  48. render() {
  49. const {index, children, to, disabled, ...props} = this.props;
  50. const carriedProps = omit(props, 'activeClassName', 'isActive', 'index');
  51. return (
  52. <StyledLi className={this.getClassName()} disabled={disabled}>
  53. <Link {...carriedProps} onlyActiveOnIndex={index} to={disabled ? '' : to}>
  54. {children}
  55. </Link>
  56. </StyledLi>
  57. );
  58. }
  59. }
  60. export default ListLink;
  61. const StyledLi = styled('li', {
  62. shouldForwardProp: prop => prop !== 'disabled',
  63. })<{disabled?: boolean}>`
  64. ${p =>
  65. p.disabled &&
  66. `
  67. a {
  68. color:${p.theme.disabled} !important;
  69. pointer-events: none;
  70. :hover {
  71. color: ${p.theme.disabled} !important;
  72. }
  73. }
  74. `}
  75. `;