listLink.tsx 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import * as React from 'react';
  2. import {Link, withRouter, WithRouterProps} 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 * as qs from 'query-string';
  8. type DefaultProps = {
  9. index: boolean;
  10. activeClassName: string;
  11. disabled: boolean;
  12. };
  13. type Props = WithRouterProps &
  14. Partial<DefaultProps> &
  15. React.ComponentProps<typeof Link> & {
  16. query?: string;
  17. // If supplied by parent component, decides whether link element
  18. // is "active" or not ... overriding default behavior of strict
  19. // route matching
  20. isActive?: (location: LocationDescriptor, indexOnly?: boolean) => boolean;
  21. };
  22. class ListLink extends React.Component<Props> {
  23. static displayName = 'ListLink';
  24. static defaultProps: DefaultProps = {
  25. activeClassName: 'active',
  26. index: false,
  27. disabled: false,
  28. };
  29. isActive() {
  30. const {isActive, to, query, index, location, router} = this.props;
  31. const queryData = query ? qs.parse(query) : undefined;
  32. const target: LocationDescriptor =
  33. typeof to === 'string'
  34. ? {pathname: to, query: queryData}
  35. : typeof to === 'function'
  36. ? to(location)
  37. : to;
  38. if (typeof isActive === 'function') {
  39. return isActive(target, index);
  40. }
  41. return router.isActive(target, index);
  42. }
  43. getClassName = () => {
  44. const _classNames = {};
  45. const {className, activeClassName} = this.props;
  46. if (className) {
  47. _classNames[className] = true;
  48. }
  49. if (this.isActive() && activeClassName) {
  50. _classNames[activeClassName] = true;
  51. }
  52. return classNames(_classNames);
  53. };
  54. render() {
  55. const {index, children, to, disabled, ...props} = this.props;
  56. const carriedProps = omit(
  57. props,
  58. 'activeClassName',
  59. 'css',
  60. 'isActive',
  61. 'index',
  62. 'router',
  63. 'location'
  64. );
  65. return (
  66. <StyledLi className={this.getClassName()} disabled={disabled}>
  67. <Link {...carriedProps} onlyActiveOnIndex={index} to={disabled ? '' : to}>
  68. {children}
  69. </Link>
  70. </StyledLi>
  71. );
  72. }
  73. }
  74. export default withRouter(ListLink);
  75. const StyledLi = styled('li', {
  76. shouldForwardProp: prop => prop !== 'disabled',
  77. })<{disabled?: boolean}>`
  78. ${p =>
  79. p.disabled &&
  80. `
  81. a {
  82. color:${p.theme.disabled} !important;
  83. pointer-events: none;
  84. :hover {
  85. color: ${p.theme.disabled} !important;
  86. }
  87. }
  88. `}
  89. `;