@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.
- package/lib/Datatable/CustomToolbar.js +38 -18
- package/lib/Datatable/DatatableContext.js +47 -0
- package/lib/Datatable/TableSearch.js +4 -6
- package/lib/Datatable/index.js +190 -37
- package/package.json +4 -4
- package/src/Datatable/CustomToolbar.js +108 -73
- package/src/Datatable/DatatableContext.js +32 -0
- package/src/Datatable/TableSearch.js +10 -7
- package/src/Datatable/index.js +200 -31
|
@@ -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-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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' : ''
|
|
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
|
|
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'));
|
package/lib/Datatable/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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:
|
|
256
|
+
TableToolbar: _CustomToolbar.default,
|
|
257
|
+
TableFooter: WrapTableFooter,
|
|
155
258
|
TableFilterList: WrapFilterList
|
|
156
259
|
}
|
|
157
260
|
});
|
|
158
261
|
|
|
159
|
-
|
|
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
|
-
|
|
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
|
-
|
|
273
|
+
ReDatatable.defaultProps = {
|
|
166
274
|
options: {},
|
|
167
275
|
style: {},
|
|
168
276
|
locale: 'en',
|
|
169
|
-
|
|
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,
|
|
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.
|
|
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": "
|
|
55
|
+
"gitHead": "3c91cd679fef62a21839887f72870b43833c7d39",
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@arcblock/icons": "^2.1.
|
|
58
|
-
"@arcblock/react-hooks": "^2.1.
|
|
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
|
|
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=
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
{
|
|
246
|
-
|
|
247
|
-
<
|
|
248
|
-
<
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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.
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
353
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
}
|
package/src/Datatable/index.js
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
const disabledCellStyle = {
|
|
42
|
+
cursor: 'not-allowed',
|
|
43
|
+
pointerEvents: 'none',
|
|
44
|
+
};
|
|
29
45
|
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
84
|
-
|
|
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:
|
|
189
|
+
TableToolbar: CustomToolbar,
|
|
190
|
+
TableFooter: WrapTableFooter,
|
|
92
191
|
TableFilterList: WrapFilterList,
|
|
93
192
|
},
|
|
94
193
|
};
|
|
95
194
|
|
|
96
|
-
|
|
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
|
-
|
|
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
|
+
`;
|