@etsoo/materialui 1.2.42 → 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,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>
@@ -1,17 +1,21 @@
1
1
  import {
2
- GridDataGet,
3
- GridLoadDataProps,
4
- ScrollerListForwardRef,
5
- useCombinedRefs
6
- } from '@etsoo/react';
7
- import { DataTypes, IdDefaultType } from '@etsoo/shared';
8
- import { Box, Stack } from '@mui/material';
9
- import React from 'react';
10
- import { MUGlobal } from '../MUGlobal';
11
- import { ScrollerListEx } from '../ScrollerListEx';
12
- import { SearchBar } from '../SearchBar';
13
- import { CommonPage, CommonPageScrollContainer } from './CommonPage';
14
- import { ListPageProps } from './ListPageProps';
2
+ GridDataGet,
3
+ GridLoadDataProps,
4
+ GridLoaderStates,
5
+ ListOnScrollProps,
6
+ ScrollerListForwardRef,
7
+ ScrollerListRef,
8
+ useCombinedRefs
9
+ } from "@etsoo/react";
10
+ import { DataTypes, IdDefaultType } from "@etsoo/shared";
11
+ import { Box, Stack } from "@mui/material";
12
+ import React from "react";
13
+ import { MUGlobal } from "../MUGlobal";
14
+ import { ScrollerListEx } from "../ScrollerListEx";
15
+ import { SearchBar } from "../SearchBar";
16
+ import { CommonPage, CommonPageScrollContainer } from "./CommonPage";
17
+ import { ListPageProps } from "./ListPageProps";
18
+ import { GridDataCacheType } from "../GridDataCacheType";
15
19
 
16
20
  /**
17
21
  * List page
@@ -19,72 +23,136 @@ import { ListPageProps } from './ListPageProps';
19
23
  * @returns Component
20
24
  */
21
25
  export function ListPage<
