@etsoo/react 1.4.11 → 1.4.15

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.
@@ -96,7 +96,7 @@ export class ReactApp extends CoreApp {
96
96
  if (startPos > 0) {
97
97
  const main = message.substring(0, startPos).trim();
98
98
  const tip = message.substring(startPos);
99
- const titleNode = React.createElement(React.Fragment, null, main, React.createElement('div', { style: { fontSize: '9px' } }, tip));
99
+ const titleNode = React.createElement(React.Fragment, null, main, React.createElement('br'), React.createElement('span', { style: { fontSize: '9px' } }, tip));
100
100
  this.notifier.alert(titleNode, callback);
101
101
  return;
102
102
  }
package/lib/index.d.ts CHANGED
@@ -48,6 +48,7 @@ export * from './mu/ItemList';
48
48
  export * from './mu/ListItemRightIcon';
49
49
  export * from './mu/LoadingButton';
50
50
  export * from './mu/MaskInput';
51
+ export * from './mu/MobileListItemRenderer';
51
52
  export * from './mu/MoreFab';
52
53
  export * from './mu/MUGlobal';
53
54
  export * from './mu/NotifierMU';
@@ -55,6 +56,7 @@ export * from './mu/OptionGroup';
55
56
  export * from './mu/PList';
56
57
  export * from './mu/ProgressCount';
57
58
  export * from './mu/PullToRefreshUI';
59
+ export * from './mu/ResponsibleContainer';
58
60
  export * from './mu/RLink';
59
61
  export * from './mu/ScrollerListEx';
60
62
  export * from './mu/ScrollTopFab';
package/lib/index.js CHANGED
@@ -51,6 +51,7 @@ export * from './mu/ItemList';
51
51
  export * from './mu/ListItemRightIcon';
52
52
  export * from './mu/LoadingButton';
53
53
  export * from './mu/MaskInput';
54
+ export * from './mu/MobileListItemRenderer';
54
55
  export * from './mu/MoreFab';
55
56
  export * from './mu/MUGlobal';
56
57
  export * from './mu/NotifierMU';
@@ -58,6 +59,7 @@ export * from './mu/OptionGroup';
58
59
  export * from './mu/PList';
59
60
  export * from './mu/ProgressCount';
60
61
  export * from './mu/PullToRefreshUI';
62
+ export * from './mu/ResponsibleContainer';
61
63
  export * from './mu/RLink';
62
64
  export * from './mu/ScrollerListEx';
63
65
  export * from './mu/ScrollTopFab';
@@ -43,7 +43,7 @@ export interface DnDListProps<D extends {}, E extends React.ElementType> {
43
43
  /**
44
44
  * Top and bottom sides renderer
45
45
  */
46
- sideRenderer?: (top: boolean, addItem: (item: D) => boolean, addItems: (items: D[]) => number) => React.ReactNode;
46
+ sideRenderer?: (top: boolean, addItem: (item: D) => boolean, addItems: (items: D[]) => number, reloadItems: () => void) => React.ReactNode;
47
47
  /**
48
48
  * Name for hidden form input
49
49
  */
package/lib/mu/DnDList.js CHANGED
@@ -90,13 +90,18 @@ export function DnDList(props) {
90
90
  // Update the state
91
91
  changeItems(newItems);
92
92
  };
93
- React.useEffect(() => {
93
+ // Reload items
94
+ const reloadItems = () => {
94
95
  // Load data
95
96
  loadData(name).then((items) => setItems(items));
97
+ };
98
+ React.useEffect(() => {
99
+ // Load data
100
+ reloadItems();
96
101
  }, [name]);
97
102
  // Layout
98
103
  return (React.createElement(React.Fragment, null,
99
- sideRenderer && sideRenderer(true, addItem, addItems),
104
+ sideRenderer && sideRenderer(true, addItem, addItems, reloadItems),
100
105
  React.createElement(Component, { ...componentProps },
101
106
  React.createElement(DragDropContext, { onDragEnd: onDragEndLocal },
102
107
  React.createElement(Droppable, { droppableId: name }, (provided, snapshot) => (React.createElement("div", { ...provided.droppableProps, ref: provided.innerRef, style: getListStyle(snapshot.isDraggingOver) },
@@ -113,5 +118,6 @@ export function DnDList(props) {
113
118
  } }, children(item, index, deleteItem, editItem)))));
114
119
  }),
115
120
  provided.placeholder))))),
