@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.
@@ -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
@@ -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
@@ -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
  */
@@ -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 (search) {
85
+ if (autoAddBlankItem) {
86
86
  SharedUtils.addBlankItem(result, idField, labelField);
87
87
  }
88
88
  setOptions(result);
@@ -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
- if (onClick.constructor.name === 'AsyncFunction') {
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
  }
@@ -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 (search)
18
+ if (autoAddBlankItem)
19
19
  Utils.addBlankItem(options);
20
20
  // Layout
21
21
  return React.createElement(SelectEx, { options: options, search: search, ...rest });
@@ -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
  */
@@ -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 (search) {
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.85",
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.33",
53
+ "@etsoo/appscript": "^1.2.37",
54
54
  "@etsoo/notificationbase": "^1.1.1",
55
- "@etsoo/shared": "^1.1.9",
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": "^5.1.3",
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.38",
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.3.0",
75
+ "react-imask": "^6.4.0",
76
76
  "react-window": "^1.8.6"
77
77
  },
78
78
  "devDependencies": {
79
- "@babel/cli": "^7.16.8",
80
- "@babel/core": "^7.16.12",
81
- "@babel/plugin-transform-runtime": "^7.16.10",
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.16.8",
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.4.7",
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"
@@ -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
  /**
@@ -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 (search) {
165
+ if (autoAddBlankItem) {
160
166
  SharedUtils.addBlankItem(result, idField, labelField);
161
167
  }
162
168
  setOptions(result);
@@ -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
- if (onClick.constructor.name === 'AsyncFunction') {
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) setLoading(false);
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}
@@ -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 (search) Utils.addBlankItem(options);
28
+ if (autoAddBlankItem) Utils.addBlankItem(options);
29
29
 
30
30
  // Layout
31
31
  return <SelectEx options={options} search={search} {...rest} />;
@@ -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 (search) {
181
+ if (autoAddBlankItem) {
176
182
  Utils.addBlankItem(result, idField, labelField);
177
183
  }
178
184
  setOptions(result);