22
- T extends object,
23
- F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate,
24
- D extends DataTypes.Keys<T> = IdDefaultType<T>
26
+ T extends object,
27
+ F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate,
28
+ D extends DataTypes.Keys<T> = IdDefaultType<T>
25
29
  >(props: ListPageProps<T, F, D>) {
26
- // Destruct
27
- const {
28
- fields,
29
- fieldTemplate,
30
- loadData,
31
- mRef,
32
- pageProps = {},
33
- ...rest
34
- } = props;
35
-
36
- pageProps.paddings ??= MUGlobal.pagePaddings;
37
-
38
- // States
39
- const [states] = React.useState<{
40
- data?: FormData;
41
- ref?: ScrollerListForwardRef<T>;
42
- }>({});
43
-
44
- const refs = useCombinedRefs(mRef, (ref: ScrollerListForwardRef<T>) => {
45
- if (ref == null) return;
46
-
47
- const first = states.ref == null;
48
-
49
- states.ref = ref;
50
-
51
- if (first) reset();
52
- });
53
-
54
- const reset = () => {
55
- if (states.data == null || states.ref == null) return;
56
- states.ref.reset({ data: states.data });
57
- };
58
-
59
- // On submit callback
60
- const onSubmit = (data: FormData, _reset: boolean) => {
61
- states.data = data;
62
- reset();
63
- };
64
-
65
- const localLoadData = (props: GridLoadDataProps) => {
66
- const data = GridDataGet(props, fieldTemplate);
67
- return loadData(data);
68
- };
69
-
70
- // Layout
71
- return (
72
- <CommonPage {...pageProps} scrollContainer={CommonPageScrollContainer}>
73
- <Stack>
74
- <Box
75
- sx={{
76
- paddingBottom: pageProps.paddings
77
- }}
78
- >
79
- <SearchBar fields={fields} onSubmit={onSubmit} />
80
- </Box>
81
- <ScrollerListEx<T, D>
82
- autoLoad={false}
83
- loadData={localLoadData}
84
- mRef={refs}
85
- {...rest}
86
- />
87
- </Stack>
88
- </CommonPage>
89
- );
30
+ // Destruct
31
+ const {
32
+ fields,
33
+ fieldTemplate,
34
+ loadData,
35
+ mRef,
36
+ pageProps = {},
37
+ cacheKey,
38
+ cacheMinutes = 120,
39
+ ...rest
40
+ } = props;
41
+
42
+ pageProps.paddings ??= MUGlobal.pagePaddings;
43
+
44
+ // States
45
+ const [states] = React.useState<{
46
+ data?: FormData;
47
+ ref?: ScrollerListForwardRef<T>;
48
+ }>({});
49
+
50
+ const refs = useCombinedRefs(mRef, (ref: ScrollerListForwardRef<T>) => {
51
+ if (ref == null) return;
52
+
53
+ const first = states.ref == null;
54
+
55
+ states.ref = ref;
56
+
57
+ if (first) reset();
58
+ });
59
+
60
+ const initLoadedRef = React.useRef<boolean>();
61
+
62
+ const reset = () => {
63
+ if (states.data == null || states.ref == null) return;
64
+ states.ref.reset({ data: states.data });
65
+ };
66
+
67
+ // On submit callback
68
+ const onSubmit = (data: FormData, _reset: boolean) => {
69
+ states.data = data;
70
+ reset();
71
+ };
72
+
73
+ const localLoadData = (props: GridLoadDataProps) => {
74
+ const data = GridDataGet(props, fieldTemplate);
75
+ return loadData(data);
76
+ };
77
+
78
+ type DataType = GridDataCacheType<T>;
79
+
80
+ const onUpdateRows = (rows: T[], state: GridLoaderStates<T>) => {
81
+ if (state.currentPage > 0 && cacheKey) {
82
+ const data: DataType = { rows, state, creation: new Date().valueOf() };
83
+ sessionStorage.setItem(cacheKey, JSON.stringify(data));
84
+ }
85
+ };
86
+
87
+ const onInitLoad = (
88
+ ref: ScrollerListRef
89
+ ): [T[], Partial<GridLoaderStates<T>>?] | null | undefined => {
90
+ // Avoid repeatedly load from cache
91
+ if (initLoadedRef.current || !cacheKey) return undefined;
92
+
93
+ // Cache data
94
+ const cacheData = sessionStorage.getItem(cacheKey);
95
+ if (cacheData) {
96
+ const { rows, state, creation } = JSON.parse(cacheData) as DataType;
97
+
98
+ // 120 minutes
99
+ if (new Date().valueOf() - creation > cacheMinutes * 60000) {
100
+ sessionStorage.removeItem(cacheKey);
101
+ return undefined;
102
+ }
103
+
104
+ // Scroll position
105
+ const scrollData = sessionStorage.getItem(`${cacheKey}-scroll`);
106
+ if (scrollData) {
107
+ const { scrollOffset } = JSON.parse(scrollData) as ListOnScrollProps;
108
+ globalThis.setTimeout(() => ref.scrollTo(scrollOffset), 0);
109
+ }
110
+
111
+ // Update flag value
112
+ initLoadedRef.current = true;
113
+
114
+ // Return cached rows and state
115
+ return [rows, state];
116
+ }
117
+
118
+ return undefined;
119
+ };
120
+
121
+ const onListScroll = (props: ListOnScrollProps) => {
122
+ if (!cacheKey || !initLoadedRef.current) return;
123
+ sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
124
+ };
125
+
126
+ const f =
127
+ typeof fields == "function"
128
+ ? fields(
129
+ JSON.parse(
130
+ sessionStorage.getItem(`${cacheKey}-searchbar`) ?? "{}"
131
+ ) as DataTypes.BasicTemplateType<F>
132
+ )
133
+ : fields;
134
+
135
+ // Layout
136
+ return (
137
+ <CommonPage {...pageProps} scrollContainer={CommonPageScrollContainer}>
138
+ <Stack>
139
+ <Box
140
+ sx={{
141
+ paddingBottom: pageProps.paddings
142
+ }}
143
+ >
144
+ <SearchBar fields={f} onSubmit={onSubmit} />
145
+ </Box>
146
+ <ScrollerListEx<T, D>
147
+ autoLoad={false}
148
+ loadData={localLoadData}
149
+ onUpdateRows={onUpdateRows}
150
+ onInitLoad={onInitLoad}
151
+ onScroll={onListScroll}
152
+ mRef={refs}
153
+ {...rest}
154
+ />
155
+ </Stack>
156
+ </CommonPage>
157
+ );
90
158
  }
@@ -1,12 +1,12 @@
1
- import { DataTypes } from '@etsoo/shared';
2
- import { ScrollerListExProps } from '../ScrollerListEx';
3
- import { SearchPageProps } from './SearchPageProps';
1
+ import { DataTypes } from "@etsoo/shared";
2
+ import { ScrollerListExProps } from "../ScrollerListEx";
3
+ import { SearchPageProps } from "./SearchPageProps";
4
4
 
5
5
  /**
6
6
  * List page props
7
7
  */
8
8
  export type ListPageProps<
9
- T extends object,
10
- F extends DataTypes.BasicTemplate,
11
- D extends DataTypes.Keys<T>
12
- > = SearchPageProps<T, F> & Omit<ScrollerListExProps<T, D>, 'loadData'>;
9
+ T extends object,
10
+ F extends DataTypes.BasicTemplate,
11
+ D extends DataTypes.Keys<T>
12
+ > = SearchPageProps<T, F> & Omit<ScrollerListExProps<T, D>, "loadData">;
@@ -9,10 +9,22 @@ export type SearchPageProps<
9
9
  T extends object,
