@etsoo/react 1.4.86 → 1.4.90
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/mu/ComboBox.d.ts +4 -0
- package/lib/mu/ComboBox.js +2 -2
- package/lib/mu/ListMoreDisplay.d.ts +14 -5
- package/lib/mu/ListMoreDisplay.js +29 -14
- package/lib/mu/LoadingButton.js +4 -8
- package/lib/mu/SelectBool.js +2 -2
- package/lib/mu/SelectEx.d.ts +4 -0
- package/lib/mu/SelectEx.js +2 -2
- package/package.json +11 -11
- package/src/mu/ComboBox.tsx +7 -1
- package/src/mu/ListMoreDisplay.tsx +70 -26
- package/src/mu/LoadingButton.tsx +5 -7
- package/src/mu/SelectBool.tsx +2 -2
- package/src/mu/SelectEx.tsx +7 -1
package/lib/mu/ComboBox.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ import { AutocompleteExtendedProps } from './AutocompleteExtendedProps';
|
|
|
5
5
|
* ComboBox props
|
|
6
6
|
*/
|
|
7
7
|
export interface ComboBoxProps<T extends {}> extends AutocompleteExtendedProps<T> {
|
|
8
|
+
/**
|
|
9
|
+
* Auto add blank item
|
|
10
|
+
*/
|
|
11
|
+
autoAddBlankItem?: boolean;
|
|
8
12
|
/**
|
|
9
13
|
* Data readonly
|
|
10
14
|
*/
|
package/lib/mu/ComboBox.js
CHANGED
|
@@ -12,7 +12,7 @@ import { SearchField } from './SearchField';
|
|
|
12
12
|
*/
|
|
13
13
|
export function ComboBox(props) {
|
|
14
14
|
// Destruct
|
|
15
|
-
const { search = false, idField = 'id', idValue, inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputVariant, defaultValue, label, labelField = 'label', loadData, onLoadData, name, inputAutoComplete = 'off', options, dataReadonly = true, readOnly, onChange, openOnFocus = true, value, getOptionLabel = (option) => String(Reflect.get(option, labelField)), sx = { minWidth: '150px' }, ...rest } = props;
|
|
15
|
+
const { search = false, autoAddBlankItem = search, idField = 'id', idValue, inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputVariant, defaultValue, label, labelField = 'label', loadData, onLoadData, name, inputAutoComplete = 'off', options, dataReadonly = true, readOnly, onChange, openOnFocus = true, value, getOptionLabel = (option) => String(Reflect.get(option, labelField)), sx = { minWidth: '150px' }, ...rest } = props;
|
|
16
16
|
// Value input ref
|
|
17
17
|
const inputRef = React.createRef();
|
|
18
18
|
// Options state
|
|
@@ -82,7 +82,7 @@ export function ComboBox(props) {
|
|
|
82
82
|
return;
|
|
83
83
|
if (onLoadData)
|
|
84
84
|
onLoadData(result);
|
|
85
|
-
if (
|
|
85
|
+
if (autoAddBlankItem) {
|
|
86
86
|
SharedUtils.addBlankItem(result, idField, labelField);
|
|
87
87
|
}
|
|
88
88
|
setOptions(result);
|
|
@@ -1,26 +1,35 @@
|
|
|
1
|
+
import { DataTypes } from '@etsoo/shared';
|
|
1
2
|
import { CardProps } from '@mui/material';
|
|
2
3
|
import React from 'react';
|
|
3
|
-
import { GridLoader } from '../components/GridLoader';
|
|
4
|
+
import { GridData, GridLoader } from '../components/GridLoader';
|
|
4
5
|
/**
|
|
5
6
|
* ListMoreDisplay props
|
|
6
7
|
*/
|
|
7
|
-
export interface ListMoreDisplayProps<T> extends CardProps, GridLoader<T> {
|
|
8
|
+
export interface ListMoreDisplayProps<T, F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate> extends CardProps, GridLoader<T> {
|
|
8
9
|
/**
|
|
9
10
|
* Children to display the list
|
|
10
11
|
*/
|
|
11
12
|
children: (data: T, index: number) => React.ReactNode;
|
|
12
13
|
/**
|
|
13
|
-
*
|
|
14
|
+
* Search field template
|
|
14
15
|
*/
|
|
15
|
-
|
|
16
|
+
fieldTemplate?: F;
|
|
17
|
+
/**
|
|
18
|
+
* Header renderer
|
|
19
|
+
*/
|
|
20
|
+
headerRenderer?: (reset: (data?: GridData) => void) => React.ReactNode;
|
|
16
21
|
/**
|
|
17
22
|
* Header title
|
|
18
23
|
*/
|
|
19
24
|
headerTitle?: React.ReactNode;
|
|
25
|
+
/**
|
|
26
|
+
* More button label
|
|
27
|
+
*/
|
|
28
|
+
moreLabel?: string;
|
|
20
29
|
}
|
|
21
30
|
/**
|
|
22
31
|
* ListMoreDisplay
|
|
23
32
|
* @param props Props
|
|
24
33
|
* @returns Component
|
|
25
34
|
*/
|
|
26
|
-
export declare function ListMoreDisplay<T extends {}>(props: ListMoreDisplayProps<T>): JSX.Element;
|
|
35
|
+
export declare function ListMoreDisplay<T extends {}, F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate>(props: ListMoreDisplayProps<T, F>): JSX.Element;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Card, CardActions, CardContent, CardHeader, CircularProgress } from '@mui/material';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { globalApp
|
|
3
|
+
import { globalApp } from '../app/ReactApp';
|
|
4
|
+
import { GridDataGet } from '../components/GridLoader';
|
|
5
|
+
import { LoadingButton } from './LoadingButton';
|
|
4
6
|
/**
|
|
5
7
|
* ListMoreDisplay
|
|
6
8
|
* @param props Props
|
|
@@ -8,9 +10,9 @@ import { globalApp, LoadingButton } from '..';
|
|
|
8
10
|
*/
|
|
9
11
|
export function ListMoreDisplay(props) {
|
|
10
12
|
// Destruct
|
|
11
|
-
const { autoLoad = true, children, defaultOrderBy, headerTitle, loadBatchSize, loadData, moreLabel = typeof globalApp === 'undefined'
|
|
13
|
+
const { autoLoad = true, children, defaultOrderBy, headerRenderer, headerTitle, loadBatchSize, loadData, moreLabel = typeof globalApp === 'undefined'
|
|
12
14
|
? undefined
|
|
13
|
-
: globalApp.get('more') + '...', threshold, ...rest } = props;
|
|
15
|
+
: globalApp.get('more') + '...', fieldTemplate, threshold, ...rest } = props;
|
|
14
16
|
// Refs
|
|
15
17
|
const refs = React.useRef({
|
|
16
18
|
autoLoad,
|
|
@@ -28,7 +30,7 @@ export function ListMoreDisplay(props) {
|
|
|
28
30
|
return { ...currentStates, ...newStates };
|
|
29
31
|
}, { completed: false });
|
|
30
32
|
// Load data
|
|
31
|
-
const loadDataLocal = async () => {
|
|
33
|
+
const loadDataLocal = async (reset = false) => {
|
|
32
34
|
// Prevent multiple loadings
|
|
33
35
|
if (!ref.hasNextPage || ref.isNextPageLoading)
|
|
34
36
|
return;
|
|
@@ -43,7 +45,8 @@ export function ListMoreDisplay(props) {
|
|
|
43
45
|
orderByAsc,
|
|
44
46
|
data
|
|
45
47
|
};
|
|
46
|
-
const
|
|
48
|
+
const mergedData = GridDataGet(loadProps, fieldTemplate);
|
|
49
|
+
const items = await loadData(mergedData);
|
|
47
50
|
if (items == null || ref.isMounted === false) {
|
|
48
51
|
return;
|
|
49
52
|
}
|
|
@@ -54,7 +57,7 @@ export function ListMoreDisplay(props) {
|
|
|
54
57
|
ref.isNextPageLoading = false;
|
|
55
58
|
ref.hasNextPage = hasNextPage;
|
|
56
59
|
// Update rows
|
|
57
|
-
if (states.items == null)
|
|
60
|
+
if (states.items == null || reset)
|
|
58
61
|
setStates({ items, completed: !hasNextPage });
|
|
59
62
|
else
|
|
60
63
|
setStates({
|
|
@@ -62,6 +65,16 @@ export function ListMoreDisplay(props) {
|
|
|
62
65
|
completed: !hasNextPage
|
|
63
66
|
});
|
|
64
67
|
};
|
|
68
|
+
const reset = (data) => {
|
|
69
|
+
// Update the form data
|
|
70
|
+
ref.data = data;
|
|
71
|
+
// Reset page number
|
|
72
|
+
ref.isNextPageLoading = false;
|
|
73
|
+
ref.currentPage = 0;
|
|
74
|
+
ref.hasNextPage = true;
|
|
75
|
+
// Load data
|
|
76
|
+
loadDataLocal();
|
|
77
|
+
};
|
|
65
78
|
React.useEffect(() => {
|
|
66
79
|
if (autoLoad)
|
|
67
80
|
loadDataLocal();
|
|
@@ -74,12 +87,14 @@ export function ListMoreDisplay(props) {
|
|
|
74
87
|
// Loading
|
|
75
88
|
if (states.items == null)
|
|
76
89
|
return React.createElement(CircularProgress, { size: 20 });
|
|
77
|
-
return (React.createElement(
|
|
78
|
-
|
|
79
|
-
React.createElement(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
return (React.createElement(React.Fragment, null,
|
|
91
|
+
headerRenderer && headerRenderer(reset),
|
|
92
|
+
React.createElement(Card, { ...rest },
|
|
93
|
+
React.createElement(CardHeader, { title: headerTitle }),
|
|
94
|
+
React.createElement(CardContent, { sx: {
|
|
95
|
+
paddingTop: 0,
|
|
96
|
+
paddingBottom: states.completed ? 0 : 'inherit'
|
|
97
|
+
} }, states.items.map((item, index) => children(item, index))),
|
|
98
|
+
!states.completed && (React.createElement(CardActions, { sx: { justifyContent: 'flex-end' } },
|
|
99
|
+
React.createElement(LoadingButton, { onClick: async () => await loadDataLocal() }, moreLabel))))));
|
|
85
100
|
}
|
package/lib/mu/LoadingButton.js
CHANGED
|
@@ -27,19 +27,15 @@ export function LoadingButton(props) {
|
|
|
27
27
|
if (onClick) {
|
|
28
28
|
// Update state
|
|
29
29
|
setLoading(true);
|
|
30
|
+
// https://stackoverflow.com/questions/38508420/how-to-know-if-a-function-is-async
|
|
30
31
|
// const AsyncFunction = (async () => {}).constructor;
|
|
31
32
|
// onClick instanceof AsyncFunction
|
|
32
|
-
|
|
33
|
-
// May long time running
|
|
34
|
-
await onClick(event);
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
onClick(event);
|
|
38
|
-
}
|
|
33
|
+
await onClick(event);
|
|
39
34
|
// Warning: Can't perform a React state update on an unmounted component
|
|
40
35
|
// It's necessary to check the component is mounted now
|
|
41
|
-
if (isMounted.current)
|
|
36
|
+
if (isMounted.current) {
|
|
42
37
|
setLoading(false);
|
|
38
|
+
}
|
|
43
39
|
}
|
|
44
40
|
}, ...rest }));
|
|
45
41
|
}
|
package/lib/mu/SelectBool.js
CHANGED
|
@@ -9,13 +9,13 @@ import { SelectEx } from './SelectEx';
|
|
|
9
9
|
*/
|
|
10
10
|
export function SelectBool(props) {
|
|
11
11
|
// Destruct
|
|
12
|
-
const { search = true, ...rest } = props;
|
|
12
|
+
const { search = true, autoAddBlankItem = search, ...rest } = props;
|
|
13
13
|
// Options
|
|
14
14
|
const options = [
|
|
15
15
|
{ id: 'false', label: globalApp.get('no') },
|
|
16
16
|
{ id: 'true', label: globalApp.get('yes') }
|
|
17
17
|
];
|
|
18
|
-
if (
|
|
18
|
+
if (autoAddBlankItem)
|
|
19
19
|
Utils.addBlankItem(options);
|
|
20
20
|
// Layout
|
|
21
21
|
return React.createElement(SelectEx, { options: options, search: search, ...rest });
|
package/lib/mu/SelectEx.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ import { IdLabelDto } from '@etsoo/appscript';
|
|
|
5
5
|
* Extended select component props
|
|
6
6
|
*/
|
|
7
7
|
export interface SelectExProps<T extends {}> extends Omit<SelectProps, 'labelId' | 'input' | 'native'> {
|
|
8
|
+
/**
|
|
9
|
+
* Auto add blank item
|
|
10
|
+
*/
|
|
11
|
+
autoAddBlankItem?: boolean;
|
|
8
12
|
/**
|
|
9
13
|
* Id field, default is id
|
|
10
14
|
*/
|
package/lib/mu/SelectEx.js
CHANGED
|
@@ -12,7 +12,7 @@ import { Utils } from '@etsoo/shared';
|
|
|
12
12
|
export function SelectEx(props) {
|
|
13
13
|
var _a;
|
|
14
14
|
// Destruct
|
|
15
|
-
const { defaultValue, idField = 'id', itemIconRenderer, label, labelField = 'label', loadData, onItemClick, onLoadData, multiple = false, name, options = [], search = false, value, onChange, ...rest } = props;
|
|
15
|
+
const { defaultValue, idField = 'id', itemIconRenderer, label, labelField = 'label', loadData, onItemClick, onLoadData, multiple = false, name, options = [], search = false, autoAddBlankItem = search, value, onChange, ...rest } = props;
|
|
16
16
|
// Options state
|
|
17
17
|
const [localOptions, setOptions] = React.useState(options);
|
|
18
18
|
const isMounted = React.useRef(true);
|
|
@@ -97,7 +97,7 @@ export function SelectEx(props) {
|
|
|
97
97
|
return;
|
|
98
98
|
if (onLoadData)
|
|
99
99
|
onLoadData(result);
|
|
100
|
-
if (
|
|
100
|
+
if (autoAddBlankItem) {
|
|
101
101
|
Utils.addBlankItem(result, idField, labelField);
|
|
102
102
|
}
|
|
103
103
|
setOptions(result);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/react",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.90",
|
|
4
4
|
"description": "TypeScript ReactJs framework",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -50,16 +50,16 @@
|
|
|
50
50
|
"@emotion/react": "^11.7.1",
|
|
51
51
|
"@emotion/style": "^0.8.0",
|
|
52
52
|
"@emotion/styled": "^11.6.0",
|
|
53
|
-
"@etsoo/appscript": "^1.2.
|
|
53
|
+
"@etsoo/appscript": "^1.2.37",
|
|
54
54
|
"@etsoo/notificationbase": "^1.1.1",
|
|
55
|
-
"@etsoo/shared": "^1.1.
|
|
55
|
+
"@etsoo/shared": "^1.1.10",
|
|
56
56
|
"@mui/icons-material": "^5.3.1",
|
|
57
57
|
"@mui/material": "^5.4.0",
|
|
58
58
|
"@reach/router": "^1.3.4",
|
|
59
|
-
"@types/pica": "^
|
|
59
|
+
"@types/pica": "^9.0.0",
|
|
60
60
|
"@types/pulltorefreshjs": "^0.1.5",
|
|
61
61
|
"@types/reach__router": "^1.3.10",
|
|
62
|
-
"@types/react": "^17.0.
|
|
62
|
+
"@types/react": "^17.0.39",
|
|
63
63
|
"@types/react-avatar-editor": "^10.3.6",
|
|
64
64
|
"@types/react-beautiful-dnd": "^13.1.2",
|
|
65
65
|
"@types/react-dom": "^17.0.11",
|
|
@@ -72,15 +72,15 @@
|
|
|
72
72
|
"react-beautiful-dnd": "^13.1.0",
|
|
73
73
|
"react-dom": "^17.0.2",
|
|
74
74
|
"react-draggable": "^4.4.4",
|
|
75
|
-
"react-imask": "^6.
|
|
75
|
+
"react-imask": "^6.4.0",
|
|
76
76
|
"react-window": "^1.8.6"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"@babel/cli": "^7.
|
|
80
|
-
"@babel/core": "^7.
|
|
81
|
-
"@babel/plugin-transform-runtime": "^7.
|
|
79
|
+
"@babel/cli": "^7.17.0",
|
|
80
|
+
"@babel/core": "^7.17.0",
|
|
81
|
+
"@babel/plugin-transform-runtime": "^7.17.0",
|
|
82
82
|
"@babel/preset-env": "^7.16.11",
|
|
83
|
-
"@babel/runtime-corejs3": "^7.
|
|
83
|
+
"@babel/runtime-corejs3": "^7.17.0",
|
|
84
84
|
"@types/jest": "^27.4.0",
|
|
85
85
|
"@types/react-test-renderer": "^17.0.1",
|
|
86
86
|
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
90
90
|
"eslint-plugin-import": "^2.25.4",
|
|
91
91
|
"eslint-plugin-react": "^7.28.0",
|
|
92
|
-
"jest": "^27.
|
|
92
|
+
"jest": "^27.5.0",
|
|
93
93
|
"react-test-renderer": "^17.0.2",
|
|
94
94
|
"ts-jest": "^27.1.3",
|
|
95
95
|
"typescript": "^4.5.5"
|
package/src/mu/ComboBox.tsx
CHANGED
|
@@ -13,6 +13,11 @@ import { SearchField } from './SearchField';
|
|
|
13
13
|
*/
|
|
14
14
|
export interface ComboBoxProps<T extends {}>
|
|
15
15
|
extends AutocompleteExtendedProps<T> {
|
|
16
|
+
/**
|
|
17
|
+
* Auto add blank item
|
|
18
|
+
*/
|
|
19
|
+
autoAddBlankItem?: boolean;
|
|
20
|
+
|
|
16
21
|
/**
|
|
17
22
|
* Data readonly
|
|
18
23
|
*/
|
|
@@ -48,6 +53,7 @@ export function ComboBox<T extends {} = IdLabelDto>(props: ComboBoxProps<T>) {
|
|
|
48
53
|
// Destruct
|
|
49
54
|
const {
|
|
50
55
|
search = false,
|
|
56
|
+
autoAddBlankItem = search,
|
|
51
57
|
idField = 'id',
|
|
52
58
|
idValue,
|
|
53
59
|
inputError,
|
|
@@ -156,7 +162,7 @@ export function ComboBox<T extends {} = IdLabelDto>(props: ComboBoxProps<T>) {
|
|
|
156
162
|
loadData().then((result) => {
|
|
157
163
|
if (result == null || !isMounted.current) return;
|
|
158
164
|
if (onLoadData) onLoadData(result);
|
|
159
|
-
if (
|
|
165
|
+
if (autoAddBlankItem) {
|
|
160
166
|
SharedUtils.addBlankItem(result, idField, labelField);
|
|
161
167
|
}
|
|
162
168
|
setOptions(result);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DataTypes } from '@etsoo/shared';
|
|
1
2
|
import {
|
|
2
3
|
Card,
|
|
3
4
|
CardActions,
|
|
@@ -7,31 +8,48 @@ import {
|
|
|
7
8
|
CircularProgress
|
|
8
9
|
} from '@mui/material';
|
|
9
10
|
import React from 'react';
|
|
10
|
-
import { globalApp
|
|
11
|
+
import { globalApp } from '../app/ReactApp';
|
|
11
12
|
import {
|
|
13
|
+
GridData,
|
|
14
|
+
GridDataGet,
|
|
12
15
|
GridLoadDataProps,
|
|
13
16
|
GridLoader,
|
|
14
17
|
GridLoaderStates
|
|
15
18
|
} from '../components/GridLoader';
|
|
19
|
+
import { LoadingButton } from './LoadingButton';
|
|
16
20
|
|
|
17
21
|
/**
|
|
18
22
|
* ListMoreDisplay props
|
|
19
23
|
*/
|
|
20
|
-
export interface ListMoreDisplayProps<
|
|
24
|
+
export interface ListMoreDisplayProps<
|
|
25
|
+
T,
|
|
26
|
+
F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate
|
|
27
|
+
> extends CardProps,
|
|
28
|
+
GridLoader<T> {
|
|
21
29
|
/**
|
|
22
30
|
* Children to display the list
|
|
23
31
|
*/
|
|
24
32
|
children: (data: T, index: number) => React.ReactNode;
|
|
25
33
|
|
|
26
34
|
/**
|
|
27
|
-
*
|
|
35
|
+
* Search field template
|
|
28
36
|
*/
|
|
29
|
-
|
|
37
|
+
fieldTemplate?: F;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Header renderer
|
|
41
|
+
*/
|
|
42
|
+
headerRenderer?: (reset: (data?: GridData) => void) => React.ReactNode;
|
|
30
43
|
|
|
31
44
|
/**
|
|
32
45
|
* Header title
|
|
33
46
|
*/
|
|
34
47
|
headerTitle?: React.ReactNode;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* More button label
|
|
51
|
+
*/
|
|
52
|
+
moreLabel?: string;
|
|
35
53
|
}
|
|
36
54
|
|
|
37
55
|
type states<T> = {
|
|
@@ -44,18 +62,23 @@ type states<T> = {
|
|
|
44
62
|
* @param props Props
|
|
45
63
|
* @returns Component
|
|
46
64
|
*/
|
|
47
|
-
export function ListMoreDisplay<
|
|
65
|
+
export function ListMoreDisplay<
|
|
66
|
+
T extends {},
|
|
67
|
+
F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate
|
|
68
|
+
>(props: ListMoreDisplayProps<T, F>) {
|
|
48
69
|
// Destruct
|
|
49
70
|
const {
|
|
50
71
|
autoLoad = true,
|
|
51
72
|
children,
|
|
52
73
|
defaultOrderBy,
|
|
74
|
+
headerRenderer,
|
|
53
75
|
headerTitle,
|
|
54
76
|
loadBatchSize,
|
|
55
77
|
loadData,
|
|
56
78
|
moreLabel = typeof globalApp === 'undefined'
|
|
57
79
|
? undefined
|
|
58
80
|
: globalApp.get('more') + '...',
|
|
81
|
+
fieldTemplate,
|
|
59
82
|
threshold,
|
|
60
83
|
...rest
|
|
61
84
|
} = props;
|
|
@@ -82,7 +105,7 @@ export function ListMoreDisplay<T extends {}>(props: ListMoreDisplayProps<T>) {
|
|
|
82
105
|
);
|
|
83
106
|
|
|
84
107
|
// Load data
|
|
85
|
-
const loadDataLocal = async () => {
|
|
108
|
+
const loadDataLocal = async (reset: boolean = false) => {
|
|
86
109
|
// Prevent multiple loadings
|
|
87
110
|
if (!ref.hasNextPage || ref.isNextPageLoading) return;
|
|
88
111
|
|
|
@@ -100,7 +123,9 @@ export function ListMoreDisplay<T extends {}>(props: ListMoreDisplayProps<T>) {
|
|
|
100
123
|
data
|
|
101
124
|
};
|
|
102
125
|
|
|
103
|
-
const
|
|
126
|
+
const mergedData = GridDataGet(loadProps, fieldTemplate);
|
|
127
|
+
|
|
128
|
+
const items = await loadData(mergedData);
|
|
104
129
|
if (items == null || ref.isMounted === false) {
|
|
105
130
|
return;
|
|
106
131
|
}
|
|
@@ -113,7 +138,8 @@ export function ListMoreDisplay<T extends {}>(props: ListMoreDisplayProps<T>) {
|
|
|
113
138
|
ref.hasNextPage = hasNextPage;
|
|
114
139
|
|
|
115
140
|
// Update rows
|
|
116
|
-
if (states.items == null
|
|
141
|
+
if (states.items == null || reset)
|
|
142
|
+
setStates({ items, completed: !hasNextPage });
|
|
117
143
|
else
|
|
118
144
|
setStates({
|
|
119
145
|
items: [...states.items, ...items],
|
|
@@ -121,6 +147,19 @@ export function ListMoreDisplay<T extends {}>(props: ListMoreDisplayProps<T>) {
|
|
|
121
147
|
});
|
|
122
148
|
};
|
|
123
149
|
|
|
150
|
+
const reset = (data?: GridData) => {
|
|
151
|
+
// Update the form data
|
|
152
|
+
ref.data = data;
|
|
153
|
+
|
|
154
|
+
// Reset page number
|
|
155
|
+
ref.isNextPageLoading = false;
|
|
156
|
+
ref.currentPage = 0;
|
|
157
|
+
ref.hasNextPage = true;
|
|
158
|
+
|
|
159
|
+
// Load data
|
|
160
|
+
loadDataLocal();
|
|
161
|
+
};
|
|
162
|
+
|
|
124
163
|
React.useEffect(() => {
|
|
125
164
|
if (autoLoad) loadDataLocal();
|
|
126
165
|
}, [autoLoad]);
|
|
@@ -135,23 +174,28 @@ export function ListMoreDisplay<T extends {}>(props: ListMoreDisplayProps<T>) {
|
|
|
135
174
|
if (states.items == null) return <CircularProgress size={20} />;
|
|
136
175
|
|
|
137
176
|
return (
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
177
|
+
<React.Fragment>
|
|
178
|
+
{headerRenderer && headerRenderer(reset)}
|
|
179
|
+
<Card {...rest}>
|
|
180
|
+
<CardHeader title={headerTitle}></CardHeader>
|
|
181
|
+
<CardContent
|
|
182
|
+
sx={{
|
|
183
|
+
paddingTop: 0,
|
|
184
|
+
paddingBottom: states.completed ? 0 : 'inherit'
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
187
|
+
{states.items.map((item, index) => children(item, index))}
|
|
188
|
+
</CardContent>
|
|
189
|
+
{!states.completed && (
|
|
190
|
+
<CardActions sx={{ justifyContent: 'flex-end' }}>
|
|
191
|
+
<LoadingButton
|
|
192
|
+
onClick={async () => await loadDataLocal()}
|
|
193
|
+
>
|
|
194
|
+
{moreLabel}
|
|
195
|
+
</LoadingButton>
|
|
196
|
+
</CardActions>
|
|
197
|
+
)}
|
|
198
|
+
</Card>
|
|
199
|
+
</React.Fragment>
|
|
156
200
|
);
|
|
157
201
|
}
|
package/src/mu/LoadingButton.tsx
CHANGED
|
@@ -57,18 +57,16 @@ export function LoadingButton(props: LoadingButtonProps) {
|
|
|
57
57
|
// Update state
|
|
58
58
|
setLoading(true);
|
|
59
59
|
|
|
60
|
+
// https://stackoverflow.com/questions/38508420/how-to-know-if-a-function-is-async
|
|
60
61
|
// const AsyncFunction = (async () => {}).constructor;
|
|
61
62
|
// onClick instanceof AsyncFunction
|
|
62
|
-
|
|
63
|
-
// May long time running
|
|
64
|
-
await onClick(event);
|
|
65
|
-
} else {
|
|
66
|
-
onClick(event);
|
|
67
|
-
}
|
|
63
|
+
await onClick(event);
|
|
68
64
|
|
|
69
65
|
// Warning: Can't perform a React state update on an unmounted component
|
|
70
66
|
// It's necessary to check the component is mounted now
|
|
71
|
-
if (isMounted.current)
|
|
67
|
+
if (isMounted.current) {
|
|
68
|
+
setLoading(false);
|
|
69
|
+
}
|
|
72
70
|
}
|
|
73
71
|
}}
|
|
74
72
|
{...rest}
|
package/src/mu/SelectBool.tsx
CHANGED
|
@@ -17,7 +17,7 @@ export interface SelectBoolProps
|
|
|
17
17
|
*/
|
|
18
18
|
export function SelectBool(props: SelectBoolProps) {
|
|
19
19
|
// Destruct
|
|
20
|
-
const { search = true, ...rest } = props;
|
|
20
|
+
const { search = true, autoAddBlankItem = search, ...rest } = props;
|
|
21
21
|
|
|
22
22
|
// Options
|
|
23
23
|
const options: IdLabelDto<string>[] = [
|
|
@@ -25,7 +25,7 @@ export function SelectBool(props: SelectBoolProps) {
|
|
|
25
25
|
{ id: 'true', label: globalApp.get('yes')! }
|
|
26
26
|
];
|
|
27
27
|
|
|
28
|
-
if (
|
|
28
|
+
if (autoAddBlankItem) Utils.addBlankItem(options);
|
|
29
29
|
|
|
30
30
|
// Layout
|
|
31
31
|
return <SelectEx options={options} search={search} {...rest} />;
|
package/src/mu/SelectEx.tsx
CHANGED
|
@@ -21,6 +21,11 @@ import { Utils } from '@etsoo/shared';
|
|
|
21
21
|
*/
|
|
22
22
|
export interface SelectExProps<T extends {}>
|
|
23
23
|
extends Omit<SelectProps, 'labelId' | 'input' | 'native'> {
|
|
24
|
+
/**
|
|
25
|
+
* Auto add blank item
|
|
26
|
+
*/
|
|
27
|
+
autoAddBlankItem?: boolean;
|
|
28
|
+
|
|
24
29
|
/**
|
|
25
30
|
* Id field, default is id
|
|
26
31
|
*/
|
|
@@ -82,6 +87,7 @@ export function SelectEx<T extends {} = IdLabelDto>(props: SelectExProps<T>) {
|
|
|
82
87
|
name,
|
|
83
88
|
options = [],
|
|
84
89
|
search = false,
|
|
90
|
+
autoAddBlankItem = search,
|
|
85
91
|
value,
|
|
86
92
|
onChange,
|
|
87
93
|
...rest
|
|
@@ -172,7 +178,7 @@ export function SelectEx<T extends {} = IdLabelDto>(props: SelectExProps<T>) {
|
|
|
172
178
|
loadData().then((result) => {
|
|
173
179
|
if (result == null || !isMounted.current) return;
|
|
174
180
|
if (onLoadData) onLoadData(result);
|
|
175
|
-
if (
|
|
181
|
+
if (autoAddBlankItem) {
|
|
176
182
|
Utils.addBlankItem(result, idField, labelField);
|
|
177
183
|
}
|
|
178
184
|
setOptions(result);
|