You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ListFooter.tsx 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import styled from '@emotion/styled';
  21. import classNames from 'classnames';
  22. import { ButtonSecondary, Spinner, themeColor } from 'design-system';
  23. import * as React from 'react';
  24. import { translate, translateWithParameters } from '../../helpers/l10n';
  25. import { formatMeasure } from '../../helpers/measures';
  26. import { MetricType } from '../../types/metrics';
  27. import LegacySpinner from '../ui/Spinner';
  28. import { Button } from './buttons';
  29. export interface ListFooterProps {
  30. loadMoreAriaLabel?: string;
  31. count: number;
  32. className?: string;
  33. loading?: boolean;
  34. loadMore?: () => void;
  35. needReload?: boolean;
  36. pageSize?: number;
  37. reload?: () => void;
  38. ready?: boolean;
  39. total?: number;
  40. useMIUIButtons?: boolean;
  41. }
  42. export default function ListFooter(props: ListFooterProps) {
  43. const {
  44. loadMoreAriaLabel,
  45. className,
  46. count,
  47. loadMore,
  48. loading = false,
  49. needReload,
  50. total,
  51. pageSize,
  52. ready = true,
  53. useMIUIButtons = false,
  54. } = props;
  55. const rootNode = React.useRef<HTMLDivElement>(null);
  56. const onLoadMore = React.useCallback(() => {
  57. if (loadMore) {
  58. loadMore();
  59. }
  60. if (rootNode.current) {
  61. rootNode.current.focus();
  62. }
  63. }, [loadMore, rootNode]);
  64. let hasMore = false;
  65. if (total !== undefined) {
  66. hasMore = total > count;
  67. } else if (pageSize !== undefined) {
  68. hasMore = count % pageSize === 0;
  69. }
  70. let button;
  71. if (needReload && props.reload) {
  72. button = React.createElement(
  73. useMIUIButtons ? ButtonSecondary : Button,
  74. {
  75. 'data-test': 'reload',
  76. className: classNames('sw-ml-2', { 'sw-body-sm': useMIUIButtons }),
  77. disabled: loading,
  78. onClick: props.reload,
  79. } as Button['props'],
  80. translate('reload'),
  81. );
  82. } else if (hasMore && props.loadMore) {
  83. button = React.createElement(
  84. useMIUIButtons ? ButtonSecondary : Button,
  85. {
  86. 'aria-label': loadMoreAriaLabel,
  87. 'data-test': 'show-more',
  88. className: classNames('sw-ml-2', { 'sw-body-sm': useMIUIButtons }),
  89. disabled: loading,
  90. onClick: onLoadMore,
  91. } as Button['props'],
  92. translate('show_more'),
  93. );
  94. }
  95. return (
  96. <StyledDiv
  97. tabIndex={-1}
  98. ref={rootNode}
  99. className={classNames(
  100. 'list-footer', // .list-footer is only used by Selenium tests; we should find a way to remove it.
  101. 'sw-body-sm sw-flex sw-items-center sw-justify-center',
  102. { 'sw-opacity-50 sw-duration-500 sw-ease-in-out': !ready },
  103. className,
  104. )}
  105. >
  106. <span aria-live="polite" aria-busy={loading}>
  107. {total !== undefined
  108. ? translateWithParameters(
  109. 'x_of_y_shown',
  110. formatMeasure(count, MetricType.Integer),
  111. formatMeasure(total, MetricType.Integer),
  112. )
  113. : translateWithParameters('x_show', formatMeasure(count, MetricType.Integer))}
  114. </span>
  115. {button}
  116. {/* eslint-disable local-rules/no-conditional-rendering-of-deferredspinner */}
  117. {useMIUIButtons ? (
  118. <Spinner loading={loading} className="sw-ml-2" />
  119. ) : (
  120. <LegacySpinner loading={loading} className="sw-ml-2" />
  121. )}
  122. </StyledDiv>
  123. );
  124. }
  125. const StyledDiv = styled.div`
  126. color: ${themeColor('pageContentLight')};
  127. margin-top: 1rem /* 16px */;
  128. `;