116
- sideRenderer && sideRenderer(false, addItem, addItems)));
121
+ sideRenderer &&
122
+ sideRenderer(false, addItem, addItems, reloadItems)));
117
123
  }
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { ListItemReact } from '../components/ListItemReact';
3
+ import { ScrollerListExInnerItemRendererProps } from './ScrollerListEx';
4
+ /**
5
+ * Default mobile list item renderer
6
+ * @param param0 List renderer props
7
+ * @param margin Margin
8
+ * @param renderer Renderer for card content
9
+ * @returns Component
10
+ */
11
+ export declare function MobileListItemRenderer<T>({ data, itemHeight }: ScrollerListExInnerItemRendererProps<T>, margin: {}, renderer: (data: T) => [
12
+ string,
13
+ string | undefined,
14
+ React.ReactNode | (ListItemReact | boolean)[],
15
+ React.ReactNode
16
+ ]): JSX.Element;
@@ -0,0 +1,70 @@
1
+ import { Card, CardContent, CardHeader, LinearProgress } from '@mui/material';
2
+ import React from 'react';
3
+ import { MoreFab } from './MoreFab';
4
+ import { MUGlobal } from './MUGlobal';
5
+ function getActions(input) {
6
+ // Actions
7
+ const actions = [];
8
+ input.forEach((action) => {
9
+ if (typeof action === 'boolean')
10
+ return;
11
+ actions.push(action);
12
+ });
13
+ return actions;
14
+ }
15
+ /**
16
+ * Default mobile list item renderer
17
+ * @param param0 List renderer props
18
+ * @param margin Margin
19
+ * @param renderer Renderer for card content
20
+ * @returns Component
21
+ */
22
+ export function MobileListItemRenderer({ data, itemHeight }, margin, renderer) {
23
+ // Loading
24
+ if (data == null)
25
+ return React.createElement(LinearProgress, null);
26
+ // Elements
27
+ const [title, subheader, actions, children] = renderer(data);
28
+ // Half
29
+ const halfMargin = MUGlobal.half(margin);
30
+ return (React.createElement(Card, { sx: {
31
+ height: (theme) => MUGlobal.adjustWithTheme(itemHeight, margin, theme.spacing),
32
+ marginLeft: margin,
33
+ marginRight: margin,
34
+ marginTop: halfMargin,
35
+ marginBottom: halfMargin
36
+ } },
37
+ React.createElement(CardHeader, { sx: { paddingBottom: 0.5 }, action: Array.isArray(actions) ? (React.createElement(MoreFab, { iconButton: true, size: "small", anchorOrigin: {
38
+ vertical: 'bottom',
39
+ horizontal: 'right'
40
+ }, transformOrigin: {
41
+ vertical: 'top',
42
+ horizontal: 'right'
43
+ }, actions: getActions(actions), PaperProps: {
44
+ elevation: 0,
45
+ sx: {
46
+ overflow: 'visible',
47
+ filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))',
48
+ mt: -0.4,
49
+ '& .MuiAvatar-root': {
50
+ width: 32,
51
+ height: 32,
52
+ ml: -0.5,
53
+ mr: 1
54
+ },
55
+ '&:before': {
56
+ content: '""',
57
+ display: 'block',
58
+ position: 'absolute',
59
+ top: 0,
60
+ right: 14,
61
+ width: 10,
62
+ height: 10,
63
+ bgcolor: 'background.paper',
64
+ transform: 'translateY(-50%) rotate(45deg)',
65
+ zIndex: 0
66
+ }
67
+ }
68
+ } })) : (actions), title: title, titleTypographyProps: { variant: 'body2' }, subheader: subheader, subheaderTypographyProps: { variant: 'caption' } }),
69
+ React.createElement(CardContent, { sx: { paddingTop: 0 } }, children)));
70
+ }
@@ -0,0 +1,69 @@
1
+ import { DataTypes } from '@etsoo/shared';
2
+ import { SxProps, Theme } from '@mui/material';
3
+ import React from 'react';
4
+ import { ListChildComponentProps } from 'react-window';
5
+ import { GridColumn } from '../components/GridColumn';
6
+ import { GridJsonData } from '../components/GridLoader';
7
+ import { DataGridExProps } from './DataGridEx';
8
+ import { GridMethodRef } from './GridMethodRef';
9
+ import { ScrollerListExInnerItemRendererProps } from './ScrollerListEx';
10
+ export interface ResponsibleContainerProps<T extends {}, F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate> extends Omit<DataGridExProps<T>, 'height' | 'itemKey' | 'loadData' | 'mRef' | 'onScroll' | 'onItemsRendered'> {
11
+ /**
12
+ * Height will be deducted
13
+ * @param height Current calcuated height
14
+ */
15
+ adjustHeight?: (height: number) => number;
16
+ /**
17
+ * Columns
18
+ */
19
+ columns: GridColumn<T>[];
20
+ /**
21
+ * Min width to show Datagrid
22
+ */
23
+ dataGridMinWidth?: number;
24
+ /**
25
+ * Search fields
26
+ */
27
+ fields?: React.ReactElement[];
28
+ /**
29
+ * Search field template
30
+ */
31
+ fieldTemplate?: F;
32
+ /**
33
+ * Grid height
34
+ */
35
+ height?: number;
36
+ /**
37
+ * Inner item renderer
38
+ */
39
+ innerItemRenderer: (props: ScrollerListExInnerItemRendererProps<T>) => React.ReactNode;
40
+ /**
41
+ * Item renderer
42
+ */
43
+ itemRenderer?: (props: ListChildComponentProps<T>) => React.ReactElement;
44
+ /**
45
+ * Item size, a function indicates its a variable size list
46
+ */
47
+ itemSize: ((index: number) => number) | number;
48
+ /**
49
+ * Listbox SX (dataGrid determines the case)
50
+ */
51
+ listBoxSx?: (dataGrid: boolean) => SxProps<Theme>;
52
+ /**
53
+ * Load data callback
54
+ */
55
+ loadData: (data: GridJsonData & DataTypes.BasicTemplateType<F>) => PromiseLike<T[] | null | undefined>;
56
+ /**
57
+ * Methods
58
+ */
59
+ mRef?: React.MutableRefObject<GridMethodRef | undefined>;
60
+ /**
61
+ * Searchbox SX
62
+ */
63
+ searchBoxSx?: SxProps<Theme>;
64
+ /**
65
+ * Size ready to read miliseconds span
66
+ */
67
+ sizeReadyMiliseconds?: number;
68
+ }
69
+ export declare function ResponsibleContainer<T extends {}, F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate>(props: ResponsibleContainerProps<T, F>): JSX.Element;
@@ -0,0 +1,79 @@
1
+ import { Box, Stack } from '@mui/material';
2
+ import React from 'react';
3
+ import { GridDataGet } from '../components/GridLoader';
4
+ import useCombinedRefs from '../uses/useCombinedRefs';
5
+ import { useDimensions } from '../uses/useDimensions';
6
+ import { DataGridEx, DataGridExCalColumns } from './DataGridEx';
7
+ import { ScrollerListEx } from './ScrollerListEx';
8
+ import { SearchBar } from './SearchBar';
9
+ export function ResponsibleContainer(props) {
10
+ var _a;
11
+ // Destruct
12
+ const { adjustHeight, columns, dataGridMinWidth = DataGridExCalColumns(columns).total, fields, fieldTemplate, height, listBoxSx, loadData, mRef, searchBoxSx, sizeReadyMiliseconds = 0, ...rest } = props;
13
+ // Refs
14
+ const refs = React.useRef({});
15
+ const mRefs = useCombinedRefs(mRef, (ref) => {
16
+ if (ref == null)
17
+ return;
18
+ refs.current.ref = ref;
19
+ });
20
+ // Has fields
21
+ const hasFields = fields != null && fields.length > 0;
22
+ // Watch container
23
+ const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
24
+ const rect = dimensions[0][2];
25
+ const showDataGrid = ((_a = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _a !== void 0 ? _a : 0) >= dataGridMinWidth;
26
+ console.log(hasFields, showDataGrid, refs);
27
+ React.useEffect(() => {
28
+ if (rect != null && rect.height > 50 && height == null) {
29
+ let gridHeight = window.innerHeight - Math.round(rect.top + rect.height + 1);
30
+ const style = window.getComputedStyle(dimensions[0][1]);
31
+ const boxPadding = parseFloat(style.paddingLeft);
32
+ if (!isNaN(boxPadding))
33
+ gridHeight -= 2 * boxPadding;
34
+ if (adjustHeight != null) {
35
+ gridHeight -= adjustHeight(gridHeight);
36
+ }
37
+ if (gridHeight !== refs.current.height) {
38
+ refs.current.height = gridHeight;
39
+ }
40
+ }
41
+ }, [rect]);
42
+ const localLoadData = (props) => {
43
+ const data = GridDataGet(props, fieldTemplate);
44
+ return loadData(data);
45
+ };
46
+ const gridHeight = refs.current.height;
47
+ const list = React.useMemo(() => {
48
+ if (gridHeight == null)
49
+ return;
50
+ if (showDataGrid) {
51
+ // Delete
52
+ delete rest.itemRenderer;
53
+ return (React.createElement(Box, { sx: listBoxSx == null ? undefined : listBoxSx(true) },
54
+ React.createElement(DataGridEx, { autoLoad: !hasFields, height: gridHeight, loadData: localLoadData, mRef: mRefs, columns: columns, ...rest })));
55
+ }
56
+ // Delete
57
+ delete rest.checkable;
58
+ delete rest.borderRowsCount;
59
+ delete rest.bottomHeight;
60
+ delete rest.footerItemRenderer;
61
+ delete rest.headerHeight;
62
+ delete rest.hideFooter;
63
+ delete rest.hoverColor;
64
+ delete rest.selectable;
65
+ return (React.createElement(Box, { sx: listBoxSx == null ? undefined : listBoxSx(false) },
66
+ React.createElement(ScrollerListEx, { autoLoad: !hasFields, height: gridHeight, loadData: localLoadData, mRef: mRefs, ...rest })));
67
+ }, [gridHeight, showDataGrid]);
68
+ // On submit callback
69
+ const onSubmit = (data, _reset) => {
70
+ if (data == null || rect == null || refs.current.ref == null)
71
+ return;
72
+ refs.current.ref.reset({ data });
73
+ };
74
+ // Layout
75
+ return (React.createElement(Stack, null,
76
+ hasFields && (React.createElement(Box, { ref: dimensions[0][0], sx: searchBoxSx },
77
+ React.createElement(SearchBar, { fields: fields, onSubmit: onSubmit }))),
78
+ list));
79
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/react",
3
- "version": "1.4.11",
3
+ "version": "1.4.15",
4
4
  "description": "TypeScript ReactJs framework",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -50,7 +50,7 @@
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.18",
53
+ "@etsoo/appscript": "^1.2.20",
54
54
  "@etsoo/notificationbase": "^1.1.0",
55
55
  "@etsoo/shared": "^1.1.0",
56
56
  "@mui/icons-material": "^5.2.5",
@@ -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.5",
92
+ "jest": "^27.4.7",
93
93
  "react-test-renderer": "^17.0.2",
94
94
  "ts-jest": "^27.1.2",
95
95
  "typescript": "^4.5.4"
@@ -223,8 +223,9 @@ export class ReactApp<
223
223
  React.Fragment,
224
224
  null,
225
225
  main,
226
+ React.createElement('br'),
226
227
  React.createElement(
227
- 'div',
228
+ 'span',
228
229
  { style: { fontSize: '9px' } },
229
230
  tip
230
231
  )
package/src/index.ts CHANGED
@@ -55,6 +55,7 @@ export * from './mu/ItemList';
55
55
  export * from './mu/ListItemRightIcon';
56
56
  export * from './mu/LoadingButton';
57
57
  export * from './mu/MaskInput';
58
+ export * from './mu/MobileListItemRenderer';
58
59
  export * from './mu/MoreFab';
59
60
  export * from './mu/MUGlobal';
60
61
  export * from './mu/NotifierMU';
@@ -62,6 +63,7 @@ export * from './mu/OptionGroup';
62
63
  export * from './mu/PList';
63
64
  export * from './mu/ProgressCount';
64
65
  export * from './mu/PullToRefreshUI';
66
+ export * from './mu/ResponsibleContainer';
65
67
  export * from './mu/RLink';
66
68
  export * from './mu/ScrollerListEx';
67
69
  export * from './mu/ScrollTopFab';
@@ -68,7 +68,8 @@ export interface DnDListProps<D extends {}, E extends React.ElementType> {
68
68
  sideRenderer?: (
69
69
  top: boolean,
70
70
  addItem: (item: D) => boolean,
71
- addItems: (items: D[]) => number
71
+ addItems: (items: D[]) => number,
72
+ reloadItems: () => void
72
73
  ) => React.ReactNode;
73
74
 
74
75
  /**
@@ -210,15 +211,21 @@ export function DnDList<
210
211
  changeItems(newItems);
211
212
  };
212
213
 
213
- React.useEffect(() => {
214
+ // Reload items
215
+ const reloadItems = () => {
214
216
  // Load data
215
217
  loadData(name).then((items) => setItems(items));
218
+ };
219
+
220
+ React.useEffect(() => {
221
+ // Load data
222
+ reloadItems();
216
223
  }, [name]);
217
224
 
218
225
  // Layout
219
226
  return (
220
227
  <React.Fragment>
221
- {sideRenderer && sideRenderer(true, addItem, addItems)}
228
+ {sideRenderer && sideRenderer(true, addItem, addItems, reloadItems)}
222
229
  <Component {...componentProps}>
223
230
  <DragDropContext onDragEnd={onDragEndLocal}>
224
231
  <Droppable droppableId={name}>
@@ -274,7 +281,8 @@ export function DnDList<
274
281
  </Droppable>
275
282
  </DragDropContext>
276
283
  </Component>
277
- {sideRenderer && sideRenderer(false, addItem, addItems)}
284
+ {sideRenderer &&
285
+ sideRenderer(false, addItem, addItems, reloadItems)}
278
286
  </React.Fragment>
279
287
  );
280
288
  }
@@ -0,0 +1,113 @@
1
+ import { Card, CardContent, CardHeader, LinearProgress } from '@mui/material';
2
+ import React from 'react';
3
+ import { ListItemReact } from '../components/ListItemReact';
4
+ import { MoreFab } from './MoreFab';
5
+ import { MUGlobal } from './MUGlobal';
6
+ import { ScrollerListExInnerItemRendererProps } from './ScrollerListEx';
7
+
8
+ function getActions(input: (ListItemReact | boolean)[]): ListItemReact[] {
9
+ // Actions
10
+ const actions: ListItemReact[] = [];
11
+ input.forEach((action) => {
12
+ if (typeof action === 'boolean') return;
13
+ actions.push(action);
14
+ });
15
+ return actions;
16
+ }
17
+
18
+ /**
19
+ * Default mobile list item renderer
20
+ * @param param0 List renderer props
21
+ * @param margin Margin
22
+ * @param renderer Renderer for card content
23
+ * @returns Component
24
+ */
25
+ export function MobileListItemRenderer<T>(
26
+ { data, itemHeight }: ScrollerListExInnerItemRendererProps<T>,
27
+ margin: {},
28
+ renderer: (
29
+ data: T
30
+ ) => [
31
+ string,
32
+ string | undefined,
33
+ React.ReactNode | (ListItemReact | boolean)[],
34
+ React.ReactNode
35
+ ]
36
+ ) {
37
+ // Loading
38
+ if (data == null) return <LinearProgress />;
39
+
40
+ // Elements
41
+ const [title, subheader, actions, children] = renderer(data);
42
+
43
+ // Half
44
+ const halfMargin = MUGlobal.half(margin);
45
+
46
+ return (
47
+ <Card
48
+ sx={{
49
+ height: (theme) =>
50
+ MUGlobal.adjustWithTheme(itemHeight, margin, theme.spacing),
51
+ marginLeft: margin,
52
+ marginRight: margin,
53
+ marginTop: halfMargin,
54
+ marginBottom: halfMargin
55
+ }}
56
+ >
57
+ <CardHeader
58
+ sx={{ paddingBottom: 0.5 }}
59
+ action={
60
+ Array.isArray(actions) ? (
61
+ <MoreFab
62
+ iconButton
63
+ size="small"
64
+ anchorOrigin={{
65
+ vertical: 'bottom',
66
+ horizontal: 'right'
67
+ }}
68
+ transformOrigin={{
69
+ vertical: 'top',
70
+ horizontal: 'right'
71
+ }}
72
+ actions={getActions(actions)}
73
+ PaperProps={{
74
+ elevation: 0,
75
+ sx: {
76
+ overflow: 'visible',
77
+ filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))',
78
+ mt: -0.4,
79
+ '& .MuiAvatar-root': {
80
+ width: 32,
81
+ height: 32,
82
+ ml: -0.5,
83
+ mr: 1
84
+ },
85
+ '&:before': {
86
+ content: '""',
87
+ display: 'block',
88
+ position: 'absolute',
89
+ top: 0,
90
+ right: 14,
91
+ width: 10,
92
+ height: 10,
93
+ bgcolor: 'background.paper',
94
+ transform:
95
+ 'translateY(-50%) rotate(45deg)',
96
+ zIndex: 0
97
+ }
98
+ }
99
+ }}
100
+ />
101
+ ) : (
102
+ actions
103
+ )
104
+ }
105
+ title={title}
106
+ titleTypographyProps={{ variant: 'body2' }}
107
+ subheader={subheader}
108
+ subheaderTypographyProps={{ variant: 'caption' }}
109
+ />
110
+ <CardContent sx={{ paddingTop: 0 }}>{children}</CardContent>
111
+ </Card>
112
+ );
113
+ }
@@ -0,0 +1,242 @@
1
+ import { DataTypes } from '@etsoo/shared';
2
+ import { Box, Stack, SxProps, Theme } from '@mui/material';
3
+ import React from 'react';
4
+ import { ListChildComponentProps } from 'react-window';
5
+ import { GridColumn } from '../components/GridColumn';
6
+ import {
7
+ GridDataGet,
8
+ GridJsonData,
9
+ GridLoadDataProps
10
+ } from '../components/GridLoader';
11
+ import useCombinedRefs from '../uses/useCombinedRefs';
12
+ import { useDimensions } from '../uses/useDimensions';
13
+ import {
14
+ DataGridEx,
15
+ DataGridExCalColumns,
16
+ DataGridExProps
17
+ } from './DataGridEx';
18
+ import { GridMethodRef } from './GridMethodRef';
19
+ import {
20
+ ScrollerListEx,
21
+ ScrollerListExInnerItemRendererProps
22
+ } from './ScrollerListEx';
23
+ import { SearchBar } from './SearchBar';
24
+
25
+ export interface ResponsibleContainerProps<
26
+ T extends {},
27
+ F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate
28
+ > extends Omit<
29
+ DataGridExProps<T>,
30
+ | 'height'
31
+ | 'itemKey'
32
+ | 'loadData'
33
+ | 'mRef'
34
+ | 'onScroll'
35
+ | 'onItemsRendered'
36
+ > {
37
+ /**
38
+ * Height will be deducted
39
+ * @param height Current calcuated height
40
+ */
41
+ adjustHeight?: (height: number) => number;
42
+
43
+ /**
44
+ * Columns
45
+ */
46
+ columns: GridColumn<T>[];
47
+
48
+ /**
49
+ * Min width to show Datagrid
50
+ */
51
+ dataGridMinWidth?: number;
52
+
53
+ /**
54
+ * Search fields
55
+ */
56
+ fields?: React.ReactElement[];
57
+
58
+ /**
59
+ * Search field template
60
+ */
61
+ fieldTemplate?: F;
62
+
63
+ /**
64
+ * Grid height
65
+ */
66
+ height?: number;
67
+
68
+ /**
69
+ * Inner item renderer
70
+ */
71
+ innerItemRenderer: (
72
+ props: ScrollerListExInnerItemRendererProps<T>
73
+ ) => React.ReactNode;
74
+
75
+ /**
76
+ * Item renderer
77
+ */
78
+ itemRenderer?: (props: ListChildComponentProps<T>) => React.ReactElement;
79
+
80
+ /**
81
+ * Item size, a function indicates its a variable size list
82
+ */
83
+ itemSize: ((index: number) => number) | number;
84
+
85
+ /**
86
+ * Listbox SX (dataGrid determines the case)
87
+ */
88
+ listBoxSx?: (dataGrid: boolean) => SxProps<Theme>;
89
+
90
+ /**
91
+ * Load data callback
92
+ */
93
+ loadData: (
94
+ data: GridJsonData & DataTypes.BasicTemplateType<F>
95
+ ) => PromiseLike<T[] | null | undefined>;
96
+
97
+ /**
98
+ * Methods
99
+ */
100
+ mRef?: React.MutableRefObject<GridMethodRef | undefined>;
101
+
102
+ /**
103
+ * Searchbox SX
104
+ */
105
+ searchBoxSx?: SxProps<Theme>;
106
+
107
+ /**
108
+ * Size ready to read miliseconds span
109
+ */
110
+ sizeReadyMiliseconds?: number;
111
+ }
112
+
113
+ interface LocalRefs {
114
+ height?: number;
115
+ ref?: GridMethodRef;
116
+ }
117
+
118
+ export function ResponsibleContainer<
119
+ T extends {},
120
+ F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate
121
+ >(props: ResponsibleContainerProps<T, F>) {
122
+ // Destruct
123
+ const {
124
+ adjustHeight,
125
+ columns,
126
+ dataGridMinWidth = DataGridExCalColumns(columns).total,
127
+ fields,
128
+ fieldTemplate,
129
+ height,
130
+ listBoxSx,
131
+ loadData,
132
+ mRef,
133
+ searchBoxSx,
134
+ sizeReadyMiliseconds = 0,
135
+ ...rest
136
+ } = props;
137
+
138
+ // Refs
139
+ const refs = React.useRef<LocalRefs>({});
140
+
141
+ const mRefs = useCombinedRefs(mRef, (ref: GridMethodRef) => {
142
+ if (ref == null) return;
143
+ refs.current.ref = ref;
144
+ });
145
+
146
+ // Has fields
147
+ const hasFields = fields != null && fields.length > 0;
148
+
149
+ // Watch container
150
+ const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
151
+ const rect = dimensions[0][2];
152
+ const showDataGrid = (rect?.width ?? 0) >= dataGridMinWidth;
153
+
154
+ console.log(hasFields, showDataGrid, refs);
155
+
156
+ React.useEffect(() => {
157
+ if (rect != null && rect.height > 50 && height == null) {
158
+ let gridHeight =
159
+ window.innerHeight - Math.round(rect.top + rect.height + 1);
160
+
161
+ const style = window.getComputedStyle(dimensions[0][1]!);
162
+ const boxPadding = parseFloat(style.paddingLeft);
163
+ if (!isNaN(boxPadding)) gridHeight -= 2 * boxPadding;
164
+
165
+ if (adjustHeight != null) {
166
+ gridHeight -= adjustHeight(gridHeight);
167
+ }
168
+
169
+ if (gridHeight !== refs.current.height) {
170
+ refs.current.height = gridHeight;
171
+ }
172
+ }
173
+ }, [rect]);
174
+
175
+ const localLoadData = (props: GridLoadDataProps) => {
176
+ const data = GridDataGet(props, fieldTemplate);
177
+ return loadData(data);
178
+ };
179
+
180
+ const gridHeight = refs.current.height;
181
+ const list = React.useMemo(() => {
182
+ if (gridHeight == null) return;
183
+
184
+ if (showDataGrid) {
185
+ // Delete
186
+ delete rest.itemRenderer;
187
+
188
+ return (
189
+ <Box sx={listBoxSx == null ? undefined : listBoxSx(true)}>
190
+ <DataGridEx<T>
191
+ autoLoad={!hasFields}
192
+ height={gridHeight}
193
+ loadData={localLoadData}
194
+ mRef={mRefs}
195
+ columns={columns}
196
+ {...rest}
197
+ />
198
+ </Box>
199
+ );
200
+ }
201
+
202
+ // Delete
203
+ delete rest.checkable;
204
+ delete rest.borderRowsCount;
205
+ delete rest.bottomHeight;
206
+ delete rest.footerItemRenderer;
207
+ delete rest.headerHeight;
208
+ delete rest.hideFooter;
209
+ delete rest.hoverColor;
210
+ delete rest.selectable;
211
+
212
+ return (
213
+ <Box sx={listBoxSx == null ? undefined : listBoxSx(false)}>
214
+ <ScrollerListEx<T>
215
+ autoLoad={!hasFields}
216
+ height={gridHeight}
217
+ loadData={localLoadData}
218
+ mRef={mRefs}
219
+ {...rest}
220
+ />
221
+ </Box>
222
+ );
223
+ }, [gridHeight, showDataGrid]);
224
+
225
+ // On submit callback
226
+ const onSubmit = (data: FormData, _reset: boolean) => {
227
+ if (data == null || rect == null || refs.current.ref == null) return;
228
+ refs.current.ref.reset({ data });
229
+ };
230
+
231
+ // Layout
232
+ return (
233
+ <Stack>
234
+ {hasFields && (
235
+ <Box ref={dimensions[0][0]} sx={searchBoxSx}>
236
+ <SearchBar fields={fields} onSubmit={onSubmit} />
237
+ </Box>
238
+ )}
239
+ {list}
240
+ </Stack>
241
+ );
242
+ }