@etsoo/materialui 1.2.40 → 1.2.42

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.
@@ -8,7 +8,7 @@ import { ScrollerListExInnerItemRendererProps, ScrollerListExItemSize } from "./
8
8
  /**
9
9
  * ResponsibleContainer props
10
10
  */
11
- export type ResponsibleContainerProps<T extends object, F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate, D extends DataTypes.Keys<T> = IdDefaultType<T>> = Omit<DataGridExProps<T, D>, "height" | "itemKey" | "loadData" | "mRef" | "onScroll" | "onItemsRendered"> & {
11
+ export type ResponsibleContainerProps<T extends object, F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate, D extends DataTypes.Keys<T> = IdDefaultType<T>> = Omit<DataGridExProps<T, D>, "height" | "itemKey" | "loadData" | "mRef" | "onScroll" | "onItemsRendered" | "onInitLoad" | "onUpdateRows"> & {
12
12
  /**
13
13
  * Height will be deducted
14
14
  * @param height Current calcuated height
@@ -22,6 +22,14 @@ export type ResponsibleContainerProps<T extends object, F extends DataTypes.Basi
22
22
  * @returns Adjusted height
23
23
  */
24
24
  adjustFabHeight?: (height: number, isGrid: boolean) => number;
25
+ /**
26
+ * Cache key
27
+ */
28
+ cacheKey?: string;
29
+ /**
30
+ * Cache minutes
31
+ */
32
+ cacheMinutes?: number;
25
33
  /**
26
34
  * Columns
27
35
  */
@@ -37,7 +45,7 @@ export type ResponsibleContainerProps<T extends object, F extends DataTypes.Basi
37
45
  /**
38
46
  * Search fields
39
47
  */
40
- fields?: React.ReactElement[];
48
+ fields?: React.ReactElement[] | ((data: DataTypes.BasicTemplateType<F>) => React.ReactElement[]);
41
49
  /**
42
50
  * Search field template
43
51
  */
@@ -22,7 +22,7 @@ function defaultContainerBoxSx(paddings, hasField, _dataGrid) {
22
22
  */
23
23
  export function ResponsibleContainer(props) {
24
24
  // Destruct
25
- const { adjustHeight, adjustFabHeight, columns, containerBoxSx = defaultContainerBoxSx, dataGridMinWidth = Math.max(576, DataGridExCalColumns(columns).total), elementReady, fields, fieldTemplate, height, loadData, mRef, paddings = MUGlobal.pagePaddings, pullToRefresh = true, quickAction, sizeReadyMiliseconds = 0, searchBarHeight = 45.6, ...rest } = props;
25
+ const { adjustHeight, adjustFabHeight, cacheKey, cacheMinutes = 120, columns, containerBoxSx = defaultContainerBoxSx, dataGridMinWidth = Math.max(576, DataGridExCalColumns(columns).total), elementReady, fields, fieldTemplate, height, loadData, mRef, paddings = MUGlobal.pagePaddings, pullToRefresh = true, quickAction, sizeReadyMiliseconds = 0, searchBarHeight = 45.6, ...rest } = props;
26
26
  // Labels
27
27
  const labels = Labels.CommonPage;
28
28
  // Refs
@@ -45,6 +45,8 @@ export function ResponsibleContainer(props) {
45
45
  const localLoadData = (props) => {
46
46
  state.mounted = true;
47
47
  const data = GridDataGet(props, fieldTemplate);
48
+ if (cacheKey)
49
+ sessionStorage.setItem(`${cacheKey}-searchbar`, JSON.stringify(data));
48
50
  return loadData(data);
49
51
  };
50
52
  // On submit callback
@@ -70,6 +72,54 @@ export function ResponsibleContainer(props) {
70
72
  state.rect = rect;
71
73
  return false;
72
74
  });
75
+ const onUpdateRows = (rows, state) => {
76
+ if (state.currentPage > 0 && cacheKey) {
77
+ const data = { rows, state, creation: new Date().valueOf() };
78
+ sessionStorage.setItem(cacheKey, JSON.stringify(data));
79
+ }
80
+ };
81
+ const onInitLoad = (ref) => {
82
+ // Avoid repeatedly load from cache
83
+ if (refs.current.initLoaded || !cacheKey)
84
+ return undefined;
85
+ // Cache data
86
+ const cacheData = sessionStorage.getItem(cacheKey);
87
+ if (cacheData) {
88
+ const { rows, state, creation } = JSON.parse(cacheData);
89
+ // 120 minutes
90
+ if (new Date().valueOf() - creation > cacheMinutes * 60000) {
91
+ sessionStorage.removeItem(cacheKey);
92
+ return undefined;
93
+ }
94
+ // Scroll position
95
+ const scrollData = sessionStorage.getItem(`${cacheKey}-scroll`);
96
+ if (scrollData) {
97
+ if ("resetAfterColumnIndex" in ref) {
98
+ const { scrollLeft, scrollTop } = JSON.parse(scrollData);
99
+ globalThis.setTimeout(() => ref.scrollTo({ scrollLeft, scrollTop }), 0);
100
+ }
101
+ else {
102
+ const { scrollOffset } = JSON.parse(scrollData);
103
+ globalThis.setTimeout(() => ref.scrollTo(scrollOffset), 0);
104
+ }
105
+ }
106
+ // Update flag value
107
+ refs.current.initLoaded = true;
108
+ // Return cached rows and state
109
+ return [rows, state];
110
+ }
111
+ return undefined;
112
+ };
113
+ const onListScroll = (props) => {
114
+ if (!cacheKey || !refs.current.initLoaded)
115
+ return;
116
+ sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
117
+ };
118
+ const onGridScroll = (props) => {
119
+ if (!cacheKey || !refs.current.initLoaded)
120
+ return;
121
+ sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
122
+ };
73
123
  // Rect
74
124
  const rect = dimensions[0][2];
75
125
  // Create list
@@ -107,7 +157,7 @@ export function ResponsibleContainer(props) {
107
157
  React.createElement(DataGridEx, { autoLoad: !hasFields, height: heightLocal, width: rect.width, loadData: localLoadData, mRef: mRefs, onDoubleClick: (_, data) => quickAction && quickAction(data), outerRef: (element) => {
108
158
  if (element != null && elementReady)
109
159
  elementReady(element, true);
110
- }, columns: columns, ...rest })),
160
+ }, onScroll: onGridScroll, columns: columns, onUpdateRows: onUpdateRows, onInitLoad: onInitLoad, ...rest })),
111
161
  true
