@etsoo/materialui 1.2.41 → 1.2.43

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,14 +1,21 @@
1
1
  import { DataTypes, IdDefaultType } from "@etsoo/shared";
2
2
  import { Box, Stack, SxProps, Theme } from "@mui/material";
3
3
  import React from "react";
4
- import { ListChildComponentProps } from "react-window";
4
+ import {
5
+ GridOnScrollProps,
6
+ ListChildComponentProps,
7
+ ListOnScrollProps,
8
+ VariableSizeGrid
9
+ } from "react-window";
5
10
  import {
6
11
  GridColumn,
7
12
  GridDataGet,
8
13
  GridJsonData,
9
14
  GridLoadDataProps,
15
+ GridLoaderStates,
10
16
  GridMethodRef,
11
17
  ReactUtils,
18
+ ScrollerListRef,
12
19
  useCombinedRefs,
13
20
  useDimensions
14
21
  } from "@etsoo/react";
@@ -26,6 +33,7 @@ import {
26
33
  } from "./ScrollerListEx";
27
34
  import { SearchBar } from "./SearchBar";
28
35
  import { Labels } from "./app/Labels";
36
+ import { GridDataCacheType } from "./GridDataCacheType";
29
37
 
30
38
  /**
31
39
  * ResponsibleContainer props
@@ -36,7 +44,14 @@ export type ResponsibleContainerProps<
36
44
  D extends DataTypes.Keys<T> = IdDefaultType<T>
37
45
  > = Omit<
38
46
  DataGridExProps<T, D>,
39
- "height" | "itemKey" | "loadData" | "mRef" | "onScroll" | "onItemsRendered"
47
+ | "height"
48
+ | "itemKey"
49
+ | "loadData"
50
+ | "mRef"
51
+ | "onScroll"
52
+ | "onItemsRendered"
53
+ | "onInitLoad"
54
+ | "onUpdateRows"
40
55
  > & {
41
56
  /**
42
57
  * Height will be deducted
@@ -75,7 +90,9 @@ export type ResponsibleContainerProps<
75
90
  /**
76
91
  * Search fields
77
92
  */
78
- fields?: React.ReactElement[];
93
+ fields?:
94
+ | React.ReactElement[]
95
+ | ((data: DataTypes.BasicTemplateType<F>) => React.ReactElement[]);
79
96
 
