index.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import {Component} from 'react';
  2. import intersection from 'lodash/intersection';
  3. import isEqual from 'lodash/isEqual';
  4. import uniq from 'lodash/uniq';
  5. import xor from 'lodash/xor';
  6. import BulkNotice from './bulkNotice';
  7. type RenderProps = {
  8. /**
  9. * Are all rows on current page selected?
  10. */
  11. isPageSelected: boolean;
  12. /**
  13. * Callback for toggling all rows across all pages
  14. */
  15. onAllRowsToggle: (select: boolean) => void;
  16. /**
  17. * Callback for toggling all rows on current page
  18. */
  19. onPageRowsToggle: (select: boolean) => void;
  20. /**
  21. * Callback for toggling single row
  22. */
  23. onRowToggle: (id: string) => void;
  24. /**
  25. * Ready to be rendered summary component showing how many items are selected,
  26. * with buttons to select everything, cancel everything, etc...
  27. */
  28. renderBulkNotice: () => React.ReactNode;
  29. } & Pick<State, 'selectedIds' | 'isAllSelected'>;
  30. type State = {
  31. /**
  32. * Are all rows across all pages selected?
  33. */
  34. isAllSelected: boolean;
  35. /**
  36. * Selected ids on the current page
  37. */
  38. selectedIds: string[];
  39. };
  40. type Props = {
  41. /**
  42. * Number of all rows across all pages
  43. */
  44. allRowsCount: number;
  45. /**
  46. * Children with render props
  47. */
  48. children: (props: RenderProps) => React.ReactNode;
  49. /**
  50. * Number of grid columns to stretch the selection summary (used in BulkNotice)
  51. */
  52. columnsCount: number;
  53. /**
  54. * Array of ids on current page
  55. */
  56. pageIds: string[];
  57. /**
  58. * Maximum number of rows that can be bulk manipulated at once (used in BulkNotice)
  59. */
  60. bulkLimit?: number;
  61. /**
  62. * Array of default selected ids
  63. */
  64. defaultSelectedIds?: string[];
  65. /**
  66. * BulkController State
  67. */
  68. onChange?: (props: State) => void;
  69. };
  70. class BulkController extends Component<Props, State> {
  71. state: State = this.getInitialState();
  72. getInitialState() {
  73. const {defaultSelectedIds, pageIds} = this.props;
  74. return {
  75. selectedIds: intersection(defaultSelectedIds ?? [], pageIds),
  76. isAllSelected: false,
  77. };
  78. }
  79. static getDerivedStateFromProps(props: Readonly<Props>, state: State) {
  80. return {
  81. ...state,
  82. selectedIds: intersection(state.selectedIds, props.pageIds),
  83. };
  84. }
  85. componentDidUpdate(_prevProps: Props, prevState: State) {
  86. if (!isEqual(prevState, this.state)) {
  87. this.props.onChange?.(this.state);
  88. }
  89. }
  90. handleRowToggle = (id: string) => {
  91. this.setState(state => ({
  92. selectedIds: xor(state.selectedIds, [id]),
  93. isAllSelected: false,
  94. }));
  95. };
  96. handleAllRowsToggle = (select: boolean) => {
  97. const {pageIds} = this.props;
  98. this.setState({
  99. selectedIds: select ? [...pageIds] : [],
  100. isAllSelected: select,
  101. });
  102. };
  103. handlePageRowsToggle = (select: boolean) => {
  104. const {pageIds} = this.props;
  105. this.setState(state => ({
  106. selectedIds: select
  107. ? uniq([...state.selectedIds, ...pageIds])
  108. : state.selectedIds.filter(id => !pageIds.includes(id)),
  109. isAllSelected: false,
  110. }));
  111. };
  112. render() {
  113. const {pageIds, children, columnsCount, allRowsCount, bulkLimit} = this.props;
  114. const {selectedIds, isAllSelected} = this.state;
  115. const isPageSelected =
  116. pageIds.length > 0 && pageIds.every(id => selectedIds.includes(id));
  117. const renderProps: RenderProps = {
  118. selectedIds,
  119. isAllSelected,
  120. isPageSelected,
  121. onRowToggle: this.handleRowToggle,
  122. onAllRowsToggle: this.handleAllRowsToggle,
  123. onPageRowsToggle: this.handlePageRowsToggle,
  124. renderBulkNotice: () => (
  125. <BulkNotice
  126. allRowsCount={allRowsCount}
  127. selectedRowsCount={selectedIds.length}
  128. onUnselectAllRows={() => this.handleAllRowsToggle(false)}
  129. onSelectAllRows={() => this.handleAllRowsToggle(true)}
  130. columnsCount={columnsCount}
  131. isPageSelected={isPageSelected}
  132. isAllSelected={isAllSelected}
  133. bulkLimit={bulkLimit}
  134. />
  135. ),
  136. };
  137. return children(renderProps);
  138. }
  139. }
  140. export default BulkController;