112
162
  ];
113
163
  }
@@ -122,17 +172,21 @@ export function ResponsibleContainer(props) {
122
172
  delete rest.selectable;
123
173
  return [
124
174
  React.createElement(Box, { className: "ListBox", sx: { height: heightLocal } },
125
- React.createElement(ScrollerListEx, { autoLoad: !hasFields, height: heightLocal, loadData: localLoadData, mRef: mRefs, onClick: (event, data) => quickAction && ReactUtils.isSafeClick(event) && quickAction(data), oRef: (element) => {
175
+ React.createElement(ScrollerListEx, { autoLoad: !hasFields, height: heightLocal, loadData: localLoadData, onUpdateRows: onUpdateRows, onInitLoad: onInitLoad, mRef: mRefs, onClick: (event, data) => quickAction && ReactUtils.isSafeClick(event) && quickAction(data), oRef: (element) => {
126
176
  if (element != null && elementReady)
127
177
  elementReady(element, false);
128
- }, ...rest })),
178
+ }, onScroll: onListScroll, ...rest })),
129
179
  false
130
180
  ];
131
181
  })();
132
182
  const searchBar = React.useMemo(() => {
183
+ var _a;
133
184
  if (!hasFields || showDataGrid == null)
134
185
  return;
135
- return (React.createElement(SearchBar, { fields: fields, onSubmit: onSubmit, className: `searchBar${showDataGrid ? "Grid" : "List"}` }));
186
+ const f = typeof fields == "function"
187
+ ? fields(JSON.parse((_a = sessionStorage.getItem(`${cacheKey}-searchbar`)) !== null && _a !== void 0 ? _a : "{}"))
188
+ : fields;
189
+ return (React.createElement(SearchBar, { fields: f, onSubmit: onSubmit, className: `searchBar${showDataGrid ? "Grid" : "List"}` }));
136
190
  }, [showDataGrid, hasFields, searchBarHeight]);