80
97
  /**
81
98
  * Search field template
@@ -151,6 +168,7 @@ interface LocalRefs<T> {
151
168
  rect?: DOMRect;
152
169
  ref?: GridMethodRef<T>;
153
170
  mounted?: boolean;
171
+ initLoaded?: boolean;
154
172
  }
155
173
 
156
174
  function defaultContainerBoxSx(
@@ -180,6 +198,8 @@ export function ResponsibleContainer<
180
198
  const {
181
199
  adjustHeight,
182
200
  adjustFabHeight,
201
+ cacheKey,
202
+ cacheMinutes = 120,
183
203
  columns,
184
204
  containerBoxSx = defaultContainerBoxSx,
185
205
  dataGridMinWidth = Math.max(576, DataGridExCalColumns(columns).total),
@@ -223,6 +243,10 @@ export function ResponsibleContainer<
223
243
  const localLoadData = (props: GridLoadDataProps) => {
224
244
  state.mounted = true;
225
245
  const data = GridDataGet(props, fieldTemplate);
246
+
247
+ if (cacheKey)
248
+ sessionStorage.setItem(`${cacheKey}-searchbar`, JSON.stringify(data));
249
+
226
250
  return loadData(data);
227
251
  };
228
252
 
@@ -260,6 +284,70 @@ export function ResponsibleContainer<
260
284
  }
261
285
  );
262
286
 
287
+ type DataType = GridDataCacheType<T>;
288
+
289
+ const onUpdateRows = (rows: T[], state: GridLoaderStates<T>) => {
290
+ if (state.currentPage > 0 && cacheKey) {
291
+ const data: DataType = { rows, state, creation: new Date().valueOf() };
292
+ sessionStorage.setItem(cacheKey, JSON.stringify(data));
293
+ }
294
+ };
295
+
296
+ const onInitLoad = (
297
+ ref: VariableSizeGrid<T> | ScrollerListRef
298
+ ): [T[], Partial<GridLoaderStates<T>>?] | null | undefined => {
299
+ // Avoid repeatedly load from cache
300
+ if (refs.current.initLoaded || !cacheKey) return undefined;
301
+
302
+ // Cache data
303
+ const cacheData = sessionStorage.getItem(cacheKey);
304
+ if (cacheData) {
305
+ const { rows, state, creation } = JSON.parse(cacheData) as DataType;
306
+
307
+ // 120 minutes
308
+ if (new Date().valueOf() - creation > cacheMinutes * 60000) {
309
+ sessionStorage.removeItem(cacheKey);
310
+ return undefined;
311
+ }
312
+
313
+ // Scroll position
314
+ const scrollData = sessionStorage.getItem(`${cacheKey}-scroll`);
315
+ if (scrollData) {
316
+ if ("resetAfterColumnIndex" in ref) {
317
+ const { scrollLeft, scrollTop } = JSON.parse(
318
+ scrollData
319
+ ) as GridOnScrollProps;
320
+
321
+ globalThis.setTimeout(
322
+ () => ref.scrollTo({ scrollLeft, scrollTop }),
323
+ 0
324
+ );
325
+ } else {
326
+ const { scrollOffset } = JSON.parse(scrollData) as ListOnScrollProps;
327
+
328
+ globalThis.setTimeout(() => ref.scrollTo(scrollOffset), 0);
329
+ }
330
+ }
331
+
332
+ // Update flag value
333
+ refs.current.initLoaded = true;
334
+
335
+ // Return cached rows and state
336
+ return [rows, state];
337
+ }
338
+
339
+ return undefined;
340
+ };
341
+
342
+ const onListScroll = (props: ListOnScrollProps) => {
343
+ if (!cacheKey || !refs.current.initLoaded) return;
344
+ sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
345
+ };
346
+ const onGridScroll = (props: GridOnScrollProps) => {
347
+ if (!cacheKey || !refs.current.initLoaded) return;
348
+ sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
349
+ };
350
+
263
351
  // Rect
264
352
  const rect = dimensions[0][2];
265
353
 
@@ -309,7 +397,10 @@ export function ResponsibleContainer<
309
397
  outerRef={(element?: HTMLDivElement) => {
310
398
  if (element != null && elementReady) elementReady(element, true);
311
399
  }}
400
+ onScroll={onGridScroll}
312
401
  columns={columns}
402
+ onUpdateRows={onUpdateRows}
403
+ onInitLoad={onInitLoad}
313
404
  {...rest}
314
405
  />
315
406
  </Box>,
@@ -333,6 +424,8 @@ export function ResponsibleContainer<
333
424
  autoLoad={!hasFields}
334
425
  height={heightLocal}
335
426
  loadData={localLoadData}
427
+ onUpdateRows={onUpdateRows}
428
+ onInitLoad={onInitLoad}
336
429
  mRef={mRefs}
337
430
  onClick={(event, data) =>
338
431
  quickAction && ReactUtils.isSafeClick(event) && quickAction(data)
@@ -340,6 +433,7 @@ export function ResponsibleContainer<
340
433
  oRef={(element) => {
341
434
  if (element != null && elementReady) elementReady(element, false);
342
435
  }}
436
+ onScroll={onListScroll}
343
437
  {...rest}
344
438
  />
345
439
  </Box>,
@@ -349,9 +443,19 @@ export function ResponsibleContainer<
349
443
 
350
444
  const searchBar = React.useMemo(() => {
351
445
  if (!hasFields || showDataGrid == null) return;
446
+
447
+ const f =
448
+ typeof fields == "function"
449
+ ? fields(
450
+ JSON.parse(
451
+ sessionStorage.getItem(`${cacheKey}-searchbar`) ?? "{}"
452
+ ) as DataTypes.BasicTemplateType<F>
453
+ )
454
+ : fields;
455
+
352
456
  return (
353
457
  <SearchBar
354
- fields={fields}
458
+ fields={f}
355
459
  onSubmit={onSubmit}
356
460
  className={`searchBar${showDataGrid ? "Grid" : "List"}`}
357
461
  />
@@ -1,7 +1,10 @@
1
1
  import {
2
2
  GridDataGet,
3
3
  GridLoadDataProps,
4
+ GridLoaderStates,
5
+ GridOnScrollProps,
4
6
  ScrollerGridForwardRef,
7
+ VariableSizeGrid,
5
8
  useCombinedRefs,
6
9
  useDimensions
7
10
  } from "@etsoo/react";
@@ -13,6 +16,7 @@ import { MUGlobal } from "../MUGlobal";
13
16
  import { SearchBar } from "../SearchBar";
14
17
  import { CommonPage } from "./CommonPage";
15
18
  import { DataGridPageProps } from "./DataGridPageProps";
19
+ import { GridDataCacheType } from "../GridDataCacheType";
16
20
 
17
21
  interface LocalStates<T> {
18
22
  data?: FormData;
@@ -41,6 +45,8 @@ export function DataGridPage<
41
45
  mRef,
42
46
  sizeReadyMiliseconds = 100,
43
47
  pageProps = {},
48
+ cacheKey,
49
+ cacheMinutes = 120,
44
50
  ...rest
45
51
  } = props;
46
52
 
@@ -65,6 +71,8 @@ export function DataGridPage<
65
71
  }
66
72
  );
67
73
 
74
+ const initLoadedRef = React.useRef<boolean>();
75
+
68
76
  // On submit callback
69
77
  const onSubmit = (data: FormData, _reset: boolean) => {
70
78
  setStates({ data });
@@ -72,9 +80,64 @@ export function DataGridPage<
72
80
 
73
81
  const localLoadData = (props: GridLoadDataProps) => {
74
82
  const data = GridDataGet(props, fieldTemplate);
83
+
84
+ if (cacheKey)
85
+ sessionStorage.setItem(`${cacheKey}-searchbar`, JSON.stringify(data));
86
+
75
87
  return loadData(data);
76
88
  };
77
89
 
90
+ type DataType = GridDataCacheType<T>;
91
+
92
+ const onUpdateRows = (rows: T[], state: GridLoaderStates<T>) => {
93
+ if (state.currentPage > 0 && cacheKey) {
94
+ const data: DataType = { rows, state, creation: new Date().valueOf() };
95
+ sessionStorage.setItem(cacheKey, JSON.stringify(data));
96
+ }
97
+ };
98
+
99
+ const onInitLoad = (
100
+ ref: VariableSizeGrid<T>
101
+ ): [T[], Partial<GridLoaderStates<T>>?] | null | undefined => {
102
+ // Avoid repeatedly load from cache
103
+ if (initLoadedRef.current || !cacheKey) return undefined;
104
+
105
+ // Cache data
106
+ const cacheData = sessionStorage.getItem(cacheKey);
107
+ if (cacheData) {
108
+ const { rows, state, creation } = JSON.parse(cacheData) as DataType;
109
+
110
+ // 120 minutes
111
+ if (new Date().valueOf() - creation > cacheMinutes * 60000) {
112
+ sessionStorage.removeItem(cacheKey);
113
+ return undefined;
114
+ }
115
+
116
+ // Scroll position
117
+ const scrollData = sessionStorage.getItem(`${cacheKey}-scroll`);
118
+ if (scrollData) {
119
+ const { scrollLeft, scrollTop } = JSON.parse(
120
+ scrollData
121
+ ) as GridOnScrollProps;
122
+
123
+ globalThis.setTimeout(() => ref.scrollTo({ scrollLeft, scrollTop }), 0);
124
+ }
125
+
126
+ // Update flag value
127
+ initLoadedRef.current = true;
128
+
129
+ // Return cached rows and state
130
+ return [rows, state];
131
+ }
132
+
133
+ return undefined;
134
+ };
135
+
136
+ const onGridScroll = (props: GridOnScrollProps) => {
137
+ if (!cacheKey || !initLoadedRef.current) return;
138
+ sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
139
+ };
140
+
78
141
  // Watch container
79
142
  const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
80
143
  const rect = dimensions[0][2];
@@ -105,6 +168,9 @@ export function DataGridPage<
105
168
  height={gridHeight}
106
169
  loadData={localLoadData}
107
170
  mRef={refs}
171
+ onUpdateRows={onUpdateRows}
172
+ onInitLoad={onInitLoad}
173
+ onScroll={onGridScroll}
108
174
  outerRef={(element?: HTMLDivElement) => {
109
175
  if (element != null) setStates({ element });
110
176
  }}
@@ -119,6 +185,15 @@ export function DataGridPage<
119
185
  ref.reset({ data });
120
186
  }, [ref, data]);
121
187
 
188
+ const f =
189
+ typeof fields == "function"
190
+ ? fields(
191
+ JSON.parse(
192
+ sessionStorage.getItem(`${cacheKey}-searchbar`) ?? "{}"
193
+ ) as DataTypes.BasicTemplateType<F>
194
+ )
195
+ : fields;
196
+
122
197
  // Layout
123
198
  return (
124
199
  <CommonPage {...pageProps} scrollContainer={states.element}>
@@ -129,7 +204,7 @@ export function DataGridPage<
129
204
  paddingBottom: pageProps.paddings
130
205
  }}
131
206
  >
132
- <SearchBar fields={fields} onSubmit={onSubmit} />
207
+ <SearchBar fields={f} onSubmit={onSubmit} />
133
208
  </Box>
134
209
  {list}
135
210
  </Stack>
@@ -1,7 +1,10 @@
1
1
  import {
2
2
  GridDataGet,
3
3
  GridLoadDataProps,
4
+ GridLoaderStates,
5
+ ListOnScrollProps,
4
6
  ScrollerListForwardRef,
7
+ ScrollerListRef,
5
8
  useCombinedRefs,
6
9
  useDimensions
7
10
  } from "@etsoo/react";
@@ -13,6 +16,7 @@ import { ScrollerListEx } from "../ScrollerListEx";
13
16
  import { SearchBar } from "../SearchBar";
14
17
  import { CommonPage } from "./CommonPage";
15
18
  import { ListPageProps } from "./ListPageProps";
19
+ import { GridDataCacheType } from "../GridDataCacheType";
16
20
 
17
21
  /**
18
22
  * Fixed height list page
@@ -42,6 +46,8 @@ export function FixedListPage<
42
46
  mRef,
43
47
  sizeReadyMiliseconds = 0,
44
48
  pageProps = {},
49
+ cacheKey,
50
+ cacheMinutes = 120,
45
51
  ...rest
46
52
  } = props;
47
53
 
@@ -53,6 +59,8 @@ export function FixedListPage<
53
59
  ref?: ScrollerListForwardRef<T>;
54
60
  }>({});
55
61
 
62
+ const initLoadedRef = React.useRef<boolean>();
63
+
56
64
  // Scroll container
57
65
  const [scrollContainer, updateScrollContainer] = React.useState<
58
66
  HTMLElement | undefined
@@ -84,9 +92,61 @@ export function FixedListPage<
84
92
 
85
93
  const localLoadData = (props: GridLoadDataProps) => {
86
94
  const data = GridDataGet(props, fieldTemplate);
95
+
96
+ if (cacheKey)
97
+ sessionStorage.setItem(`${cacheKey}-searchbar`, JSON.stringify(data));
98
+
87
99
  return loadData(data);
88
100
  };
89
101
 
102
+ type DataType = GridDataCacheType<T>;
103
+
104
+ const onUpdateRows = (rows: T[], state: GridLoaderStates<T>) => {
105
+ if (state.currentPage > 0 && cacheKey) {
106
+ const data: DataType = { rows, state, creation: new Date().valueOf() };
107
+ sessionStorage.setItem(cacheKey, JSON.stringify(data));
108
+ }
109
+ };
110
+
111
+ const onInitLoad = (
112
+ ref: ScrollerListRef
113
+ ): [T[], Partial<GridLoaderStates<T>>?] | null | undefined => {
114
+ // Avoid repeatedly load from cache
115
+ if (initLoadedRef.current || !cacheKey) return undefined;
116
+
117
+ // Cache data
118
+ const cacheData = sessionStorage.getItem(cacheKey);
119
+ if (cacheData) {
120
+ const { rows, state, creation } = JSON.parse(cacheData) as DataType;
121
+
122
+ // 120 minutes
123
+ if (new Date().valueOf() - creation > cacheMinutes * 60000) {
124
+ sessionStorage.removeItem(cacheKey);
125
+ return undefined;
126
+ }
127
+
128
+ // Scroll position
129
+ const scrollData = sessionStorage.getItem(`${cacheKey}-scroll`);
130
+ if (scrollData) {
131
+ const { scrollOffset } = JSON.parse(scrollData) as ListOnScrollProps;
132
+ globalThis.setTimeout(() => ref.scrollTo(scrollOffset), 0);
133
+ }
134
+
135
+ // Update flag value
136
+ initLoadedRef.current = true;
137
+
138
+ // Return cached rows and state
139
+ return [rows, state];
140
+ }
141
+
142
+ return undefined;
143
+ };
144
+
145
+ const onListScroll = (props: ListOnScrollProps) => {
146
+ if (!cacheKey || !initLoadedRef.current) return;
147
+ sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
148
+ };
149
+
90
150
  // Watch container
91
151
  const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
92
152
  const rect = dimensions[0][2];
@@ -110,6 +170,9 @@ export function FixedListPage<
110
170
  height={height}
111
171
  loadData={localLoadData}
112
172
  mRef={refs}
173
+ onUpdateRows={onUpdateRows}
174
+ onInitLoad={onInitLoad}
175
+ onScroll={onListScroll}
113
176
  oRef={(element) => {
114
177
  if (element != null) updateScrollContainer(element);
115
178
  }}
@@ -120,6 +183,15 @@ export function FixedListPage<
120
183
  }
121
184
  }, [rect]);
122
185
 
186
+ const f =
187
+ typeof fields == "function"
188
+ ? fields(
189
+ JSON.parse(
190
+ sessionStorage.getItem(`${cacheKey}-searchbar`) ?? "{}"
191
+ ) as DataTypes.BasicTemplateType<F>
192
+ )
193
+ : fields;
194
+
123
195
  const { paddings, ...pageRest } = pageProps;
124
196
 
125
197
  // Layout
@@ -127,7 +199,7 @@ export function FixedListPage<
127
199
  <CommonPage {...pageRest} paddings={{}} scrollContainer={scrollContainer}>
128
200
  <Stack>
129
201
  <Box ref={dimensions[0][0]} sx={{ padding: paddings }}>
130
- <SearchBar fields={fields} onSubmit={onSubmit} />
202
+ <SearchBar fields={f} onSubmit={onSubmit} />
131
203
  </Box>
132
204
  {list}
133
205
  </Stack>