@etsoo/react 1.1.80 → 1.1.84
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/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/lib/mu/EmailInput.d.ts +11 -0
- package/lib/mu/EmailInput.js +15 -0
- package/lib/mu/LoadingButton.d.ts +16 -0
- package/lib/mu/LoadingButton.js +45 -0
- package/lib/mu/NotifierMU.js +21 -22
- package/lib/mu/SearchBar.d.ts +5 -0
- package/lib/mu/SearchBar.js +2 -2
- package/lib/mu/pages/DataGridPage.js +2 -2
- package/lib/mu/pages/FixedListPage.js +2 -2
- package/lib/mu/pages/ResponsivePage.js +2 -2
- package/lib/mu/pages/SearchPageProps.d.ts +5 -0
- package/lib/mu/pages/TablePage.js +2 -2
- package/package.json +3 -3
- package/src/index.ts +2 -0
- package/src/mu/EmailInput.tsx +24 -0
- package/src/mu/LoadingButton.tsx +77 -0
- package/src/mu/NotifierMU.tsx +29 -21
- package/src/mu/SearchBar.tsx +8 -1
- package/src/mu/pages/DataGridPage.tsx +2 -1
- package/src/mu/pages/FixedListPage.tsx +2 -1
- package/src/mu/pages/ResponsivePage.tsx +2 -1
- package/src/mu/pages/SearchPageProps.ts +6 -0
- package/src/mu/pages/TablePage.tsx +10 -2
package/lib/index.d.ts
CHANGED
|
@@ -27,11 +27,13 @@ export * from './mu/DataGridEx';
|
|
|
27
27
|
export * from './mu/DataGridRenderers';
|
|
28
28
|
export * from './mu/DialogButton';
|
|
29
29
|
export * from './mu/DraggablePaperComponent';
|
|
30
|
+
export * from './mu/EmailInput';
|
|
30
31
|
export * from './mu/FabBox';
|
|
31
32
|
export * from './mu/FlexBox';
|
|
32
33
|
export * from './mu/IconButtonLink';
|
|
33
34
|
export * from './mu/InputField';
|
|
34
35
|
export * from './mu/ItemList';
|
|
36
|
+
export * from './mu/LoadingButton';
|
|
35
37
|
export * from './mu/MoreFab';
|
|
36
38
|
export * from './mu/MUGlobal';
|
|
37
39
|
export * from './mu/NotifierMU';
|
package/lib/index.js
CHANGED
|
@@ -30,11 +30,13 @@ export * from './mu/DataGridEx';
|
|
|
30
30
|
export * from './mu/DataGridRenderers';
|
|
31
31
|
export * from './mu/DialogButton';
|
|
32
32
|
export * from './mu/DraggablePaperComponent';
|
|
33
|
+
export * from './mu/EmailInput';
|
|
33
34
|
export * from './mu/FabBox';
|
|
34
35
|
export * from './mu/FlexBox';
|
|
35
36
|
export * from './mu/IconButtonLink';
|
|
36
37
|
export * from './mu/InputField';
|
|
37
38
|
export * from './mu/ItemList';
|
|
39
|
+
export * from './mu/LoadingButton';
|
|
38
40
|
export * from './mu/MoreFab';
|
|
39
41
|
export * from './mu/MUGlobal';
|
|
40
42
|
export * from './mu/NotifierMU';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { TextFieldProps } from '@mui/material';
|
|
3
|
+
/**
|
|
4
|
+
* Email input props
|
|
5
|
+
*/
|
|
6
|
+
export declare type EmailInputProps = Omit<TextFieldProps, 'type'> & {};
|
|
7
|
+
/**
|
|
8
|
+
* Email input
|
|
9
|
+
* @param props Props
|
|
10
|
+
*/
|
|
11
|
+
export declare function EmailInput(props: EmailInputProps): JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { TextField } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Email input
|
|
5
|
+
* @param props Props
|
|
6
|
+
*/
|
|
7
|
+
export function EmailInput(props) {
|
|
8
|
+
var _a;
|
|
9
|
+
// Destruct
|
|
10
|
+
const { inputProps = {}, ...rest } = props;
|
|
11
|
+
// Default max length
|
|
12
|
+
(_a = inputProps.maxLength) !== null && _a !== void 0 ? _a : (inputProps.maxLength = 128);
|
|
13
|
+
// Layout
|
|
14
|
+
return (React.createElement(TextField, { type: "email", fullWidth: true, inputProps: inputProps, ...rest }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { ButtonProps, CircularProgressProps } from '@mui/material';
|
|
3
|
+
/**
|
|
4
|
+
* Loading button props
|
|
5
|
+
*/
|
|
6
|
+
export declare type LoadingButtonProps = ButtonProps & {
|
|
7
|
+
/**
|
|
8
|
+
* Loading icon props
|
|
9
|
+
*/
|
|
10
|
+
loadingIconProps?: CircularProgressProps;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Loading button
|
|
14
|
+
* @param props Props
|
|
15
|
+
*/
|
|
16
|
+
export declare function LoadingButton(props: LoadingButtonProps): JSX.Element;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Button, CircularProgress } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Loading button
|
|
5
|
+
* @param props Props
|
|
6
|
+
*/
|
|
7
|
+
export function LoadingButton(props) {
|
|
8
|
+
var _a;
|
|
9
|
+
// Destruct
|
|
10
|
+
const { endIcon, loadingIconProps = {}, onClick, ...rest } = props;
|
|
11
|
+
// Default size
|
|
12
|
+
(_a = loadingIconProps.size) !== null && _a !== void 0 ? _a : (loadingIconProps.size = 12);
|
|
13
|
+
// State
|
|
14
|
+
// https://stackoverflow.com/questions/55265255/react-usestate-hook-event-handler-using-initial-state
|
|
15
|
+
const [loading, setLoading] = React.useState(false);
|
|
16
|
+
// Icon
|
|
17
|
+
const localEndIcon = loading ? (React.createElement(CircularProgress, { ...loadingIconProps })) : (endIcon);
|
|
18
|
+
// Check if the component is mounted
|
|
19
|
+
const isMounted = React.useRef(true);
|
|
20
|
+
React.useEffect(() => {
|
|
21
|
+
return () => {
|
|
22
|
+
isMounted.current = false;
|
|
23
|
+
};
|
|
24
|
+
}, []);
|
|
25
|
+
// Layout
|
|
26
|
+
return (React.createElement(Button, { disabled: loading, endIcon: localEndIcon, onClick: async (event) => {
|
|
27
|
+
if (onClick) {
|
|
28
|
+
// Update state
|
|
29
|
+
setLoading(true);
|
|
30
|
+
// const AsyncFunction = (async () => {}).constructor;
|
|
31
|
+
// onClick instanceof AsyncFunction
|
|
32
|
+
if (onClick.constructor.name === 'AsyncFunction') {
|
|
33
|
+
// May long time running
|
|
34
|
+
await onClick(event);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
onClick(event);
|
|
38
|
+
}
|
|
39
|
+
// Warning: Can't perform a React state update on an unmounted component
|
|
40
|
+
// It's necessary to check the component is mounted now
|
|
41
|
+
if (isMounted.current)
|
|
42
|
+
setLoading(false);
|
|
43
|
+
}
|
|
44
|
+
}, ...rest }));
|
|
45
|
+
}
|
package/lib/mu/NotifierMU.js
CHANGED
|
@@ -5,6 +5,7 @@ import React from 'react';
|
|
|
5
5
|
import { Labels } from '../app/Labels';
|
|
6
6
|
import { NotificationReact, NotifierReact } from '../notifier/Notifier';
|
|
7
7
|
import { DraggablePaperComponent } from './DraggablePaperComponent';
|
|
8
|
+
import { LoadingButton } from './LoadingButton';
|
|
8
9
|
// Custom icon dialog title bar
|
|
9
10
|
const IconDialogTitle = styled(DialogTitle) `
|
|
10
11
|
${({ theme }) => `
|
|
@@ -106,12 +107,13 @@ export class NotificationMU extends NotificationReact {
|
|
|
106
107
|
const labels = Labels.NotificationMU;
|
|
107
108
|
const title = (_a = this.title) !== null && _a !== void 0 ? _a : labels.promptTitle;
|
|
108
109
|
const { cancelLabel = labels.promptCancel, okLabel = labels.promptOK, inputs, type, ...rest } = this.inputProps;
|
|
109
|
-
const handleSubmit = (event) => {
|
|
110
|
-
|
|
110
|
+
const handleSubmit = async (event) => {
|
|
111
|
+
// Result
|
|
112
|
+
let result = undefined;
|
|
111
113
|
if (this.onReturn) {
|
|
112
114
|
// Inputs case, no HTMLForm set to value, set the current form
|
|
113
115
|
if (inputs && value == null)
|
|
114
|
-
value = event.currentTarget;
|
|
116
|
+
value = event.currentTarget.form;
|
|
115
117
|
if (inputRef.current) {
|
|
116
118
|
if (type === 'date') {
|
|
117
119
|
const dateValue = inputRef.current.valueAsDate;
|
|
@@ -119,8 +121,7 @@ export class NotificationMU extends NotificationReact {
|
|
|
119
121
|
inputRef.current.focus();
|
|
120
122
|
return;
|
|
121
123
|
}
|
|
122
|
-
|
|
123
|
-
return;
|
|
124
|
+
result = this.onReturn(dateValue);
|
|
124
125
|
}
|
|
125
126
|
else if (type === 'number') {
|
|
126
127
|
const numberValue = inputRef.current.valueAsNumber;
|
|
@@ -128,34 +129,35 @@ export class NotificationMU extends NotificationReact {
|
|
|
128
129
|
inputRef.current.focus();
|
|
129
130
|
return;
|
|
130
131
|
}
|
|
131
|
-
|
|
132
|
-
return;
|
|
132
|
+
result = this.onReturn(numberValue);
|
|
133
133
|
}
|
|
134
134
|
else if (type === 'switch') {
|
|
135
135
|
const boolValue = inputRef.current.value == 'true';
|
|
136
|
-
|
|
137
|
-
return;
|
|
136
|
+
result = this.onReturn(boolValue);
|
|
138
137
|
}
|
|
139
138
|
else {
|
|
140
|
-
const textValue = inputRef.current.value;
|
|
141
|
-
if (textValue) {
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
const textValue = inputRef.current.value.trim();
|
|
140
|
+
if (textValue === '') {
|
|
141
|
+
inputRef.current.focus();
|
|
142
|
+
return;
|
|
144
143
|
}
|
|
145
|
-
|
|
144
|
+
result = this.onReturn(textValue);
|
|
146
145
|
}
|
|
147
146
|
}
|
|
148
147
|
else if (value != null) {
|
|
149
|
-
|
|
150
|
-
return;
|
|
148
|
+
result = this.onReturn(value);
|
|
151
149
|
}
|
|
152
150
|
}
|
|
151
|
+
// Get the value
|
|
152
|
+
// returns false to prevent default dismiss
|
|
153
|
+
const v = await result;
|
|
154
|
+
if (v === false)
|
|
155
|
+
return;
|
|
153
156
|
this.dismiss();
|
|
154
157
|
};
|
|
155
158
|
let localInputs;
|
|
156
159
|
let inputRef = React.createRef();
|
|
157
160
|
let value = undefined;
|
|
158
|
-
let formChangeHandler;
|
|
159
161
|
if (inputs == null) {
|
|
160
162
|
if (type === 'switch') {
|
|
161
163
|
localInputs = (React.createElement(Switch, { inputRef: inputRef, ...rest, value: "true", autoFocus: true }));
|
|
@@ -169,12 +171,9 @@ export class NotificationMU extends NotificationReact {
|
|
|
169
171
|
}
|
|
170
172
|
else {
|
|
171
173
|
localInputs = inputs;
|
|
172
|
-
formChangeHandler = (event) => {
|
|
173
|
-
value = event.currentTarget;
|
|
174
|
-
};
|
|
175
174
|
}
|
|
176
175
|
return (React.createElement(Dialog, { key: this.id, open: this.open, PaperComponent: DraggablePaperComponent, className: className },
|
|
177
|
-
React.createElement("form",
|
|
176
|
+
React.createElement("form", null,
|
|
178
177
|
React.createElement(IconDialogTitle, { className: classes.iconTitle, id: "draggable-dialog-title" },
|
|
179
178
|
React.createElement(Info, { color: "primary" }),
|
|
180
179
|
React.createElement("span", { className: "dialogTitle" }, title)),
|
|
@@ -183,7 +182,7 @@ export class NotificationMU extends NotificationReact {
|
|
|
183
182
|
localInputs),
|
|
184
183
|
React.createElement(DialogActions, null,
|
|
185
184
|
React.createElement(Button, { color: "secondary", onClick: () => this.dismiss() }, cancelLabel),
|
|
186
|
-
React.createElement(
|
|
185
|
+
React.createElement(LoadingButton, { color: "primary", autoFocus: true, onClick: handleSubmit }, okLabel)))));
|
|
187
186
|
}
|
|
188
187
|
// Create loading
|
|
189
188
|
createLoading(_props, className, _classes) {
|
package/lib/mu/SearchBar.d.ts
CHANGED
package/lib/mu/SearchBar.js
CHANGED
|
@@ -53,7 +53,7 @@ const setChildState = (child, enabled) => {
|
|
|
53
53
|
*/
|
|
54
54
|
export function SearchBar(props) {
|
|
55
55
|
// Destruct
|
|
56
|
-
const { className, fields, onSubmit } = props;
|
|
56
|
+
const { className, fields, innerHeight = 40, onSubmit } = props;
|
|
57
57
|
// Labels
|
|
58
58
|
const labels = Labels.CommonPage;
|
|
59
59
|
// Spacing
|
|
@@ -235,7 +235,7 @@ export function SearchBar(props) {
|
|
|
235
235
|
if (form)
|
|
236
236
|
forms.form = form;
|
|
237
237
|
} },
|
|
238
|
-
React.createElement(Stack, { ref: dimensions[0][0], justifyContent: "center", alignItems: "center", direction: "row", spacing: 1, sx: {
|
|
238
|
+
React.createElement(Stack, { ref: dimensions[0][0], justifyContent: "center", alignItems: "center", direction: "row", spacing: 1, height: innerHeight, sx: {
|
|
239
239
|
'& > :not(style)': {
|
|
240
240
|
flexBasis: 'auto',
|
|
241
241
|
flexGrow: 0,
|
|
@@ -15,7 +15,7 @@ import { CommonPage } from './CommonPage';
|
|
|
15
15
|
export function DataGridPage(props) {
|
|
16
16
|
var _a, _b;
|
|
17
17
|
// Destruct
|
|
18
|
-
const { adjustHeight, fields, height, loadData, mRef, pageProps = {}, ...rest } = props;
|
|
18
|
+
const { adjustHeight, fields, height, loadData, mRef, sizeReadyMiliseconds = 100, pageProps = {}, ...rest } = props;
|
|
19
19
|
(_a = pageProps.paddings) !== null && _a !== void 0 ? _a : (pageProps.paddings = MUGlobal.pagePaddings);
|
|
20
20
|
// States
|
|
21
21
|
const [states, setStates] = React.useReducer((currentState, newState) => {
|
|
@@ -38,7 +38,7 @@ export function DataGridPage(props) {
|
|
|
38
38
|
return loadData({ ...json, ...rest });
|
|
39
39
|
};
|
|
40
40
|
// Watch container
|
|
41
|
-
const { dimensions } = useDimensions(1, undefined,
|
|
41
|
+
const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
|
|
42
42
|
const rect = dimensions[0][2];
|
|
43
43
|
React.useEffect(() => {
|
|
44
44
|
if (rect != null && rect.height > 50 && height == null) {
|
|
@@ -15,7 +15,7 @@ import { CommonPage } from './CommonPage';
|
|
|
15
15
|
export function FixedListPage(props) {
|
|
16
16
|
var _a;
|
|
17
17
|
// Destruct
|
|
18
|
-
const { adjustHeight, fields, loadData, mRef, pageProps = {}, ...rest } = props;
|
|
18
|
+
const { adjustHeight, fields, loadData, mRef, sizeReadyMiliseconds = 100, pageProps = {}, ...rest } = props;
|
|
19
19
|
(_a = pageProps.paddings) !== null && _a !== void 0 ? _a : (pageProps.paddings = MUGlobal.pagePaddings);
|
|
20
20
|
// States
|
|
21
21
|
const [states] = React.useState({});
|
|
@@ -44,7 +44,7 @@ export function FixedListPage(props) {
|
|
|
44
44
|
return loadData({ ...json, ...rest });
|
|
45
45
|
};
|
|
46
46
|
// Watch container
|
|
47
|
-
const { dimensions } = useDimensions(1, undefined,
|
|
47
|
+
const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
|
|
48
48
|
const rect = dimensions[0][2];
|
|
49
49
|
const list = React.useMemo(() => {
|
|
50
50
|
if (rect != null && rect.height > 50) {
|
|
@@ -16,7 +16,7 @@ import { CommonPage } from './CommonPage';
|
|
|
16
16
|
export function ResponsivePage(props) {
|
|
17
17
|
var _a, _b;
|
|
18
18
|
// Destruct
|
|
19
|
-
const { adjustHeight, columns, dataGridMinWidth = DataGridExCalColumns(columns).total, fields, height, loadData, innerItemRenderer, itemSize, mRef, pageProps = {}, ...rest } = props;
|
|
19
|
+
const { adjustHeight, columns, dataGridMinWidth = DataGridExCalColumns(columns).total, fields, height, loadData, innerItemRenderer, itemSize, mRef, sizeReadyMiliseconds = 100, pageProps = {}, ...rest } = props;
|
|
20
20
|
(_a = pageProps.paddings) !== null && _a !== void 0 ? _a : (pageProps.paddings = MUGlobal.pagePaddings);
|
|
21
21
|
// States
|
|
22
22
|
const [states, setStates] = React.useReducer((currentState, newState) => {
|
|
@@ -39,7 +39,7 @@ export function ResponsivePage(props) {
|
|
|
39
39
|
setStates({ data });
|
|
40
40
|
};
|
|
41
41
|
// Watch container
|
|
42
|
-
const { dimensions } = useDimensions(1, undefined,
|
|
42
|
+
const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
|
|
43
43
|
const rect = dimensions[0][2];
|
|
44
44
|
const showDataGrid = ((_b = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _b !== void 0 ? _b : 0) >= dataGridMinWidth;
|
|
45
45
|
React.useEffect(() => {
|
|
@@ -15,7 +15,7 @@ import { CommonPage, CommonPagePullContainer, CommonPageScrollContainer } from '
|
|
|
15
15
|
export function TablePage(props) {
|
|
16
16
|
var _a;
|
|
17
17
|
// Destruct
|
|
18
|
-
const { columns, fields, loadData, mRef, pageProps = {}, ...rest } = props;
|
|
18
|
+
const { columns, fields, loadData, mRef, sizeReadyMiliseconds = 100, pageProps = {}, ...rest } = props;
|
|
19
19
|
(_a = pageProps.paddings) !== null && _a !== void 0 ? _a : (pageProps.paddings = MUGlobal.pagePaddings);
|
|
20
20
|
// States
|
|
21
21
|
const [states] = React.useState({});
|
|
@@ -47,7 +47,7 @@ export function TablePage(props) {
|
|
|
47
47
|
return previousValue + ((_a = width !== null && width !== void 0 ? width : minWidth) !== null && _a !== void 0 ? _a : TableExMinWidth);
|
|
48
48
|
}, 0), [columns]);
|
|
49
49
|
// Watch container
|
|
50
|
-
const { dimensions } = useDimensions(1, undefined,
|
|
50
|
+
const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
|
|
51
51
|
const rect = dimensions[0][2];
|
|
52
52
|
const list = React.useMemo(() => {
|
|
53
53
|
if (rect != null && rect.height > 50 && rect.width >= totalWidth) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/react",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.84",
|
|
4
4
|
"description": "TypeScript ReactJs framework",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"@emotion/react": "^11.4.1",
|
|
45
45
|
"@emotion/style": "^0.8.0",
|
|
46
46
|
"@emotion/styled": "^11.3.0",
|
|
47
|
-
"@etsoo/appscript": "^1.0.
|
|
48
|
-
"@etsoo/notificationbase": "^1.0.
|
|
47
|
+
"@etsoo/appscript": "^1.0.87",
|
|
48
|
+
"@etsoo/notificationbase": "^1.0.78",
|
|
49
49
|
"@etsoo/shared": "^1.0.41",
|
|
50
50
|
"@mui/icons-material": "^5.0.0-rc.0",
|
|
51
51
|
"@mui/material": "^5.0.0-rc.0",
|
package/src/index.ts
CHANGED
|
@@ -32,11 +32,13 @@ export * from './mu/DataGridEx';
|
|
|
32
32
|
export * from './mu/DataGridRenderers';
|
|
33
33
|
export * from './mu/DialogButton';
|
|
34
34
|
export * from './mu/DraggablePaperComponent';
|
|
35
|
+
export * from './mu/EmailInput';
|
|
35
36
|
export * from './mu/FabBox';
|
|
36
37
|
export * from './mu/FlexBox';
|
|
37
38
|
export * from './mu/IconButtonLink';
|
|
38
39
|
export * from './mu/InputField';
|
|
39
40
|
export * from './mu/ItemList';
|
|
41
|
+
export * from './mu/LoadingButton';
|
|
40
42
|
export * from './mu/MoreFab';
|
|
41
43
|
export * from './mu/MUGlobal';
|
|
42
44
|
export * from './mu/NotifierMU';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TextField, TextFieldProps } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Email input props
|
|
6
|
+
*/
|
|
7
|
+
export type EmailInputProps = Omit<TextFieldProps, 'type'> & {};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Email input
|
|
11
|
+
* @param props Props
|
|
12
|
+
*/
|
|
13
|
+
export function EmailInput(props: EmailInputProps) {
|
|
14
|
+
// Destruct
|
|
15
|
+
const { inputProps = {}, ...rest } = props;
|
|
16
|
+
|
|
17
|
+
// Default max length
|
|
18
|
+
inputProps.maxLength ??= 128;
|
|
19
|
+
|
|
20
|
+
// Layout
|
|
21
|
+
return (
|
|
22
|
+
<TextField type="email" fullWidth inputProps={inputProps} {...rest} />
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
ButtonProps,
|
|
4
|
+
CircularProgress,
|
|
5
|
+
CircularProgressProps
|
|
6
|
+
} from '@mui/material';
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Loading button props
|
|
11
|
+
*/
|
|
12
|
+
export type LoadingButtonProps = ButtonProps & {
|
|
13
|
+
/**
|
|
14
|
+
* Loading icon props
|
|
15
|
+
*/
|
|
16
|
+
loadingIconProps?: CircularProgressProps;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Loading button
|
|
21
|
+
* @param props Props
|
|
22
|
+
*/
|
|
23
|
+
export function LoadingButton(props: LoadingButtonProps) {
|
|
24
|
+
// Destruct
|
|
25
|
+
const { endIcon, loadingIconProps = {}, onClick, ...rest } = props;
|
|
26
|
+
|
|
27
|
+
// Default size
|
|
28
|
+
loadingIconProps.size ??= 12;
|
|
29
|
+
|
|
30
|
+
// State
|
|
31
|
+
// https://stackoverflow.com/questions/55265255/react-usestate-hook-event-handler-using-initial-state
|
|
32
|
+
const [loading, setLoading] = React.useState(false);
|
|
33
|
+
|
|
34
|
+
// Icon
|
|
35
|
+
const localEndIcon = loading ? (
|
|
36
|
+
<CircularProgress {...loadingIconProps} />
|
|
37
|
+
) : (
|
|
38
|
+
endIcon
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// Check if the component is mounted
|
|
42
|
+
const isMounted = React.useRef(true);
|
|
43
|
+
|
|
44
|
+
React.useEffect(() => {
|
|
45
|
+
return () => {
|
|
46
|
+
isMounted.current = false;
|
|
47
|
+
};
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
// Layout
|
|
51
|
+
return (
|
|
52
|
+
<Button
|
|
53
|
+
disabled={loading}
|
|
54
|
+
endIcon={localEndIcon}
|
|
55
|
+
onClick={async (event) => {
|
|
56
|
+
if (onClick) {
|
|
57
|
+
// Update state
|
|
58
|
+
setLoading(true);
|
|
59
|
+
|
|
60
|
+
// const AsyncFunction = (async () => {}).constructor;
|
|
61
|
+
// onClick instanceof AsyncFunction
|
|
62
|
+
if (onClick.constructor.name === 'AsyncFunction') {
|
|
63
|
+
// May long time running
|
|
64
|
+
await onClick(event);
|
|
65
|
+
} else {
|
|
66
|
+
onClick(event);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Warning: Can't perform a React state update on an unmounted component
|
|
70
|
+
// It's necessary to check the component is mounted now
|
|
71
|
+
if (isMounted.current) setLoading(false);
|
|
72
|
+
}
|
|
73
|
+
}}
|
|
74
|
+
{...rest}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
package/src/mu/NotifierMU.tsx
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
} from '../notifier/Notifier';
|
|
40
40
|
import { DraggablePaperComponent } from './DraggablePaperComponent';
|
|
41
41
|
import { NotifierPromptProps } from './NotifierPromptProps';
|
|
42
|
+
import { LoadingButton } from './LoadingButton';
|
|
42
43
|
|
|
43
44
|
// Custom icon dialog title bar
|
|
44
45
|
const IconDialogTitle = styled(DialogTitle)`
|
|
@@ -220,12 +221,16 @@ export class NotificationMU extends NotificationReact {
|
|
|
220
221
|
...rest
|
|
221
222
|
} = this.inputProps as NotifierPromptProps;
|
|
222
223
|
|
|
223
|
-
const handleSubmit = (
|
|
224
|
-
event.
|
|
224
|
+
const handleSubmit = async (
|
|
225
|
+
event: React.MouseEvent<HTMLButtonElement>
|
|
226
|
+
) => {
|
|
227
|
+
// Result
|
|
228
|
+
let result: boolean | void | PromiseLike<boolean | void> =
|
|
229
|
+
undefined;
|
|
225
230
|
|
|
226
231
|
if (this.onReturn) {
|
|
227
232
|
// Inputs case, no HTMLForm set to value, set the current form
|
|
228
|
-
if (inputs && value == null) value = event.currentTarget;
|
|
233
|
+
if (inputs && value == null) value = event.currentTarget.form;
|
|
229
234
|
|
|
230
235
|
if (inputRef.current) {
|
|
231
236
|
if (type === 'date') {
|
|
@@ -234,29 +239,35 @@ export class NotificationMU extends NotificationReact {
|
|
|
234
239
|
inputRef.current.focus();
|
|
235
240
|
return;
|
|
236
241
|
}
|
|
237
|
-
|
|
242
|
+
result = this.onReturn(dateValue);
|
|
238
243
|
} else if (type === 'number') {
|
|
239
244
|
const numberValue = inputRef.current.valueAsNumber;
|
|
240
245
|
if (isNaN(numberValue)) {
|
|
241
246
|
inputRef.current.focus();
|
|
242
247
|
return;
|
|
243
248
|
}
|
|
244
|
-
|
|
249
|
+
result = this.onReturn(numberValue);
|
|
245
250
|
} else if (type === 'switch') {
|
|
246
251
|
const boolValue = inputRef.current.value == 'true';
|
|
247
|
-
|
|
252
|
+
result = this.onReturn(boolValue);
|
|
248
253
|
} else {
|
|
249
|
-
const textValue = inputRef.current.value;
|
|
250
|
-
if (textValue) {
|
|
251
|
-
|
|
254
|
+
const textValue = inputRef.current.value.trim();
|
|
255
|
+
if (textValue === '') {
|
|
256
|
+
inputRef.current.focus();
|
|
257
|
+
return;
|
|
252
258
|
}
|
|
253
|
-
|
|
259
|
+
result = this.onReturn(textValue);
|
|
254
260
|
}
|
|
255
261
|
} else if (value != null) {
|
|
256
|
-
|
|
262
|
+
result = this.onReturn(value);
|
|
257
263
|
}
|
|
258
264
|
}
|
|
259
265
|
|
|
266
|
+
// Get the value
|
|
267
|
+
// returns false to prevent default dismiss
|
|
268
|
+
const v = await result;
|
|
269
|
+
if (v === false) return;
|
|
270
|
+
|
|
260
271
|
this.dismiss();
|
|
261
272
|
};
|
|
262
273
|
|
|
@@ -264,10 +275,6 @@ export class NotificationMU extends NotificationReact {
|
|
|
264
275
|
let inputRef = React.createRef<HTMLInputElement>();
|
|
265
276
|
let value: any = undefined;
|
|
266
277
|
|
|
267
|
-
let formChangeHandler:
|
|
268
|
-
| React.FormEventHandler<HTMLFormElement>
|
|
269
|
-
| undefined;
|
|
270
|
-
|
|
271
278
|
if (inputs == null) {
|
|
272
279
|
if (type === 'switch') {
|
|
273
280
|
localInputs = (
|
|
@@ -293,9 +300,6 @@ export class NotificationMU extends NotificationReact {
|
|
|
293
300
|
}
|
|
294
301
|
} else {
|
|
295
302
|
localInputs = inputs;
|
|
296
|
-
formChangeHandler = (event) => {
|
|
297
|
-
value = event.currentTarget;
|
|
298
|
-
};
|
|
299
303
|
}
|
|
300
304
|
|
|
301
305
|
return (
|
|
@@ -305,7 +309,7 @@ export class NotificationMU extends NotificationReact {
|
|
|
305
309
|
PaperComponent={DraggablePaperComponent}
|
|
306
310
|
className={className}
|
|
307
311
|
>
|
|
308
|
-
<form
|
|
312
|
+
<form>
|
|
309
313
|
<IconDialogTitle
|
|
310
314
|
className={classes.iconTitle}
|
|
311
315
|
id="draggable-dialog-title"
|
|
@@ -324,9 +328,13 @@ export class NotificationMU extends NotificationReact {
|
|
|
324
328
|
>
|
|
325
329
|
{cancelLabel}
|
|
326
330
|
</Button>
|
|
327
|
-
<
|
|
331
|
+
<LoadingButton
|
|
332
|
+
color="primary"
|
|
333
|
+
autoFocus
|
|
334
|
+
onClick={handleSubmit}
|
|
335
|
+
>
|
|
328
336
|
{okLabel}
|
|
329
|
-
</
|
|
337
|
+
</LoadingButton>
|
|
330
338
|
</DialogActions>
|
|
331
339
|
</form>
|
|
332
340
|
</Dialog>
|
package/src/mu/SearchBar.tsx
CHANGED
|
@@ -20,6 +20,12 @@ export interface SearchBarProps {
|
|
|
20
20
|
*/
|
|
21
21
|
fields: React.ReactElement[];
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Inner height
|
|
25
|
+
* @default 40
|
|
26
|
+
*/
|
|
27
|
+
innerHeight?: number;
|
|
28
|
+
|
|
23
29
|
/**
|
|
24
30
|
* On submit callback
|
|
25
31
|
*/
|
|
@@ -78,7 +84,7 @@ const setChildState = (child: Element, enabled: boolean) => {
|
|
|
78
84
|
*/
|
|
79
85
|
export function SearchBar(props: SearchBarProps) {
|
|
80
86
|
// Destruct
|
|
81
|
-
const { className, fields, onSubmit } = props;
|
|
87
|
+
const { className, fields, innerHeight = 40, onSubmit } = props;
|
|
82
88
|
|
|
83
89
|
// Labels
|
|
84
90
|
const labels = Labels.CommonPage;
|
|
@@ -316,6 +322,7 @@ export function SearchBar(props: SearchBarProps) {
|
|
|
316
322
|
alignItems="center"
|
|
317
323
|
direction="row"
|
|
318
324
|
spacing={1}
|
|
325
|
+
height={innerHeight}
|
|
319
326
|
sx={{
|
|
320
327
|
'& > :not(style)': {
|
|
321
328
|
flexBasis: 'auto',
|
|
@@ -30,6 +30,7 @@ export function DataGridPage<T>(props: DataGridPageProps<T>) {
|
|
|
30
30
|
height,
|
|
31
31
|
loadData,
|
|
32
32
|
mRef,
|
|
33
|
+
sizeReadyMiliseconds = 100,
|
|
33
34
|
pageProps = {},
|
|
34
35
|
...rest
|
|
35
36
|
} = props;
|
|
@@ -63,7 +64,7 @@ export function DataGridPage<T>(props: DataGridPageProps<T>) {
|
|
|
63
64
|
};
|
|
64
65
|
|
|
65
66
|
// Watch container
|
|
66
|
-
const { dimensions } = useDimensions(1, undefined,
|
|
67
|
+
const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
|
|
67
68
|
const rect = dimensions[0][2];
|
|
68
69
|
|
|
69
70
|
React.useEffect(() => {
|
|
@@ -30,6 +30,7 @@ export function FixedListPage<T>(
|
|
|
30
30
|
fields,
|
|
31
31
|
loadData,
|
|
32
32
|
mRef,
|
|
33
|
+
sizeReadyMiliseconds = 100,
|
|
33
34
|
pageProps = {},
|
|
34
35
|
...rest
|
|
35
36
|
} = props;
|
|
@@ -74,7 +75,7 @@ export function FixedListPage<T>(
|
|
|
74
75
|
};
|
|
75
76
|
|
|
76
77
|
// Watch container
|
|
77
|
-
const { dimensions } = useDimensions(1, undefined,
|
|
78
|
+
const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
|
|
78
79
|
const rect = dimensions[0][2];
|
|
79
80
|
const list = React.useMemo(() => {
|
|
80
81
|
if (rect != null && rect.height > 50) {
|
|
@@ -35,6 +35,7 @@ export function ResponsivePage<T>(props: ResponsePageProps<T>) {
|
|
|
35
35
|
innerItemRenderer,
|
|
36
36
|
itemSize,
|
|
37
37
|
mRef,
|
|
38
|
+
sizeReadyMiliseconds = 100,
|
|
38
39
|
pageProps = {},
|
|
39
40
|
...rest
|
|
40
41
|
} = props;
|
|
@@ -68,7 +69,7 @@ export function ResponsivePage<T>(props: ResponsePageProps<T>) {
|
|
|
68
69
|
};
|
|
69
70
|
|
|
70
71
|
// Watch container
|
|
71
|
-
const { dimensions } = useDimensions(1, undefined,
|
|
72
|
+
const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
|
|
72
73
|
const rect = dimensions[0][2];
|
|
73
74
|
const showDataGrid = (rect?.width ?? 0) >= dataGridMinWidth;
|
|
74
75
|
|
|
@@ -20,7 +20,15 @@ import { TablePageProps } from './TablePageProps';
|
|
|
20
20
|
*/
|
|
21
21
|
export function TablePage<T>(props: TablePageProps<T>) {
|
|
22
22
|
// Destruct
|
|
23
|
-
const {
|
|
23
|
+
const {
|
|
24
|
+
columns,
|
|
25
|
+
fields,
|
|
26
|
+
loadData,
|
|
27
|
+
mRef,
|
|
28
|
+
sizeReadyMiliseconds = 100,
|
|
29
|
+
pageProps = {},
|
|
30
|
+
...rest
|
|
31
|
+
} = props;
|
|
24
32
|
|
|
25
33
|
pageProps.paddings ??= MUGlobal.pagePaddings;
|
|
26
34
|
|
|
@@ -66,7 +74,7 @@ export function TablePage<T>(props: TablePageProps<T>) {
|
|
|
66
74
|
);
|
|
67
75
|
|
|
68
76
|
// Watch container
|
|
69
|
-
const { dimensions } = useDimensions(1, undefined,
|
|
77
|
+
const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
|
|
70
78
|
const rect = dimensions[0][2];
|
|
71
79
|
const list = React.useMemo(() => {
|
|
72
80
|
if (rect != null && rect.height > 50 && rect.width >= totalWidth) {
|