137
191
  // Pull container
138
192
  const pullContainer = showDataGrid == null
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.2.40",
3
+ "version": "1.2.42",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -52,7 +52,7 @@
52
52
  "@emotion/styled": "^11.11.0",
53
53
  "@etsoo/appscript": "^1.4.8",
54
54
  "@etsoo/notificationbase": "^1.1.25",
55
- "@etsoo/react": "^1.6.81",
55
+ "@etsoo/react": "^1.6.84",
56
56
  "@etsoo/shared": "^1.2.5",
57
57
  "@mui/icons-material": "^5.11.16",
58
58
  "@mui/material": "^5.13.0",
@@ -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";
@@ -36,7 +43,14 @@ export type ResponsibleContainerProps<
36
43
  D extends DataTypes.Keys<T> = IdDefaultType<T>
37
44
  > = Omit<
38
45
  DataGridExProps<T, D>,
39
- "height" | "itemKey" | "loadData" | "mRef" | "onScroll" | "onItemsRendered"
46
+ | "height"
47
+ | "itemKey"
48
+ | "loadData"
49
+ | "mRef"
50
+ | "onScroll"
51
+ | "onItemsRendered"
52
+ | "onInitLoad"
53
+ | "onUpdateRows"
40
54
  > & {
41
55
  /**
42
56
  * Height will be deducted
@@ -53,6 +67,16 @@ export type ResponsibleContainerProps<
53
67
  */
54
68
  adjustFabHeight?: (height: number, isGrid: boolean) => number;
55
69
 
70
+ /**
71
+ * Cache key
72
+ */
73
+ cacheKey?: string;
74
+
75
+ /**
76
+ * Cache minutes
77
+ */
78
+ cacheMinutes?: number;
79
+
56
80
  /**
57
81
  * Columns
58
82
  */
@@ -75,7 +99,9 @@ export type ResponsibleContainerProps<
75
99
  /**
76
100
  * Search fields
77
101
  */
78
- fields?: React.ReactElement[];
102
+ fields?:
103
+ | React.ReactElement[]
104
+ | ((data: DataTypes.BasicTemplateType<F>) => React.ReactElement[]);
79
105
 
80
106
  /**
81
107
  * Search field template
@@ -151,6 +177,7 @@ interface LocalRefs<T> {
151
177
  rect?: DOMRect;
152
178
  ref?: GridMethodRef<T>;
153
179
  mounted?: boolean;
180
+ initLoaded?: boolean;
154
181
  }
155
182
 
156
183
  function defaultContainerBoxSx(
@@ -180,6 +207,8 @@ export function ResponsibleContainer<
180
207
  const {
181
208
  adjustHeight,
182
209
  adjustFabHeight,
210
+ cacheKey,
211
+ cacheMinutes = 120,
183
212
  columns,
184
213
  containerBoxSx = defaultContainerBoxSx,
185
214
  dataGridMinWidth = Math.max(576, DataGridExCalColumns(columns).total),
@@ -223,6 +252,10 @@ export function ResponsibleContainer<
223
252
  const localLoadData = (props: GridLoadDataProps) => {
224
253
  state.mounted = true;
225
254
  const data = GridDataGet(props, fieldTemplate);
255
+
256
+ if (cacheKey)
257
+ sessionStorage.setItem(`${cacheKey}-searchbar`, JSON.stringify(data));
258
+
226
259
  return loadData(data);
227
260
  };
228
261
 
@@ -260,6 +293,70 @@ export function ResponsibleContainer<
260
293
  }
261
294
  );
262
295
 
296
+ type DataType = { rows: T[]; state: GridLoaderStates<T>; creation: number };
297
+
298
+ const onUpdateRows = (rows: T[], state: GridLoaderStates<T>) => {
299
+ if (state.currentPage > 0 && cacheKey) {
300
+ const data: DataType = { rows, state, creation: new Date().valueOf() };
301
+ sessionStorage.setItem(cacheKey, JSON.stringify(data));
302
+ }
303
+ };
304
+
305
+ const onInitLoad = (
306
+ ref: VariableSizeGrid<T> | ScrollerListRef
307
+ ): [T[], Partial<GridLoaderStates<T>>?] | null | undefined => {
308
+ // Avoid repeatedly load from cache
309
+ if (refs.current.initLoaded || !cacheKey) return undefined;
310
+
311
+ // Cache data
312
+ const cacheData = sessionStorage.getItem(cacheKey);
313
+ if (cacheData) {
314
+ const { rows, state, creation } = JSON.parse(cacheData) as DataType;
315
+
316
+ // 120 minutes
317
+ if (new Date().valueOf() - creation > cacheMinutes * 60000) {
318
+ sessionStorage.removeItem(cacheKey);
319
+ return undefined;
320
+ }
321
+
322
+ // Scroll position
323
+ const scrollData = sessionStorage.getItem(`${cacheKey}-scroll`);
324
+ if (scrollData) {
325
+ if ("resetAfterColumnIndex" in ref) {
326
+ const { scrollLeft, scrollTop } = JSON.parse(
327
+ scrollData
328
+ ) as GridOnScrollProps;
329
+
330
+ globalThis.setTimeout(
331
+ () => ref.scrollTo({ scrollLeft, scrollTop }),
332
+ 0
333
+ );
334
+ } else {
335
+ const { scrollOffset } = JSON.parse(scrollData) as ListOnScrollProps;
336
+
337
+ globalThis.setTimeout(() => ref.scrollTo(scrollOffset), 0);
338
+ }
339
+ }
340
+
341
+ // Update flag value
342
+ refs.current.initLoaded = true;
343
+
344
+ // Return cached rows and state
345
+ return [rows, state];
346
+ }
347
+
348
+ return undefined;
349
+ };
350
+
351
+ const onListScroll = (props: ListOnScrollProps) => {
352
+ if (!cacheKey || !refs.current.initLoaded) return;
353
+ sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
354
+ };
355
+ const onGridScroll = (props: GridOnScrollProps) => {
356
+ if (!cacheKey || !refs.current.initLoaded) return;
357
+ sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
358
+ };
359
+
263
360
  // Rect
264
361
  const rect = dimensions[0][2];
265
362
 
@@ -309,7 +406,10 @@ export function ResponsibleContainer<
309
406
  outerRef={(element?: HTMLDivElement) => {
310
407
  if (element != null && elementReady) elementReady(element, true);
311
408
  }}
409
+ onScroll={onGridScroll}
312
410
  columns={columns}
411
+ onUpdateRows={onUpdateRows}
412
+ onInitLoad={onInitLoad}
313
413
  {...rest}
314
414
  />
315
415
  </Box>,
@@ -333,6 +433,8 @@ export function ResponsibleContainer<
333
433
  autoLoad={!hasFields}
334
434
  height={heightLocal}
335
435
  loadData={localLoadData}
436
+ onUpdateRows={onUpdateRows}
437
+ onInitLoad={onInitLoad}
336
438
  mRef={mRefs}
337
439
  onClick={(event, data) =>
338
440
  quickAction && ReactUtils.isSafeClick(event) && quickAction(data)
@@ -340,6 +442,7 @@ export function ResponsibleContainer<
340
442
  oRef={(element) => {
341
443
  if (element != null && elementReady) elementReady(element, false);
342
444
  }}
445
+ onScroll={onListScroll}
343
446
  {...rest}
344
447
  />
345
448
  </Box>,
@@ -349,9 +452,19 @@ export function ResponsibleContainer<
349
452
 
350
453
  const searchBar = React.useMemo(() => {
351
454
  if (!hasFields || showDataGrid == null) return;
455
+
456
+ const f =
457
+ typeof fields == "function"
458
+ ? fields(
459
+ JSON.parse(
460
+ sessionStorage.getItem(`${cacheKey}-searchbar`) ?? "{}"
461
+ ) as DataTypes.BasicTemplateType<F>
462
+ )
463
+ : fields;
464
+
352
465
  return (
353
466
  <SearchBar
354
- fields={fields}
467
+ fields={f}
355
468
  onSubmit={onSubmit}
356
469
  className={`searchBar${showDataGrid ? "Grid" : "List"}`}
357
470
  />