PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 2.20.0
GiveWP – Donation Plugin and Fundraising Platform v2.20.0
4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / src / Views / Components / Table / index.js
give / src / Views / Components / Table Last commit date
index.js 4 years ago style.module.scss 4 years ago
index.js
174 lines
1 import {useState} from 'react';
2 import PropTypes from 'prop-types';
3 import classNames from 'classnames';
4 import {LoadingOverlay, Spinner} from '@givewp/components';
5
6 import styles from './style.module.scss';
7
8 import {__} from '@wordpress/i18n';
9
10 const Table = ({title, columns, data, columnFilters, stripped, isLoading, isSorting, ...rest}) => {
11 const [state, setState] = useState({});
12 const [cachedData, setCachedData] = useState([]);
13
14 Table.resetSortState = () => {
15 setState({});
16 };
17
18 // Clear cache if data is empty
19 if (!isLoading && !data.length && cachedData.length) {
20 setCachedData([]);
21 } else if (data.length && data !== cachedData) {
22 // Cache data so we can show that under overlay while new data is fetching
23 setCachedData(data);
24 }
25
26 const visibleColumns = columns.filter((column) => column.visible || column.visible === undefined);
27 const allowedColumns = visibleColumns.map((column) => column.key);
28 // Get additional row columns added manually
29 const additionalColumns = visibleColumns
30 .filter((column) => 'append' in column && column.append)
31 .map((column) => {
32 return {
33 [column.key]: column.defaultValue ?? '',
34 };
35 });
36
37 // Used when additional columns are added, but they not exist in the result row.
38 // So we have to sort result row to match columns order
39 const sortResults = (order, object) => {
40 const newObject = {};
41
42 order.forEach((key) => {
43 newObject[key] = object[key];
44 });
45 return newObject;
46 };
47
48 const handleItemSort = (item) => {
49 const direction = state[item.label] === 'desc' ? 'asc' : 'desc';
50
51 setState({[item.label]: direction});
52
53 return item.sortCallback(direction);
54 };
55
56 const getItemSortDirectionIcon = (item) => {
57 if (!state[item.label]) {
58 return (
59 <span className={classNames('dashicons dashicons-sort', styles.sortIcons, styles.sortIconUndefined)} />
60 );
61 }
62
63 const iconClasses = classNames(
64 'dashicons',
65 styles.sortIcons,
66 {'dashicons-arrow-down': state[item.label] === 'desc'},
67 {'dashicons-arrow-up': state[item.label] === 'asc'}
68 );
69
70 return <span className={iconClasses} />;
71 };
72
73 const getHeaderRow = () => {
74 return visibleColumns.map((item, index) => {
75 const columnStyles = item.styles ? {style: item.styles} : null;
76 return (
77 <div className={styles.label} key={index} {...columnStyles}>
78 {item.label}
79 {item.sort && typeof item.sortCallback === 'function' && (
80 <span onClick={() => handleItemSort(item)}>{getItemSortDirectionIcon(item)}</span>
81 )}
82 {isLoading && isSorting && state[item.label] !== undefined && (
83 <Spinner size="tiny" style={{marginLeft: 10}} />
84 )}
85 </div>
86 );
87 });
88 };
89
90 const getRows = () => {
91 if (!isLoading && data.length === 0) {
92 return <div className={styles.noData}>{__('No data', 'give')}</div>;
93 }
94
95 if (cachedData.length && isLoading) {
96 data = cachedData;
97 }
98
99 return data.map((row, index) => {
100 // Add additional row columns and sort the result row
101 const result =
102 additionalColumns.length > 0
103 ? sortResults(allowedColumns, Object.assign(row, ...additionalColumns))
104 : row;
105 const RowItems = Object.entries(result)
106 // Display only provided columns
107 .filter(([key]) => allowedColumns.includes(key))
108 .map(([key, value]) => {
109 if (columnFilters[key] && typeof columnFilters[key] === 'function') {
110 value = columnFilters[key](value, data[index]);
111 }
112
113 const currentColumn = visibleColumns.find((column) => column.key === key);
114 const columnStyles = currentColumn.styles ? {style: currentColumn.styles} : null;
115
116 return (
117 <div className={styles.item} key={key} {...columnStyles}>
118 {value}
119 </div>
120 );
121 });
122
123 const rowClasses = classNames(
124 'give-table-row',
125 {[styles.rowStripped]: stripped},
126 {[styles.row]: !stripped}
127 );
128
129 return (
130 <div className={rowClasses} key={index}>
131 {RowItems}
132 </div>
133 );
134 });
135 };
136
137 return (
138 <>
139 {isLoading && !isSorting && <LoadingOverlay spinnerSize="small" />}
140 {title && <div className={styles.title}>{title}</div>}
141 <div className={styles.table} {...rest}>
142 <div className={classNames(styles.header, {[styles.headerStripped]: stripped})}>{getHeaderRow()}</div>
143 {getRows()}
144 </div>
145 </>
146 );
147 };
148
149 Table.propTypes = {
150 // Table title
151 title: PropTypes.string,
152 // Columns to display
153 columns: PropTypes.array.isRequired,
154 // Table data rows
155 data: PropTypes.array.isRequired,
156 // Column filters
157 columnFilters: PropTypes.object,
158 // Stripped rows
159 stripped: PropTypes.bool,
160 // Show spinner if data is loading
161 isLoading: PropTypes.bool,
162 };
163
164 Table.defaultProps = {
165 title: null,
166 columns: [],
167 data: [],
168 columnFilters: {},
169 stripped: true,
170 isLoading: false,
171 };
172
173 export default Table;
174