@etsoo/react 1.4.85 → 1.4.89
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/app/ServiceApp.d.ts +2 -1
- package/lib/app/ServiceApp.js +4 -2
- package/lib/mu/ComboBox.d.ts +4 -0
- package/lib/mu/ComboBox.js +2 -2
- package/lib/mu/LoadingButton.js +11 -9
- 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/app/ServiceApp.ts +4 -2
- package/src/mu/ComboBox.tsx +7 -1
- package/src/mu/LoadingButton.tsx +15 -7
- package/src/mu/SelectBool.tsx +2 -2
- package/src/mu/SelectEx.tsx +7 -1
package/lib/app/ServiceApp.d.ts
CHANGED
|
@@ -34,8 +34,9 @@ export declare class ServiceApp<U extends IServiceUser = IServiceUser, P extends
|
|
|
34
34
|
constructor(settings: S, name: string);
|
|
35
35
|
/**
|
|
36
36
|
* Go to the login page
|
|
37
|
+
* @param tryLogin Try to login again
|
|
37
38
|
*/
|
|
38
|
-
toLoginPage(): void;
|
|
39
|
+
toLoginPage(tryLogin?: boolean): void;
|
|
39
40
|
/**
|
|
40
41
|
* Refresh token
|
|
41
42
|
* @param props Props
|
package/lib/app/ServiceApp.js
CHANGED
|
@@ -41,8 +41,9 @@ export class ServiceApp extends ReactApp {
|
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
43
|
* Go to the login page
|
|
44
|
+
* @param tryLogin Try to login again
|
|
44
45
|
*/
|
|
45
|
-
toLoginPage() {
|
|
46
|
+
toLoginPage(tryLogin) {
|
|
46
47
|
const coreUrl = this.settings.webUrl;
|
|
47
48
|
window.location.href =
|
|
48
49
|
coreUrl +
|
|
@@ -51,7 +52,8 @@ export class ServiceApp extends ReactApp {
|
|
|
51
52
|
'&' +
|
|
52
53
|
DomUtils.CultureField +
|
|
53
54
|
'=' +
|
|
54
|
-
this.culture
|
|
55
|
+
this.culture +
|
|
56
|
+
(tryLogin ? '' : '&tryLogin=false');
|
|
55
57
|
}
|
|
56
58
|
/**
|
|
57
59
|
* Refresh token
|
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);
|
package/lib/mu/LoadingButton.js
CHANGED
|
@@ -15,6 +15,8 @@ export function LoadingButton(props) {
|
|
|
15
15
|
const [loading, setLoading] = React.useState(false);
|
|
16
16
|
// Icon
|
|
17
17
|
const localEndIcon = loading ? (React.createElement(CircularProgress, { ...loadingIconProps })) : (endIcon);
|
|
18
|
+
// Button ref
|
|
19
|
+
const buttonRef = React.createRef();
|
|
18
20
|
// Check if the component is mounted
|
|
19
21
|
const isMounted = React.useRef(true);
|
|
20
22
|
React.useEffect(() => {
|
|
@@ -23,23 +25,23 @@ export function LoadingButton(props) {
|
|
|
23
25
|
};
|
|
24
26
|
}, []);
|
|
25
27
|
// Layout
|
|
26
|
-
return (React.createElement(Button, { disabled: loading, endIcon: localEndIcon, onClick: async (event) => {
|
|
28
|
+
return (React.createElement(Button, { disabled: loading, ref: buttonRef, endIcon: localEndIcon, onClick: async (event) => {
|
|
27
29
|
if (onClick) {
|
|
30
|
+
// Disable the button
|
|
31
|
+
buttonRef.current.disabled = true;
|
|
28
32
|
// Update state
|
|
29
33
|
setLoading(true);
|
|
34
|
+
// https://stackoverflow.com/questions/38508420/how-to-know-if-a-function-is-async
|
|
30
35
|
// const AsyncFunction = (async () => {}).constructor;
|
|
31
36
|
// onClick instanceof AsyncFunction
|
|
32
|
-
|
|
33
|
-
// May long time running
|
|
34
|
-
await onClick(event);
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
onClick(event);
|
|
38
|
-
}
|
|
37
|
+
await onClick(event);
|
|
39
38
|
// Warning: Can't perform a React state update on an unmounted component
|
|
40
39
|
// It's necessary to check the component is mounted now
|
|
41
|
-
if (isMounted.current)
|
|
40
|
+
if (isMounted.current) {
|
|
42
41
|
setLoading(false);
|
|
42
|
+
// Enable the button
|
|
43
|
+
buttonRef.current.disabled = false;
|
|
44
|
+
}
|
|
43
45
|
}
|
|
44
46
|
}, ...rest }));
|
|
45
47
|
}
|
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.89",
|
|
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/app/ServiceApp.ts
CHANGED
|
@@ -69,8 +69,9 @@ export class ServiceApp<
|
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Go to the login page
|
|
72
|
+
* @param tryLogin Try to login again
|
|
72
73
|
*/
|
|
73
|
-
override toLoginPage() {
|
|
74
|
+
override toLoginPage(tryLogin?: boolean) {
|
|
74
75
|
const coreUrl = this.settings.webUrl;
|
|
75
76
|
window.location.href =
|
|
76
77
|
coreUrl +
|
|
@@ -79,7 +80,8 @@ export class ServiceApp<
|
|
|
79
80
|
'&' +
|
|
80
81
|
DomUtils.CultureField +
|
|
81
82
|
'=' +
|
|
82
|
-
this.culture
|
|
83
|
+
this.culture +
|
|
84
|
+
(tryLogin ? '' : '&tryLogin=false');
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
/**
|
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);
|
package/src/mu/LoadingButton.tsx
CHANGED
|
@@ -38,6 +38,9 @@ export function LoadingButton(props: LoadingButtonProps) {
|
|
|
38
38
|
endIcon
|
|
39
39
|
);
|
|
40
40
|
|
|
41
|
+
// Button ref
|
|
42
|
+
const buttonRef = React.createRef<HTMLButtonElement>();
|
|
43
|
+
|
|
41
44
|
// Check if the component is mounted
|
|
42
45
|
const isMounted = React.useRef(true);
|
|
43
46
|
|
|
@@ -51,24 +54,29 @@ export function LoadingButton(props: LoadingButtonProps) {
|
|
|
51
54
|
return (
|
|
52
55
|
<Button
|
|
53
56
|
disabled={loading}
|
|
57
|
+
ref={buttonRef}
|
|
54
58
|
endIcon={localEndIcon}
|
|
55
59
|
onClick={async (event) => {
|
|
56
60
|
if (onClick) {
|
|
61
|
+
// Disable the button
|
|
62
|
+
buttonRef.current!.disabled = true;
|
|
63
|
+
|
|
57
64
|
// Update state
|
|
58
65
|
setLoading(true);
|
|
59
66
|
|
|
67
|
+
// https://stackoverflow.com/questions/38508420/how-to-know-if-a-function-is-async
|
|
60
68
|
// const AsyncFunction = (async () => {}).constructor;
|
|
61
69
|
// onClick instanceof AsyncFunction
|
|
62
|
-
|
|
63
|
-
// May long time running
|
|
64
|
-
await onClick(event);
|
|
65
|
-
} else {
|
|
66
|
-
onClick(event);
|
|
67
|
-
}
|
|
70
|
+
await onClick(event);
|
|
68
71
|
|
|
69
72
|
// Warning: Can't perform a React state update on an unmounted component
|
|
70
73
|
// It's necessary to check the component is mounted now
|
|
71
|
-
if (isMounted.current)
|
|
74
|
+
if (isMounted.current) {
|
|
75
|
+
setLoading(false);
|
|
76
|
+
|
|
77
|
+
// Enable the button
|
|
78
|
+
buttonRef.current!.disabled = false;
|
|
79
|
+
}
|
|
72
80
|
}
|
|
73
81
|
}}
|
|
74
82
|
{...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);
|