TBody.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import { connect } from 'react-redux';
  4. import styled, { css } from 'styled-components';
  5. import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
  6. import TBodyShortUrl from './TBodyShortUrl';
  7. import TBodyCount from './TBodyCount';
  8. const TBody = styled.tbody`
  9. display: flex;
  10. flex: 1 1 auto;
  11. flex-direction: column;
  12. ${({ loading }) =>
  13. loading &&
  14. css`
  15. opacity: 0.2;
  16. `};
  17. tr:hover {
  18. background-color: #f8f8f8;
  19. td:after {
  20. background: linear-gradient(to left, #f8f8f8, #f8f8f8, transparent);
  21. }
  22. }
  23. `;
  24. const Td = styled.td`
  25. white-space: nowrap;
  26. overflow: hidden;
  27. ${({ withFade }) =>
  28. withFade &&
  29. css`
  30. :after {
  31. content: '';
  32. position: absolute;
  33. right: 0;
  34. top: 0;
  35. height: 100%;
  36. width: 56px;
  37. background: linear-gradient(to left, white, white, transparent);
  38. }
  39. `};
  40. :last-child {
  41. justify-content: space-between;
  42. }
  43. a {
  44. color: #2196f3;
  45. text-decoration: none;
  46. box-sizing: border-box;
  47. border-bottom: 1px dotted transparent;
  48. transition: all 0.2s ease-out;
  49. :hover {
  50. border-bottom-color: #2196f3;
  51. }
  52. }
  53. ${({ date }) =>
  54. date &&
  55. css`
  56. font-size: 15px;
  57. `};
  58. ${({ flex }) =>
  59. flex &&
  60. css`
  61. flex: ${`${flex} ${flex}`} 0;
  62. `};
  63. @media only screen and (max-width: 768px) {
  64. flex: 1;
  65. :nth-child(2) {
  66. display: none;
  67. }
  68. }
  69. @media only screen and (max-width: 510px) {
  70. :nth-child(1) {
  71. display: none;
  72. }
  73. }
  74. `;
  75. const TableBody = ({ copiedIndex, handleCopy, tableLoading, showModal, urls }) => {
  76. const showList = (url, index) => (
  77. <tr key={`tbody-${index}`}>
  78. <Td flex="2" withFade>
  79. <a href={url.target}>{url.target}</a>
  80. </Td>
  81. <Td flex="1" date>
  82. {`${distanceInWordsToNow(url.created_at)} ago`}
  83. </Td>
  84. <Td flex="1" withFade>
  85. <TBodyShortUrl index={index} copiedIndex={copiedIndex} handleCopy={handleCopy} url={url} />
  86. </Td>
  87. <Td flex="1">
  88. <TBodyCount url={url} showModal={showModal(url)} />
  89. </Td>
  90. </tr>
  91. );
  92. return (
  93. <TBody loading={tableLoading}>
  94. {urls.length ? (
  95. urls.map(showList)
  96. ) : (
  97. <tr>
  98. <Td>Nothing to show.</Td>
  99. </tr>
  100. )}
  101. </TBody>
  102. );
  103. };
  104. TableBody.propTypes = {
  105. urls: PropTypes.arrayOf(
  106. PropTypes.shape({
  107. id: PropTypes.string.isRequired,
  108. count: PropTypes.number,
  109. created_at: PropTypes.string.isRequired,
  110. password: PropTypes.bool,
  111. target: PropTypes.string.isRequired,
  112. })
  113. ).isRequired,
  114. copiedIndex: PropTypes.number.isRequired,
  115. showModal: PropTypes.func.isRequired,
  116. tableLoading: PropTypes.bool.isRequired,
  117. handleCopy: PropTypes.func.isRequired,
  118. };
  119. const mapStateToProps = ({ loading: { table: tableLoading } }) => ({ tableLoading });
  120. export default connect(mapStateToProps)(TableBody);