@etsoo/react 1.4.88 → 1.4.92

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.
@@ -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
- * More button label
14
+ * Search field template
14
15
  */
15
- moreLabel?: string;
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, LoadingButton } from '..';
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 { children, defaultOrderBy, headerRenderer, autoLoad = headerRenderer == null, 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 items = await loadData(loadProps);
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(true);
77
+ };
65
78
  React.useEffect(() => {
66
79
  if (autoLoad)
67
80
  loadDataLocal();
@@ -71,15 +84,14 @@ export function ListMoreDisplay(props) {
71
84
  ref.isMounted = false;
72
85
  };
73
86
  }, []);
74
- // Loading
75
- if (states.items == null)
76
- return React.createElement(CircularProgress, { size: 20 });
77
- return (React.createElement(Card, { ...rest },
78
- React.createElement(CardHeader, { title: headerTitle }),
79
- React.createElement(CardContent, { sx: {
80
- paddingTop: 0,
81
- paddingBottom: states.completed ? 0 : 'inherit'
82
- } }, states.items.map((item, index) => children(item, index))),
83
- !states.completed && (React.createElement(CardActions, { sx: { justifyContent: 'flex-end' } },
84
- React.createElement(LoadingButton, { onClick: async () => await loadDataLocal() }, moreLabel)))));
87
+ return (React.createElement(React.Fragment, null,
88
+ headerRenderer && headerRenderer(reset),
89
+ React.createElement(Card, { ...rest },
90
+ React.createElement(CardHeader, { title: headerTitle }),
91
+ React.createElement(CardContent, { sx: {
92
+ paddingTop: 0,
93
+ paddingBottom: states.completed ? 0 : 'inherit'
94
+ } }, states.items == null ? (React.createElement(CircularProgress, { size: 20 })) : (states.items.map((item, index) => children(item, index)))),
95
+ !states.completed && (React.createElement(CardActions, { sx: { justifyContent: 'flex-end' } },
96
+ React.createElement(LoadingButton, { onClick: async () => await loadDataLocal() }, moreLabel))))));
85
97
  }
@@ -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
- if (onClick.constructor.name === 'AsyncFunction') {
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/react",
3
- "version": "1.4.88",
3
+ "version": "1.4.92",
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.35",
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"
@@ -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, LoadingButton } from '..';
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<T> extends CardProps, GridLoader<T> {
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
- * More button label
35
+ * Search field template
28
36
  */
29
- moreLabel?: string;
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<T extends {}>(props: ListMoreDisplayProps<T>) {
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
- autoLoad = true,
51
71
  children,
52
72
  defaultOrderBy,
73
+ headerRenderer,
74
+ autoLoad = headerRenderer == null,
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 items = await loadData(loadProps);
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) setStates({ items, completed: !hasNextPage });
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(true);
161
+ };
162
+
124
163
  React.useEffect(() => {
125
164
  if (autoLoad) loadDataLocal();
126
165
  }, [autoLoad]);
@@ -131,27 +170,33 @@ export function ListMoreDisplay<T extends {}>(props: ListMoreDisplayProps<T>) {
131
170
  };
132
171
  }, []);
133
172
 
134
- // Loading
135
- if (states.items == null) return <CircularProgress size={20} />;
136
-
137
173
  return (
138
- <Card {...rest}>
139
- <CardHeader title={headerTitle}></CardHeader>
140
- <CardContent
141
- sx={{
142
- paddingTop: 0,
143
- paddingBottom: states.completed ? 0 : 'inherit'
144
- }}
145
- >
146
- {states.items.map((item, index) => children(item, index))}
147
- </CardContent>
148
- {!states.completed && (
149
- <CardActions sx={{ justifyContent: 'flex-end' }}>
150
- <LoadingButton onClick={async () => await loadDataLocal()}>
151
- {moreLabel}
152
- </LoadingButton>
153
- </CardActions>
154
- )}
155
- </Card>
174
+ <React.Fragment>
175
+ {headerRenderer && headerRenderer(reset)}
176
+ <Card {...rest}>
177
+ <CardHeader title={headerTitle}></CardHeader>
178
+ <CardContent
179
+ sx={{
180
+ paddingTop: 0,
181
+ paddingBottom: states.completed ? 0 : 'inherit'
182
+ }}
183
+ >
184
+ {states.items == null ? (
185
+ <CircularProgress size={20} />
186
+ ) : (
187
+ states.items.map((item, index) => children(item, index))
188
+ )}
189
+ </CardContent>
190
+ {!states.completed && (
191
+ <CardActions sx={{ justifyContent: 'flex-end' }}>
192
+ <LoadingButton
193
+ onClick={async () => await loadDataLocal()}
194
+ >
195
+ {moreLabel}
196
+ </LoadingButton>
197
+ </CardActions>
198
+ )}
199
+ </Card>
200
+ </React.Fragment>
156
201
  );
157
202
  }
@@ -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
- if (onClick.constructor.name === 'AsyncFunction') {
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) setLoading(false);
67
+ if (isMounted.current) {
68
+ setLoading(false);
69
+ }
72
70
  }
73
71
  }}
74
72
  {...rest}