10
10
  F extends DataTypes.BasicTemplate
11
11
  > = Omit<GridLoader<T>, "loadData"> & {
12
+ /**
13
+ * Cache key
14
+ */
15
+ cacheKey?: string;
16
+
17
+ /**
18
+ * Cache minutes
19
+ */
20
+ cacheMinutes?: number;
21
+
12
22
  /**
13
23
  * Search fields
14
24
  */
15
- fields: React.ReactElement[];
25
+ fields:
26
+ | React.ReactElement[]
27
+ | ((data: DataTypes.BasicTemplateType<F>) => React.ReactElement[]);
16
28
 
17
29
  /**
18
30
  * Search field template
@@ -1,17 +1,17 @@
1
1
  import {
2
- GridDataGet,
3
- GridLoadDataProps,
4
- useCombinedRefs,
5
- useDimensions
6
- } from '@etsoo/react';
7
- import { DataTypes, IdDefaultType } from '@etsoo/shared';
8
- import { Box, Stack } from '@mui/material';
9
- import React from 'react';
10
- import { MUGlobal } from '../MUGlobal';
11
- import { SearchBar } from '../SearchBar';
12
- import { TableEx, TableExMethodRef, TableExMinWidth } from '../TableEx';
13
- import { CommonPage, CommonPageScrollContainer } from './CommonPage';
14
- import { TablePageProps } from './TablePageProps';
2
+ GridDataGet,
3
+ GridLoadDataProps,
4
+ useCombinedRefs,
5
+ useDimensions
6
+ } from "@etsoo/react";
7
+ import { DataTypes, IdDefaultType } from "@etsoo/shared";
8
+ import { Box, Stack } from "@mui/material";
9
+ import React from "react";
10
+ import { MUGlobal } from "../MUGlobal";
11
+ import { SearchBar } from "../SearchBar";
12
+ import { TableEx, TableExMethodRef, TableExMinWidth } from "../TableEx";
13
+ import { CommonPage, CommonPageScrollContainer } from "./CommonPage";
14
+ import { TablePageProps } from "./TablePageProps";
15
15
 
16
16
  /**
17
17
  * Table page
@@ -19,108 +19,122 @@ import { TablePageProps } from './TablePageProps';
19
19
  * @returns Component
20
20
  */
21
21
  export function TablePage<
22
- T extends object,
23
- F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate,
24
- D extends DataTypes.Keys<T> = IdDefaultType<T>
22
+ T extends object,
23
+ F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate,
24
+ D extends DataTypes.Keys<T> = IdDefaultType<T>
25
25
  >(props: TablePageProps<T, F, D>) {
26
- // Destruct
27
- const {
28
- columns,
29
- fields,
30
- fieldTemplate,
31
- loadData,
32
- mRef,
33
- sizeReadyMiliseconds = 0,
34
- pageProps = {},
35
- ...rest
36
- } = props;
37
-
38
- pageProps.paddings ??= MUGlobal.pagePaddings;
39
-
40
- // States
41
- const [states] = React.useState<{
42
- data?: FormData;
43
- ref?: TableExMethodRef<T>;
44
- }>({});
45
-
46
- const refs = useCombinedRefs(
47
- mRef,
48
- (ref: TableExMethodRef<T> | null | undefined) => {
49
- if (ref == null) return;
50
-
51
- const first = states.ref == null;
52
-
53
- states.ref = ref;
54
-
55
- if (first) reset();
56
- }
57
- );
58
-
59
- const reset = () => {
60
- if (states.data == null || states.ref == null) return;
61
- states.ref.reset({ data: states.data });
62
- };
63
-
64
- // On submit callback
65
- const onSubmit = (data: FormData, _reset: boolean) => {
66
- states.data = data;
67
- reset();
68
- };
69
-
70
- const localLoadData = (props: GridLoadDataProps) => {
71
- const data = GridDataGet(props, fieldTemplate);
72
- return loadData(data);
73
- };
74
-
75
- // Total width
76
- const totalWidth = React.useMemo(
77
- () =>
78
- columns.reduce((previousValue, { width, minWidth }) => {
79
- return previousValue + (width ?? minWidth ?? TableExMinWidth);
80
- }, 0),
81
- [columns]
82
- );
83
-
84
- // Watch container
85
- const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
86
- const rect = dimensions[0][2];
87
- const list = React.useMemo(() => {
88
- if (rect != null && rect.height > 50 && rect.width >= totalWidth) {
89
- let maxHeight =
90
- document.documentElement.clientHeight -
91
- (rect.top + rect.height + 1);
92
-
93
- const style = window.getComputedStyle(dimensions[0][1]!);
94
- const paddingBottom = parseFloat(style.paddingBottom);
95
- if (!isNaN(paddingBottom)) maxHeight -= paddingBottom;
96
-
97
- return (
98
- <TableEx<T, D>
99
- autoLoad={false}
100
- columns={columns}
101
- loadData={localLoadData}
102
- maxHeight={maxHeight}
103
- mRef={refs}
104
- {...rest}
105
- />
106
- );
107
- }
108
- }, [rect]);
109
-
110
- // Layout
111
- return (
112
- <CommonPage {...pageProps} scrollContainer={CommonPageScrollContainer}>
113
- <Stack>
114
- <Box
115
- ref={dimensions[0][0]}
116
- sx={{
117
- paddingBottom: pageProps.paddings
118
- }}
119
- >
120
- <SearchBar fields={fields} onSubmit={onSubmit} />
121
- </Box>
122
- {list}
123
- </Stack>
124
- </CommonPage>
125
- );
26
+ // Destruct
27
+ const {
28
+ columns,
29
+ fields,
30
+ fieldTemplate,
31
+ loadData,
32
+ mRef,
33
+ sizeReadyMiliseconds = 0,
34
+ pageProps = {},
35
+ cacheKey,
36
+ cacheMinutes = 120,
37
+ ...rest
38
+ } = props;
39
+
40
+ pageProps.paddings ??= MUGlobal.pagePaddings;
41
+
42
+ // States
43
+ const [states] = React.useState<{
44
+ data?: FormData;
45
+ ref?: TableExMethodRef<T>;
46
+ }>({});
47
+
48
+ const refs = useCombinedRefs(
49
+ mRef,
50
+ (ref: TableExMethodRef<T> | null | undefined) => {
51
+ if (ref == null) return;
52
+
53
+ const first = states.ref == null;
54
+
55
+ states.ref = ref;
56
+
57
+ if (first) reset();
58
+ }
59
+ );
60
+
61
+ const reset = () => {
62
+ if (states.data == null || states.ref == null) return;
63
+ states.ref.reset({ data: states.data });
64
+ };
65
+
66
+ // On submit callback
67
+ const onSubmit = (data: FormData, _reset: boolean) => {
68
+ states.data = data;
69
+ reset();
70
+ };
71
+
72
+ const localLoadData = (props: GridLoadDataProps) => {
73
+ const data = GridDataGet(props, fieldTemplate);
74
+
75
+ if (cacheKey)
76
+ sessionStorage.setItem(`${cacheKey}-searchbar`, JSON.stringify(data));
77
+
78
+ return loadData(data);
79
+ };
80
+
81
+ // Total width
82
+ const totalWidth = React.useMemo(
83
+ () =>
84
+ columns.reduce((previousValue, { width, minWidth }) => {
85
+ return previousValue + (width ?? minWidth ?? TableExMinWidth);
86
+ }, 0),
87
+ [columns]
88
+ );
89
+
90
+ // Watch container
91
+ const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
92
+ const rect = dimensions[0][2];
93
+ const list = React.useMemo(() => {
94
+ if (rect != null && rect.height > 50 && rect.width >= totalWidth) {
95
+ let maxHeight =
96
+ document.documentElement.clientHeight - (rect.top + rect.height + 1);
97
+
98
+ const style = window.getComputedStyle(dimensions[0][1]!);
99
+ const paddingBottom = parseFloat(style.paddingBottom);
100
+ if (!isNaN(paddingBottom)) maxHeight -= paddingBottom;
101
+
102
+ return (
103
+ <TableEx<T, D>
104
+ autoLoad={false}
105
+ columns={columns}
106
+ loadData={localLoadData}
107
+ maxHeight={maxHeight}
108
+ mRef={refs}
109
+ {...rest}
110
+ />
111
+ );
112
+ }
113
+ }, [rect]);
114
+
115
+ const f =
116
+ typeof fields == "function"
117
+ ? fields(
118
+ JSON.parse(
119
+ sessionStorage.getItem(`${cacheKey}-searchbar`) ?? "{}"
120
+ ) as DataTypes.BasicTemplateType<F>
121
+ )
122
+ : fields;
123
+
124
+ // Layout
125
+ return (
126
+ <CommonPage {...pageProps} scrollContainer={CommonPageScrollContainer}>
127
+ <Stack>
128
+ <Box
129
+ ref={dimensions[0][0]}
130
+ sx={{
131
+ paddingBottom: pageProps.paddings
132
+ }}
133
+ >
134
+ <SearchBar fields={f} onSubmit={onSubmit} />
135
+ </Box>
136
+ {list}
137
+ </Stack>
138
+ </CommonPage>
139
+ );
126
140
  }