@arcblock/ux 2.1.7 → 2.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,10 +43,14 @@ var _useMediaQuery = _interopRequireDefault(require("@mui/material/useMediaQuery
43
43
 
44
44
  var _styles = require("@mui/material/styles");
45
45
 
46
+ var _LinearProgress = _interopRequireDefault(require("@mui/material/LinearProgress"));
47
+
46
48
  var _utils = require("./utils");
47
49
 
48
50
  var _TableSearch = _interopRequireDefault(require("./TableSearch"));
49
51
 
52
+ var _DatatableContext = require("./DatatableContext");
53
+
50
54
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
51
55
 
52
56
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -63,6 +67,12 @@ function CustomToolbar(props) {
63
67
  const moreBtn = (0, _react.useRef)(null);
64
68
  const isMobile = useMobile();
65
69
  const toolbarId = (0, _react.useRef)(Math.random().toString(32).slice(2));
70
+ const [searchOpened, setSearchOpened] = (0, _react.useState)(false);
71
+ const {
72
+ customButtons,
73
+ loading,
74
+ disabled
75
+ } = (0, _DatatableContext.useDatatableContext)();
66
76
  const {
67
77
  data,
68
78
  options,
@@ -78,9 +88,9 @@ function CustomToolbar(props) {
78
88
  title,
79
89
  searchText,
80
90
  searchTextUpdate,
81
- searchClose,
82
- customButtons
91
+ searchClose
83
92
  } = props;
93
+ const customToolbarEle = options.customToolbar ? options.customToolbar(props) : '';
84
94
  const {
85
95
  search,
86
96
  downloadCsv,
@@ -92,6 +102,11 @@ function CustomToolbar(props) {
92
102
  const hidePrint = options.print === false || options.print === 'false';
93
103
  const TableFilterComponent = components.TableFilter || _muiDatatables.TableFilter;
94
104
  const TableViewColComponent = components.TableViewCol || _muiDatatables.TableViewCol;
105
+ (0, _react.useEffect)(() => {
106
+ if (loading || disabled) {
107
+ setAllPopsEl({});
108
+ }
109
+ }, [loading, disabled]);
95
110
 
96
111
  const printArea = func => {
97
112
  return /*#__PURE__*/_react.default.createElement(_reactToPrint.default, {
@@ -101,7 +116,7 @@ function CustomToolbar(props) {
101
116
 
102
117
  const getPopId = key => "toolbar-pop-".concat(toolbarId.current, "-").concat(key);
103
118
 
104
- const defaultButtons = [];
119
+ const defaultButtons = []; // download/viewColumns/filter button behaviours, rendered using custom button logic
105
120
 
106
121
  if (!(options.download === false || options.download === 'false')) {
107
122
  defaultButtons.push({
@@ -156,7 +171,9 @@ function CustomToolbar(props) {
156
171
 
157
172
  const showMore = [!hidePrint, ...defaultButtons, ...customButtons].filter(e => !!e).length > 1 && isMobile;
158
173
  const allPops = [];
159
- const [allPopsEl, setAllPopsEl] = (0, _react.useState)({});
174
+ const [allPopsEl, setAllPopsEl] = (0, _react.useState)({}); // Large screens show the toolbar buttons directly, small screens show the drop-down menu style buttons
175
+ // The right-hand button of the form toolbar in desktop mode
176
+
160
177
  const toolbarButtons = [...defaultButtons, ...customButtons].map((e, index) => {
161
178
  if ( /*#__PURE__*/(0, _react.isValidElement)(e)) {
162
179
  return e;
@@ -168,7 +185,7 @@ function CustomToolbar(props) {
168
185
  const {
169
186
  Icon,
170
187
  popRender
171
- } = e;
188
+ } = e; // When popRender is present, clicking the button will bubble up the content returned by the popRender
172
189
 
173
190
  if (popRender) {
174
191
  allPops.push( /*#__PURE__*/_react.default.createElement(_Popover.default, {
@@ -198,6 +215,7 @@ function CustomToolbar(props) {
198
215
  }
199
216
 
200
217
  if (popRender) {
218
+ // On the large screen, the bubble is positioned at the corresponding button
201
219
  setAllPopsEl({
202
220
  [popId]: document.getElementById("btn-".concat(popId))
203
221
  });
@@ -207,7 +225,8 @@ function CustomToolbar(props) {
207
225
  }
208
226
 
209
227
  return e;
210
- });
228
+ }); // The toolbar menu in the mobile to replace toolbarButtons
229
+
211
230
  const menuItems = [...defaultButtons, ...customButtons].map((e, index) => {
212
231
  const popId = getPopId(index);
213
232
  let content;
@@ -233,6 +252,7 @@ function CustomToolbar(props) {
233
252
  }
234
253
 
235
254
  if (e.popRender) {
255
+ // On the small screen, the bubbles are positioned at the three dot buttons
236
256
  setAllPopsEl({
237
257
  [popId]: moreBtn.current
238
258
  });
@@ -241,20 +261,18 @@ function CustomToolbar(props) {
241
261
  }, content);
242
262
  });
243
263
  return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(Container, null, /*#__PURE__*/_react.default.createElement("div", {
244
- className: "custom-toobar-title"
264
+ className: "custom-toobar-title ".concat(isMobile && searchOpened && /*#__PURE__*/(0, _react.isValidElement)(title) ? 'toobar-title-hidden' : '')
245
265
  }, /*#__PURE__*/_react.default.createElement("div", {
246
266
  className: "custom-toobar-title-inner"
247
267
  }, /*#__PURE__*/_react.default.createElement("span", null, title))), /*#__PURE__*/_react.default.createElement("div", {
248
- className: "custom-toobar-right"
249
- }, /*#__PURE__*/_react.default.createElement("div", {
250
- className: "custom-toobar-btns"
268
+ className: "custom-toobar-btns ".concat(loading || disabled ? 'toobar-btns-disabled' : '')
251
269
  }, !hideSearch && /*#__PURE__*/_react.default.createElement(_TableSearch.default, {
252
270
  search: search,
253
271
  options: options,
254
272
  searchText: searchText,
255
273
  searchTextUpdate: searchTextUpdate,
256
274
  searchClose: searchClose,
257
- isMobile: isMobile
275
+ onSearchOpen: setSearchOpened
258
276
  }), !showMore && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, !hidePrint && printArea(_ref => {
259
277
  let {
260
278
  handlePrint
@@ -271,8 +289,11 @@ function CustomToolbar(props) {
271
289
  ref: moreBtn,
272
290
  "aria-haspopup": "true",
273
291
  "aria-expanded": menuIconEl ? 'true' : undefined,
274
- onClick: event => setMenuIconEl(event.currentTarget)
275
- }, /*#__PURE__*/_react.default.createElement(_MoreVert.default, null))))), /*#__PURE__*/_react.default.createElement(_Menu.default, {
292
+ onClick: event => setMenuIconEl(event.currentTarget),
293
+ style: {
294
+ flexShrink: 0
295
+ }
296
+ }, /*#__PURE__*/_react.default.createElement(_MoreVert.default, null))), customToolbarEle), /*#__PURE__*/_react.default.createElement(_Menu.default, {
276
297
  anchorEl: menuIconEl,
277
298
  open: !!menuIconEl,
278
299
  onClose: () => setMenuIconEl(null),
@@ -293,7 +314,7 @@ function CustomToolbar(props) {
293
314
  })), /*#__PURE__*/_react.default.createElement(_ListItemText.default, null, print));
294
315
  }), menuItems), allPops.map((e, index) => /*#__PURE__*/_react.default.createElement("div", {
295
316
  key: getPopId(index)
296
- }, e)));
317
+ }, e)), loading && /*#__PURE__*/_react.default.createElement(_LinearProgress.default, null));
297
318
  }
298
319
 
299
320
  CustomToolbar.propTypes = {
@@ -308,12 +329,11 @@ CustomToolbar.propTypes = {
308
329
  updateFilterByType: _propTypes.default.func.isRequired,
309
330
  toggleViewColumn: _propTypes.default.func.isRequired,
310
331
  updateColumns: _propTypes.default.func.isRequired,
311
- title: _propTypes.default.string,
332
+ title: _propTypes.default.any,
312
333
  searchText: _propTypes.default.any,
313
334
  searchTextUpdate: _propTypes.default.func.isRequired,
314
335
  searchClose: _propTypes.default.func.isRequired,
315
- tableRef: _propTypes.default.func.isRequired,
316
- customButtons: _propTypes.default.array.isRequired
336
+ tableRef: _propTypes.default.func.isRequired
317
337
  };
318
338
  CustomToolbar.defaultProps = {
319
339
  data: [],
@@ -327,4 +347,4 @@ CustomToolbar.defaultProps = {
327
347
  const Container = _styledComponents.default.div.withConfig({
328
348
  displayName: "CustomToolbar__Container",
329
349
  componentId: "sc-cvz3dp-0"
330
- })(["display:flex;align-items:center;height:56px;.custom-toobar{&-title{position:relative;flex:1;font-size:18px;font-weight:800;height:56px;&-inner{line-height:56px;width:100%;height:56px;position:absolute;left:0;top:0;span{display:inline-block;max-width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}}}&-right{display:flex;margin-left:auto;}&-btns{display:flex;justify-content:center;align-items:center;}}"]);
350
+ })(["display:flex;align-items:center;height:56px;.custom-toobar-title{position:relative;flex:1;font-size:18px;font-weight:800;height:56px;transition:all ease 0.3s;&-inner{line-height:56px;width:100%;height:56px;position:absolute;left:0;top:0;span{display:inline-block;max-width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}}}.custom-toobar-btns{display:flex;justify-content:center;align-items:center;&.toobar-btns-disabled{position:relative;opacity:0.5;&:after{position:absolute;display:block;z-index:2;width:100%;height:100%;left:0;top:0;content:'';cursor:not-allowed;}}}.toobar-title-hidden{opacity:0;cursor:none;}"]);
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.DatatableProvide = void 0;
7
+ exports.useDatatableContext = useDatatableContext;
8
+
9
+ var _react = _interopRequireWildcard(require("react"));
10
+
11
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
12
+
13
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
14
+
15
+ const DatatableContext = /*#__PURE__*/(0, _react.createContext)({});
16
+ const {
17
+ Provider
18
+ } = DatatableContext; // eslint-disable-next-line react/prop-types
19
+
20
+ const DatatableProvide = _ref => {
21
+ let {
22
+ children
23
+ } = _ref;
24
+ const [customButtons, setCustomButtons] = (0, _react.useState)([]);
25
+ const [loading, setLoading] = (0, _react.useState)(false);
26
+ const [disabled, setDisabled] = (0, _react.useState)(false);
27
+ const [filterLabel, setFilterLabel] = (0, _react.useState)('Filter');
28
+ const value = {
29
+ customButtons,
30
+ setCustomButtons,
31
+ filterLabel,
32
+ setFilterLabel,
33
+ loading,
34
+ setLoading,
35
+ disabled,
36
+ setDisabled
37
+ };
38
+ return /*#__PURE__*/_react.default.createElement(Provider, {
39
+ value: value
40
+ }, children);
41
+ };
42
+
43
+ exports.DatatableProvide = DatatableProvide;
44
+
45
+ function useDatatableContext() {
46
+ return (0, _react.useContext)(DatatableContext);
47
+ }
@@ -34,7 +34,6 @@ function TableSearch(_ref) {
34
34
  searchText,
35
35
  searchTextUpdate,
36
36
  searchClose,
37
- isMobile,
38
37
  onSearchOpen
39
38
  } = _ref;
40
39
  const [inputMode, setInputMode] = (0, _react.useState)(false);
@@ -55,7 +54,7 @@ function TableSearch(_ref) {
55
54
  };
56
55
 
57
56
  return /*#__PURE__*/_react.default.createElement(Container, null, inputMode ? /*#__PURE__*/_react.default.createElement("div", {
58
- className: "toolbar-search-icon-placeholder-"
57
+ className: "toolbar-search-icon-placeholder"
59
58
  }, /*#__PURE__*/_react.default.createElement(_Search.default, null)) : /*#__PURE__*/_react.default.createElement(_Tooltip.default, {
60
59
  title: search,
61
60
  disableFocusListener: true
@@ -65,7 +64,7 @@ function TableSearch(_ref) {
65
64
  disabled: options.search === 'disabled',
66
65
  onClick: clickSearchIcon
67
66
  }, /*#__PURE__*/_react.default.createElement(_Search.default, null))), /*#__PURE__*/_react.default.createElement("div", {
68
- className: "toolbar-search-area ".concat(inputMode ? 'toolbar-btn-show' : '', " ").concat(isMobile ? 'small-textfield' : '')
67
+ className: "toolbar-search-area ".concat(inputMode ? 'toolbar-btn-show' : '')
69
68
  }, inputMode && /*#__PURE__*/_react.default.createElement(_TextField.default, {
70
69
  variant: "standard",
71
70
  spacing: 2,
@@ -85,8 +84,7 @@ TableSearch.propTypes = {
85
84
  onSearchOpen: _propTypes.default.func,
86
85
  options: _propTypes.default.object.isRequired,
87
86
  searchTextUpdate: _propTypes.default.func.isRequired,
88
- searchClose: _propTypes.default.func.isRequired,
89
- isMobile: _propTypes.default.bool.isRequired
87
+ searchClose: _propTypes.default.func.isRequired
90
88
  };
91
89
  TableSearch.defaultProps = {
92
90
  search: '',
@@ -97,4 +95,4 @@ TableSearch.defaultProps = {
97
95
  const Container = _styledComponents.default.div.withConfig({
98
96
  displayName: "TableSearch__Container",
99
97
  componentId: "sc-43ylue-0"
100
- })(["display:flex;align-items:center;.toolbar-search-area{width:0;transition:all ease 0.3s;overflow:hidden;.MuiFormControl-root{width:inherit;margin:0 12px;}&.toolbar-btn-show{width:260px;padding-left:8px;&.small-textfield{width:200px;}}}.toolbar-search-close{width:0;transition:all ease 0.3s;overflow:hidden;&.toolbar-btn-show{width:40px;}}.toolbar-search-icon-placeholder{display:flex;justify-content:center;align-items:center;width:40px;height:40px;}"]);
98
+ })(["display:flex;align-items:center;.toolbar-search-area{width:0;transition:all ease 0.3s;overflow:hidden;.MuiFormControl-root{width:inherit;margin:0 12px;}&.toolbar-btn-show{width:260px;padding-left:8px;", "{width:200px;}", "{width:180px;}&.small-textfield{width:200px;}}}.toolbar-search-close{width:0;transition:all ease 0.3s;overflow:hidden;&.toolbar-btn-show{width:40px;}}.toolbar-search-icon-placeholder{display:flex;justify-content:center;align-items:center;width:40px;height:40px;}"], props => props.theme.breakpoints.down('md'), props => props.theme.breakpoints.down('sm'));
@@ -13,9 +13,15 @@ var _muiDatatables = _interopRequireWildcard(require("mui-datatables"));
13
13
 
14
14
  var _styledComponents = _interopRequireDefault(require("styled-components"));
15
15
 
16
+ var _isObject = _interopRequireDefault(require("lodash/isObject"));
17
+
18
+ var _cloneDeep = _interopRequireDefault(require("lodash/cloneDeep"));
19
+
16
20
  var _CustomToolbar = _interopRequireDefault(require("./CustomToolbar"));
17
21
 
18
- const _excluded = ["locale", "options", "style", "customButtons"];
22
+ var _DatatableContext = require("./DatatableContext");
23
+
24
+ const _excluded = ["data", "columns", "locale", "options", "style", "customButtons", "onChange", "loading", "disabled"];
19
25
 
20
26
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21
27
 
@@ -33,37 +39,104 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
33
39
 
34
40
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
35
41
 
36
- const WrapFilterList = props => {
37
- const hasFilter = !!props.filterList.filter(e => e.length).length;
38
-
39
- if (hasFilter) {
40
- return /*#__PURE__*/_react.default.createElement(FilterLine, null, hasFilter && /*#__PURE__*/_react.default.createElement("div", {
41
- className: "toolbar-filter-title"
42
- }, "Filter"), /*#__PURE__*/_react.default.createElement("div", {
43
- className: "toolbar-filter-content"
44
- }, /*#__PURE__*/_react.default.createElement(_muiDatatables.TableFilterList, props)));
45
- }
46
-
47
- return '';
48
- };
42
+ function Datatable(_ref) {
43
+ let props = Object.assign({}, _ref);
44
+ return /*#__PURE__*/_react.default.createElement(_DatatableContext.DatatableProvide, null, /*#__PURE__*/_react.default.createElement(ReDatatable, props));
45
+ }
46
+ /**
47
+ * @param {Object} props.options The options of mui-datatable,detail see https://github.com/gregnb/mui-datatables/tree/b8d2eee6af4589d254b40918e5d7e70b1ee4baca
48
+ * @param {Array} props.customButtons Custom buttons for toolbar
49
+ * @param {Function} props.onChange When onChange is present, serverSide mode is activated by default https://github.com/gregnb/mui-datatables/tree/b8d2eee6af4589d254b40918e5d7e70b1ee4baca#remote-data
50
+ * @param {Boolean} props.loading For dynamic data, usually used with onChange
51
+ * @returns
52
+ */
49
53
 
50
- WrapFilterList.propTypes = {
51
- filterList: _propTypes.default.array
52
- };
53
- WrapFilterList.defaultProps = {
54
- filterList: []
55
- };
56
54
 
57
- function Datatable(_ref) {
55
+ function ReDatatable(_ref2) {
58
56
  let {
57
+ data: originData,
58
+ columns: originColumns,
59
59
  locale,
60
60
  options,
61
61
  style,
62
- customButtons
63
- } = _ref,
64
- rest = _objectWithoutProperties(_ref, _excluded);
62
+ customButtons,
63
+ onChange,
64
+ loading,
65
+ disabled
66
+ } = _ref2,
67
+ rest = _objectWithoutProperties(_ref2, _excluded);
65
68
 
66
69
  const container = (0, _react.useRef)(null);
70
+ const oldState = (0, _react.useRef)(null);
71
+ const {
72
+ setCustomButtons,
73
+ setFilterLabel,
74
+ setLoading,
75
+ setDisabled
76
+ } = (0, _DatatableContext.useDatatableContext)();
77
+ const disabledCellStyle = {
78
+ cursor: 'not-allowed',
79
+ pointerEvents: 'none'
80
+ };
81
+ const keys = []; // Convert Columns fields to object sets to support the width function
82
+
83
+ const columns = originColumns.map(e => {
84
+ let tempObj;
85
+
86
+ if (!(0, _isObject.default)(e)) {
87
+ tempObj = {
88
+ label: e,
89
+ name: e
90
+ };
91
+ } else {
92
+ tempObj = (0, _cloneDeep.default)(e);
93
+ }
94
+
95
+ keys.push(tempObj.name);
96
+
97
+ if (!tempObj.options) {
98
+ tempObj.options = {};
99
+ }
100
+
101
+ const {
102
+ setCellHeaderProps
103
+ } = tempObj.options;
104
+
105
+ tempObj.options.setCellHeaderProps = columnMeta => {
106
+ let cellProps = {}; // Complementing width while inheriting old setCellHeaderProps
107
+
108
+ if (setCellHeaderProps && !setCellHeaderProps.__innerFunc) {
109
+ cellProps = setCellHeaderProps(columnMeta) || {};
110
+ }
111
+
112
+ if (loading || disabled) {
113
+ cellProps = _objectSpread(_objectSpread({}, cellProps), {}, {
114
+ style: disabledCellStyle
115
+ });
116
+ }
117
+
118
+ if (tempObj.width) {
119
+ cellProps.width = tempObj.width;
120
+ }
121
+
122
+ return cellProps;
123
+ }; // Prevent memory xie caused by recursive forwarding of setCellHeaderProps functions
124
+
125
+
126
+ tempObj.options.setCellHeaderProps.__innerFunc = 1;
127
+ return tempObj;
128
+ }); // Fixing object-type structures
129
+
130
+ const data = originData.map(e => {
131
+ if (!Array.isArray(e) && (0, _isObject.default)(e)) {
132
+ return keys.map(key => e[key]);
133
+ }
134
+
135
+ return e;
136
+ });
137
+ (0, _react.useEffect)(() => setCustomButtons(customButtons || []), [customButtons]);
138
+ (0, _react.useEffect)(() => setLoading(loading), [loading]);
139
+ (0, _react.useEffect)(() => setDisabled(disabled), [disabled]);
67
140
  let textLabels = {
68
141
  body: {
69
142
  noMatch: 'Sorry, no matching records found',
@@ -136,42 +209,83 @@ function Datatable(_ref) {
136
209
  };
137
210
  }
138
211
 
212
+ (0, _react.useEffect)(() => setFilterLabel(textLabels.filter.title), [textLabels.filter.title]);
213
+
139
214
  const opts = _objectSpread({
140
215
  selectableRows: 'none',
141
- textLabels
216
+ textLabels,
217
+ rowsPerPage: 10,
218
+ rowsPerPageOptions: [10, 20, 50]
142
219
  }, options);
143
220
 
144
- const WrapCustomToolBar = props => {
145
- return /*#__PURE__*/_react.default.createElement(_CustomToolbar.default, Object.assign({}, props, {
146
- customButtons: customButtons || []
147
- }));
148
- };
221
+ if (onChange) {
222
+ Object.assign(opts, {
223
+ serverSide: true,
224
+ // Wrap the more friendly onChange callback by listening to onTableChange,
225
+ // which will only be triggered when the table key state changes
226
+ onTableChange: (action, tableState) => {
227
+ if (action === 'propsUpdate') {
228
+ return;
229
+ }
230
+
231
+ const state = {
232
+ count: tableState.count,
233
+ page: tableState.page,
234
+ rowsPerPage: tableState.rowsPerPage,
235
+ searchText: tableState.searchText,
236
+ sortOrder: tableState.sortOrder,
237
+ //
238
+ filterList: tableState.filterList
239
+ };
240
+ const stateStr = JSON.stringify(state);
241
+
242
+ if (stateStr === oldState.current) {
243
+ return;
244
+ }
245
+
246
+ oldState.current = stateStr;
247
+ onChange(state, action);
248
+ }
249
+ });
250
+ }
149
251
 
150
252
  const props = _objectSpread(_objectSpread({
151
253
  options: opts
152
254
  }, rest), {}, {
153
255
  components: {
154
- TableToolbar: WrapCustomToolBar,
256
+ TableToolbar: _CustomToolbar.default,
257
+ TableFooter: WrapTableFooter,
155
258
  TableFilterList: WrapFilterList
156
259
  }
157
260
  });
158
261
 
159
- Datatable.propTypes = {
262
+ ReDatatable.propTypes = {
263
+ data: _propTypes.default.array.isRequired,
264
+ columns: _propTypes.default.array.isRequired,
160
265
  options: _propTypes.default.object,
161
266
  style: _propTypes.default.object,
162
267
  locale: _propTypes.default.string,
163
- customButtons: _propTypes.default.array
268
+ loading: _propTypes.default.bool,
269
+ disabled: _propTypes.default.bool,
270
+ customButtons: _propTypes.default.array,
271
+ onChange: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.string])
164
272
  };
165
- Datatable.defaultProps = {
273
+ ReDatatable.defaultProps = {
166
274
  options: {},
167
275
  style: {},
168
276
  locale: 'en',
169
- customButtons: []
277
+ loading: false,
278
+ disabled: false,
279
+ customButtons: [],
280
+ onChange: ''
170
281
  };
171
282
  return /*#__PURE__*/_react.default.createElement(TableContainer, {
172
283
  ref: container,
173
284
  style: style
174
- }, /*#__PURE__*/_react.default.createElement(_muiDatatables.default, props));
285
+ }, /*#__PURE__*/_react.default.createElement(_muiDatatables.default, Object.assign({
286
+ data: data,
287
+ columns: columns
288
+ }, props)));
175
289
  }
176
290
 
177
291
  const TableContainer = _styledComponents.default.div.withConfig({
@@ -182,4 +296,43 @@ const TableContainer = _styledComponents.default.div.withConfig({
182
296
  const FilterLine = _styledComponents.default.div.withConfig({
183
297
  displayName: "Datatable__FilterLine",
184
298
  componentId: "sc-1ju12vq-1"
185
- })(["display:flex;align-items:center;.toolbar-filter-content{margin-bottom:8px;}.toolbar-filter-title{font-weight:700;font-size:14px;}"]);
299
+ })(["display:flex;align-items:center;.toolbar-filter-content{margin-bottom:8px;}.toolbar-filter-title{font-weight:700;font-size:14px;}"]);
300
+
301
+ const WrapFilterList = props => {
302
+ const {
303
+ filterLabel
304
+ } = (0, _DatatableContext.useDatatableContext)();
305
+ const hasFilter = !!props.filterList.filter(e => e.length).length;
306
+
307
+ if (hasFilter) {
308
+ return /*#__PURE__*/_react.default.createElement(FilterLine, null, hasFilter && /*#__PURE__*/_react.default.createElement("div", {
309
+ className: "toolbar-filter-title"
310
+ }, filterLabel), /*#__PURE__*/_react.default.createElement("div", {
311
+ className: "toolbar-filter-content"
312
+ }, /*#__PURE__*/_react.default.createElement(_muiDatatables.TableFilterList, props)));
313
+ }
314
+
315
+ return '';
316
+ };
317
+
318
+ WrapFilterList.propTypes = {
319
+ filterList: _propTypes.default.array
320
+ };
321
+ WrapFilterList.defaultProps = {
322
+ filterList: []
323
+ };
324
+
325
+ const WrapTableFooter = props => {
326
+ const {
327
+ loading,
328
+ disabled
329
+ } = (0, _DatatableContext.useDatatableContext)();
330
+ return /*#__PURE__*/_react.default.createElement(FooterContainer, null, /*#__PURE__*/_react.default.createElement("div", {
331
+ className: "datatable-footer ".concat(loading || disabled ? 'datatable-footer-disabled' : '')
332
+ }, /*#__PURE__*/_react.default.createElement(_muiDatatables.TableFooter, props)));
333
+ };
334
+
335
+ const FooterContainer = _styledComponents.default.div.withConfig({
336
+ displayName: "Datatable__FooterContainer",
337
+ componentId: "sc-1ju12vq-2"
338
+ })(["display:flex;align-items:center;.datatable-footer{position:relative;margin-left:auto;&.datatable-footer-disabled{position:relative;.MuiTablePagination-root{opacity:0.6;}&:after{position:absolute;display:block;z-index:2;width:100%;height:100%;left:0;top:0;content:'';cursor:not-allowed;}}}"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcblock/ux",
3
- "version": "2.1.7",
3
+ "version": "2.1.8",
4
4
  "description": "Common used react components for arcblock products",
5
5
  "keywords": [
6
6
  "react",
@@ -52,10 +52,10 @@
52
52
  "react": ">=18.1.0",
53
53
  "react-ga": "^2.7.0"
54
54
  },
55
- "gitHead": "a54ef3a9335aae1bc7404b5f4a8c9c508b6ff81f",
55
+ "gitHead": "3c91cd679fef62a21839887f72870b43833c7d39",
56
56
  "dependencies": {
57
- "@arcblock/icons": "^2.1.7",
58
- "@arcblock/react-hooks": "^2.1.7",
57
+ "@arcblock/icons": "^2.1.8",
58
+ "@arcblock/react-hooks": "^2.1.8",
59
59
  "@babel/plugin-syntax-dynamic-import": "^7.8.3",
60
60
  "@emotion/react": "^11.9.0",
61
61
  "@emotion/styled": "^11.8.1",
@@ -1,4 +1,4 @@
1
- import React, { useState, useRef, isValidElement } from 'react';
1
+ import React, { useState, useRef, useEffect, isValidElement } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { TableFilter, TableViewCol } from 'mui-datatables';
4
4
  import styled from 'styled-components';
@@ -17,8 +17,10 @@ import ListItemIcon from '@mui/material/ListItemIcon';
17
17
  import ListItemText from '@mui/material/ListItemText';
18
18
  import useMediaQuery from '@mui/material/useMediaQuery';
19
19
  import { useTheme } from '@mui/material/styles';
20
+ import LinearProgress from '@mui/material/LinearProgress';
20
21
  import { handleCSVDownload } from './utils';
21
22
  import TableSearch from './TableSearch';
23
+ import { useDatatableContext } from './DatatableContext';
22
24
 
23
25
  function useMobile() {
24
26
  const theme = useTheme();
@@ -30,6 +32,8 @@ export default function CustomToolbar(props) {
30
32
  const moreBtn = useRef(null);
31
33
  const isMobile = useMobile();
32
34
  const toolbarId = useRef(Math.random().toString(32).slice(2));
35
+ const [searchOpened, setSearchOpened] = useState(false);
36
+ const { customButtons, loading, disabled } = useDatatableContext();
33
37
 
34
38
  const {
35
39
  data,
@@ -47,9 +51,10 @@ export default function CustomToolbar(props) {
47
51
  searchText,
48
52
  searchTextUpdate,
49
53
  searchClose,
50
- customButtons,
51
54
  } = props;
52
55
 
56
+ const customToolbarEle = options.customToolbar ? options.customToolbar(props) : '';
57
+
53
58
  const { search, downloadCsv, print, viewColumns, filterTable } = options.textLabels.toolbar;
54
59
 
55
60
  const hideSearch = options.search === false || options.search === 'false';
@@ -58,6 +63,12 @@ export default function CustomToolbar(props) {
58
63
  const TableFilterComponent = components.TableFilter || TableFilter;
59
64
  const TableViewColComponent = components.TableViewCol || TableViewCol;
60
65
 
66
+ useEffect(() => {
67
+ if (loading || disabled) {
68
+ setAllPopsEl({});
69
+ }
70
+ }, [loading, disabled]);
71
+
61
72
  const printArea = func => {
62
73
  return (
63
74
  <ReactToPrint content={() => props.tableRef()}>
@@ -70,6 +81,7 @@ export default function CustomToolbar(props) {
70
81
 
71
82
  const defaultButtons = [];
72
83
 
84
+ // download/viewColumns/filter button behaviours, rendered using custom button logic
73
85
  if (!(options.download === false || options.download === 'false')) {
74
86
  defaultButtons.push({
75
87
  Icon: DownloadIcon,
@@ -127,6 +139,8 @@ export default function CustomToolbar(props) {
127
139
  const allPops = [];
128
140
  const [allPopsEl, setAllPopsEl] = useState({});
129
141
 
142
+ // Large screens show the toolbar buttons directly, small screens show the drop-down menu style buttons
143
+ // The right-hand button of the form toolbar in desktop mode
130
144
  const toolbarButtons = [...defaultButtons, ...customButtons].map((e, index) => {
131
145
  if (isValidElement(e)) {
132
146
  return e;
@@ -136,6 +150,7 @@ export default function CustomToolbar(props) {
136
150
 
137
151
  if (e.Icon) {
138
152
  const { Icon, popRender } = e;
153
+ // When popRender is present, clicking the button will bubble up the content returned by the popRender
139
154
  if (popRender) {
140
155
  allPops.push(
141
156
  <Popover
@@ -166,6 +181,7 @@ export default function CustomToolbar(props) {
166
181
  }
167
182
 
168
183
  if (popRender) {
184
+ // On the large screen, the bubble is positioned at the corresponding button
169
185
  setAllPopsEl({
170
186
  [popId]: document.getElementById(`btn-${popId}`),
171
187
  });
@@ -180,6 +196,7 @@ export default function CustomToolbar(props) {
180
196
  return e;
181
197
  });
182
198
 
199
+ // The toolbar menu in the mobile to replace toolbarButtons
183
200
  const menuItems = [...defaultButtons, ...customButtons].map((e, index) => {
184
201
  const popId = getPopId(index);
185
202
 
@@ -210,6 +227,7 @@ export default function CustomToolbar(props) {
210
227
  }
211
228
 
212
229
  if (e.popRender) {
230
+ // On the small screen, the bubbles are positioned at the three dot buttons
213
231
  setAllPopsEl({
214
232
  [popId]: moreBtn.current,
215
233
  });
@@ -223,54 +241,57 @@ export default function CustomToolbar(props) {
223
241
  return (
224
242
  <div>
225
243
  <Container>
226
- <div className="custom-toobar-title">
244
+ <div
245
+ className={`custom-toobar-title ${
246
+ isMobile && searchOpened && isValidElement(title) ? 'toobar-title-hidden' : ''
247
+ }`}>
227
248
  <div className="custom-toobar-title-inner">
228
249
  <span>{title}</span>
229
250
  </div>
230
251
  </div>
231
- <div className="custom-toobar-right">
232
- <div className="custom-toobar-btns">
233
- {!hideSearch && (
234
- <TableSearch
235
- search={search}
236
- options={options}
237
- searchText={searchText}
238
- searchTextUpdate={searchTextUpdate}
239
- searchClose={searchClose}
240
- isMobile={isMobile}
241
- />
242
- )}
243
- {!showMore && (
244
- <>
245
- {!hidePrint &&
246
- printArea(({ handlePrint }) => (
247
- <span>
248
- <Tooltip title={print}>
249
- <IconButton
250
- data-testid={`${print}-iconButton`}
251
- aria-label={print}
252
- disabled={options.print === 'disabled'}
253
- onClick={handlePrint}>
254
- <PrintIcon />
255
- </IconButton>
256
- </Tooltip>
257
- </span>
258
- ))}
259
-
260
- {toolbarButtons}
261
- </>
262
- )}
263
- {showMore && (
264
- <IconButton
265
- ref={moreBtn}
266
- aria-haspopup="true"
267
- aria-expanded={menuIconEl ? 'true' : undefined}
268
- onClick={event => setMenuIconEl(event.currentTarget)}>
269
- <MoreVertIcon />
270
- </IconButton>
271
- )}
272
- </div>
252
+ <div className={`custom-toobar-btns ${loading || disabled ? 'toobar-btns-disabled' : ''}`}>
253
+ {!hideSearch && (
254
+ <TableSearch
255
+ search={search}
256
+ options={options}
257
+ searchText={searchText}
258
+ searchTextUpdate={searchTextUpdate}
259
+ searchClose={searchClose}
260
+ onSearchOpen={setSearchOpened}
261
+ />
262
+ )}
263
+ {!showMore && (
264
+ <>
265
+ {!hidePrint &&
266
+ printArea(({ handlePrint }) => (
267
+ <span>
268
+ <Tooltip title={print}>
269
+ <IconButton
270
+ data-testid={`${print}-iconButton`}
271
+ aria-label={print}
272
+ disabled={options.print === 'disabled'}
273
+ onClick={handlePrint}>
274
+ <PrintIcon />
275
+ </IconButton>
276
+ </Tooltip>
277
+ </span>
278
+ ))}
279
+
280
+ {toolbarButtons}
281
+ </>
282
+ )}
283
+ {showMore && (
284
+ <IconButton
285
+ ref={moreBtn}
286
+ aria-haspopup="true"
287
+ aria-expanded={menuIconEl ? 'true' : undefined}
288
+ onClick={event => setMenuIconEl(event.currentTarget)}
289
+ style={{ flexShrink: 0 }}>
290
+ <MoreVertIcon />
291
+ </IconButton>
292
+ )}
273
293
  </div>
294
+ {customToolbarEle}
274
295
  </Container>
275
296
 
276
297
  <Menu
@@ -298,6 +319,7 @@ export default function CustomToolbar(props) {
298
319
  {allPops.map((e, index) => (
299
320
  <div key={getPopId(index)}>{e}</div>
300
321
  ))}
322
+ {loading && <LinearProgress />}
301
323
  </div>
302
324
  );
303
325
  }
@@ -314,12 +336,11 @@ CustomToolbar.propTypes = {
314
336
  updateFilterByType: PropTypes.func.isRequired,
315
337
  toggleViewColumn: PropTypes.func.isRequired,
316
338
  updateColumns: PropTypes.func.isRequired,
317
- title: PropTypes.string,
339
+ title: PropTypes.any,
318
340
  searchText: PropTypes.any,
319
341
  searchTextUpdate: PropTypes.func.isRequired,
320
342
  searchClose: PropTypes.func.isRequired,
321
343
  tableRef: PropTypes.func.isRequired,
322
- customButtons: PropTypes.array.isRequired,
323
344
  };
324
345
 
325
346
  CustomToolbar.defaultProps = {
@@ -335,37 +356,51 @@ const Container = styled.div`
335
356
  display: flex;
336
357
  align-items: center;
337
358
  height: 56px;
338
- .custom-toobar {
339
- &-title {
340
- position: relative;
341
- flex: 1;
342
- font-size: 18px;
343
- font-weight: 800;
359
+ .custom-toobar-title {
360
+ position: relative;
361
+ flex: 1;
362
+ font-size: 18px;
363
+ font-weight: 800;
364
+ height: 56px;
365
+ transition: all ease 0.3s;
366
+ &-inner {
367
+ line-height: 56px;
368
+ width: 100%;
344
369
  height: 56px;
345
- &-inner {
346
- line-height: 56px;
347
- width: 100%;
348
- height: 56px;
370
+ position: absolute;
371
+ left: 0;
372
+ top: 0;
373
+ span {
374
+ display: inline-block;
375
+ max-width: 100%;
376
+ white-space: nowrap;
377
+ text-overflow: ellipsis;
378
+ overflow: hidden;
379
+ }
380
+ }
381
+ }
382
+ .custom-toobar-btns {
383
+ display: flex;
384
+ justify-content: center;
385
+ align-items: center;
386
+ &.toobar-btns-disabled {
387
+ position: relative;
388
+ opacity: 0.5;
389
+ &:after {
349
390
  position: absolute;
391
+ display: block;
392
+ z-index: 2;
393
+ width: 100%;
394
+ height: 100%;
350
395
  left: 0;
351
396
  top: 0;
352
- span {
353
- display: inline-block;
354
- max-width: 100%;
355
- white-space: nowrap;
356
- text-overflow: ellipsis;
357
- overflow: hidden;
358
- }
397
+ content: '';
398
+ cursor: not-allowed;
359
399
  }
360
400
  }
361
- &-right {
362
- display: flex;
363
- margin-left: auto;
364
- }
365
- &-btns {
366
- display: flex;
367
- justify-content: center;
368
- align-items: center;
369
- }
401
+ }
402
+ .toobar-title-hidden {
403
+ opacity: 0;
404
+ cursor: none;
370
405
  }
371
406
  `;
@@ -0,0 +1,32 @@
1
+ import React, { createContext, useContext, useState } from 'react';
2
+
3
+ const DatatableContext = createContext({});
4
+
5
+ const { Provider } = DatatableContext;
6
+
7
+ // eslint-disable-next-line react/prop-types
8
+ const DatatableProvide = ({ children }) => {
9
+ const [customButtons, setCustomButtons] = useState([]);
10
+ const [loading, setLoading] = useState(false);
11
+ const [disabled, setDisabled] = useState(false);
12
+ const [filterLabel, setFilterLabel] = useState('Filter');
13
+
14
+ const value = {
15
+ customButtons,
16
+ setCustomButtons,
17
+ filterLabel,
18
+ setFilterLabel,
19
+ loading,
20
+ setLoading,
21
+ disabled,
22
+ setDisabled,
23
+ };
24
+
25
+ return <Provider value={value}>{children}</Provider>;
26
+ };
27
+
28
+ function useDatatableContext() {
29
+ return useContext(DatatableContext);
30
+ }
31
+
32
+ export { DatatableProvide, useDatatableContext };
@@ -13,7 +13,6 @@ export default function TableSearch({
13
13
  searchText,
14
14
  searchTextUpdate,
15
15
  searchClose,
16
- isMobile,
17
16
  onSearchOpen,
18
17
  }) {
19
18
  const [inputMode, setInputMode] = useState(false);
@@ -36,7 +35,7 @@ export default function TableSearch({
36
35
  return (
37
36
  <Container>
38
37
  {inputMode ? (
39
- <div className="toolbar-search-icon-placeholder-">
38
+ <div className="toolbar-search-icon-placeholder">
40
39
  <SearchIcon />
41
40
  </div>
42
41
  ) : (
@@ -51,10 +50,7 @@ export default function TableSearch({
51
50
  </Tooltip>
52
51
  )}
53
52
 
54
- <div
55
- className={`toolbar-search-area ${inputMode ? 'toolbar-btn-show' : ''} ${
56
- isMobile ? 'small-textfield' : ''
57
- }`}>
53
+ <div className={`toolbar-search-area ${inputMode ? 'toolbar-btn-show' : ''}`}>
58
54
  {inputMode && (
59
55
  <TextField
60
56
  variant="standard"
@@ -81,7 +77,6 @@ TableSearch.propTypes = {
81
77
  options: PropTypes.object.isRequired,
82
78
  searchTextUpdate: PropTypes.func.isRequired,
83
79
  searchClose: PropTypes.func.isRequired,
84
- isMobile: PropTypes.bool.isRequired,
85
80
  };
86
81
 
87
82
  TableSearch.defaultProps = {
@@ -104,6 +99,14 @@ const Container = styled.div`
104
99
  &.toolbar-btn-show {
105
100
  width: 260px;
106
101
  padding-left: 8px;
102
+
103
+ ${props => props.theme.breakpoints.down('md')} {
104
+ width: 200px;
105
+ }
106
+
107
+ ${props => props.theme.breakpoints.down('sm')} {
108
+ width: 180px;
109
+ }
107
110
  &.small-textfield {
108
111
  width: 200px;
109
112
  }
@@ -1,34 +1,105 @@
1
- import React, { useRef } from 'react';
1
+ import React, { useEffect, useRef } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import MUIDataTable, { TableFilterList } from 'mui-datatables';
3
+ import MUIDataTable, { TableFilterList, TableFooter } from 'mui-datatables';
4
4
  import styled from 'styled-components';
5
+ import isObject from 'lodash/isObject';
6
+ import cloneDeep from 'lodash/cloneDeep';
5
7
  import CustomToolbar from './CustomToolbar';
8
+ import { DatatableProvide, useDatatableContext } from './DatatableContext';
6
9
 
7
- const WrapFilterList = props => {
8
- const hasFilter = !!props.filterList.filter(e => e.length).length;
9
- if (hasFilter) {
10
- return (
11
- <FilterLine>
12
- {hasFilter && <div className="toolbar-filter-title">Filter</div>}
13
- <div className="toolbar-filter-content">
14
- <TableFilterList {...props} />
15
- </div>
16
- </FilterLine>
17
- );
18
- }
19
- return '';
20
- };
10
+ export default function Datatable({ ...props }) {
11
+ return (
12
+ <DatatableProvide>
13
+ <ReDatatable {...props} />
14
+ </DatatableProvide>
15
+ );
16
+ }
21
17
 
22
- WrapFilterList.propTypes = {
23
- filterList: PropTypes.array,
24
- };
18
+ /**
19
+ * @param {Object} props.options The options of mui-datatable,detail see https://github.com/gregnb/mui-datatables/tree/b8d2eee6af4589d254b40918e5d7e70b1ee4baca
20
+ * @param {Array} props.customButtons Custom buttons for toolbar
21
+ * @param {Function} props.onChange When onChange is present, serverSide mode is activated by default https://github.com/gregnb/mui-datatables/tree/b8d2eee6af4589d254b40918e5d7e70b1ee4baca#remote-data
22
+ * @param {Boolean} props.loading For dynamic data, usually used with onChange
23
+ * @returns
24
+ */
25
+ function ReDatatable({
26
+ data: originData,
27
+ columns: originColumns,
28
+ locale,
29
+ options,
30
+ style,
31
+ customButtons,
32
+ onChange,
33
+ loading,
34
+ disabled,
35
+ ...rest
36
+ }) {
37
+ const container = useRef(null);
38
+ const oldState = useRef(null);
39
+ const { setCustomButtons, setFilterLabel, setLoading, setDisabled } = useDatatableContext();
25
40
 
26
- WrapFilterList.defaultProps = {
27
- filterList: [],
28
- };
41
+ const disabledCellStyle = {
42
+ cursor: 'not-allowed',
43
+ pointerEvents: 'none',
44
+ };
29
45
 
30
- export default function Datatable({ locale, options, style, customButtons, ...rest }) {
31
- const container = useRef(null);
46
+ const keys = [];
47
+
48
+ // Convert Columns fields to object sets to support the width function
49
+ const columns = originColumns.map(e => {
50
+ let tempObj;
51
+
52
+ if (!isObject(e)) {
53
+ tempObj = {
54
+ label: e,
55
+ name: e,
56
+ };
57
+ } else {
58
+ tempObj = cloneDeep(e);
59
+ }
60
+ keys.push(tempObj.name);
61
+
62
+ if (!tempObj.options) {
63
+ tempObj.options = {};
64
+ }
65
+
66
+ const { setCellHeaderProps } = tempObj.options;
67
+ tempObj.options.setCellHeaderProps = columnMeta => {
68
+ let cellProps = {};
69
+
70
+ // Complementing width while inheriting old setCellHeaderProps
71
+ if (setCellHeaderProps && !setCellHeaderProps.__innerFunc) {
72
+ cellProps = setCellHeaderProps(columnMeta) || {};
73
+ }
74
+
75
+ if (loading || disabled) {
76
+ cellProps = { ...cellProps, style: disabledCellStyle };
77
+ }
78
+
79
+ if (tempObj.width) {
80
+ cellProps.width = tempObj.width;
81
+ }
82
+
83
+ return cellProps;
84
+ };
85
+
86
+ // Prevent memory xie caused by recursive forwarding of setCellHeaderProps functions
87
+ tempObj.options.setCellHeaderProps.__innerFunc = 1;
88
+
89
+ return tempObj;
90
+ });
91
+
92
+ // Fixing object-type structures
93
+ const data = originData.map(e => {
94
+ if (!Array.isArray(e) && isObject(e)) {
95
+ return keys.map(key => e[key]);
96
+ }
97
+ return e;
98
+ });
99
+
100
+ useEffect(() => setCustomButtons(customButtons || []), [customButtons]);
101
+ useEffect(() => setLoading(loading), [loading]);
102
+ useEffect(() => setDisabled(disabled), [disabled]);
32
103
 
33
104
  let textLabels = {
34
105
  body: { noMatch: 'Sorry, no matching records found', toolTip: 'Sort' },
@@ -74,42 +145,78 @@ export default function Datatable({ locale, options, style, customButtons, ...re
74
145
  };
75
146
  }
76
147
 
148
+ useEffect(() => setFilterLabel(textLabels.filter.title), [textLabels.filter.title]);
149
+
77
150
  const opts = {
78
151
  selectableRows: 'none',
79
152
  textLabels,
153
+ rowsPerPage: 10,
154
+ rowsPerPageOptions: [10, 20, 50],
80
155
  ...options,
81
156
  };
82
157
 
83
- const WrapCustomToolBar = props => {
84
- return <CustomToolbar {...props} customButtons={customButtons || []} />;
85
- };
158
+ if (onChange) {
159
+ Object.assign(opts, {
160
+ serverSide: true,
161
+ // Wrap the more friendly onChange callback by listening to onTableChange,
162
+ // which will only be triggered when the table key state changes
163
+ onTableChange: (action, tableState) => {
164
+ if (action === 'propsUpdate') {
165
+ return;
166
+ }
167
+ const state = {
168
+ count: tableState.count,
169
+ page: tableState.page,
170
+ rowsPerPage: tableState.rowsPerPage,
171
+ searchText: tableState.searchText,
172
+ sortOrder: tableState.sortOrder, //
173
+ filterList: tableState.filterList,
174
+ };
175
+ const stateStr = JSON.stringify(state);
176
+ if (stateStr === oldState.current) {
177
+ return;
178
+ }
179
+ oldState.current = stateStr;
180
+ onChange(state, action);
181
+ },
182
+ });
183
+ }
86
184
 
87
185
  const props = {
88
186
  options: opts,
89
187
  ...rest,
90
188
  components: {
91
- TableToolbar: WrapCustomToolBar,
189
+ TableToolbar: CustomToolbar,
190
+ TableFooter: WrapTableFooter,
92
191
  TableFilterList: WrapFilterList,
93
192
  },
94
193
  };
95
194
 
96
- Datatable.propTypes = {
195
+ ReDatatable.propTypes = {
196
+ data: PropTypes.array.isRequired,
197
+ columns: PropTypes.array.isRequired,
97
198
  options: PropTypes.object,
98
199
  style: PropTypes.object,
99
200
  locale: PropTypes.string,
201
+ loading: PropTypes.bool,
202
+ disabled: PropTypes.bool,
100
203
  customButtons: PropTypes.array,
204
+ onChange: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
101
205
  };
102
206
 
103
- Datatable.defaultProps = {
207
+ ReDatatable.defaultProps = {
104
208
  options: {},
105
209
  style: {},
106
210
  locale: 'en',
211
+ loading: false,
212
+ disabled: false,
107
213
  customButtons: [],
214
+ onChange: '',
108
215
  };
109
216
 
110
217
  return (
111
218
  <TableContainer ref={container} style={style}>
112
- <MUIDataTable {...props} />
219
+ <MUIDataTable data={data} columns={columns} {...props} />
113
220
  </TableContainer>
114
221
  );
115
222
  }
@@ -143,3 +250,65 @@ const FilterLine = styled.div`
143
250
  font-size: 14px;
144
251
  }
145
252
  `;
253
+
254
+ const WrapFilterList = props => {
255
+ const { filterLabel } = useDatatableContext();
256
+ const hasFilter = !!props.filterList.filter(e => e.length).length;
257
+ if (hasFilter) {
258
+ return (
259
+ <FilterLine>
260
+ {hasFilter && <div className="toolbar-filter-title">{filterLabel}</div>}
261
+ <div className="toolbar-filter-content">
262
+ <TableFilterList {...props} />
263
+ </div>
264
+ </FilterLine>
265
+ );
266
+ }
267
+ return '';
268
+ };
269
+
270
+ WrapFilterList.propTypes = {
271
+ filterList: PropTypes.array,
272
+ };
273
+
274
+ WrapFilterList.defaultProps = {
275
+ filterList: [],
276
+ };
277
+
278
+ const WrapTableFooter = props => {
279
+ const { loading, disabled } = useDatatableContext();
280
+
281
+ return (
282
+ <FooterContainer>
283
+ <div className={`datatable-footer ${loading || disabled ? 'datatable-footer-disabled' : ''}`}>
284
+ <TableFooter {...props} />
285
+ </div>
286
+ </FooterContainer>
287
+ );
288
+ };
289
+
290
+ const FooterContainer = styled.div`
291
+ display: flex;
292
+ align-items: center;
293
+ .datatable-footer {
294
+ position: relative;
295
+ margin-left: auto;
296
+ &.datatable-footer-disabled {
297
+ position: relative;
298
+ .MuiTablePagination-root {
299
+ opacity: 0.6;
300
+ }
301
+ &:after {
302
+ position: absolute;
303
+ display: block;
304
+ z-index: 2;
305
+ width: 100%;
306
+ height: 100%;
307
+ left: 0;
308
+ top: 0;
309
+ content: '';
310
+ cursor: not-allowed;
311
+ }
312
+ }
313
+ }
314
+ `;