@etsoo/react 1.4.20 → 1.4.21
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.
- package/lib/components/GridLoader.d.ts +6 -2
- package/lib/components/ScrollerGrid.d.ts +1 -1
- package/lib/components/ScrollerGrid.js +48 -43
- package/lib/components/ScrollerList.js +30 -32
- package/lib/mu/DataGridEx.d.ts +1 -1
- package/lib/mu/DataGridEx.js +23 -49
- package/lib/mu/DataGridRenderers.d.ts +1 -1
- package/lib/mu/DataGridRenderers.js +4 -4
- package/lib/mu/GridMethodRef.d.ts +2 -1
- package/lib/mu/ResponsibleContainer.d.ts +12 -0
- package/lib/mu/ResponsibleContainer.js +77 -30
- package/lib/mu/ScrollerListEx.js +1 -2
- package/lib/mu/TableEx.js +27 -25
- package/lib/uses/useDimensions.js +0 -8
- package/package.json +1 -1
- package/src/components/GridLoader.ts +7 -2
- package/src/components/ScrollerGrid.tsx +66 -63
- package/src/components/ScrollerList.tsx +44 -48
- package/src/mu/DataGridEx.tsx +51 -91
- package/src/mu/DataGridRenderers.tsx +7 -8
- package/src/mu/GridMethodRef.ts +3 -1
- package/src/mu/ResponsibleContainer.tsx +117 -36
- package/src/mu/ScrollerListEx.tsx +1 -2
- package/src/mu/TableEx.tsx +40 -46
- package/src/uses/useDimensions.ts +0 -11
|
@@ -2,6 +2,7 @@ import { DataTypes } from '@etsoo/shared';
|
|
|
2
2
|
import { Box, Stack, SxProps, Theme } from '@mui/material';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { ListChildComponentProps } from 'react-window';
|
|
5
|
+
import { Labels } from '../app/Labels';
|
|
5
6
|
import { GridColumn } from '../components/GridColumn';
|
|
6
7
|
import {
|
|
7
8
|
GridDataGet,
|
|
@@ -16,12 +17,16 @@ import {
|
|
|
16
17
|
DataGridExProps
|
|
17
18
|
} from './DataGridEx';
|
|
18
19
|
import { GridMethodRef } from './GridMethodRef';
|
|
20
|
+
import { PullToRefreshUI } from './PullToRefreshUI';
|
|
19
21
|
import {
|
|
20
22
|
ScrollerListEx,
|
|
21
23
|
ScrollerListExInnerItemRendererProps
|
|
22
24
|
} from './ScrollerListEx';
|
|
23
25
|
import { SearchBar } from './SearchBar';
|
|
24
26
|
|
|
27
|
+
/**
|
|
28
|
+
* ResponsibleContainer props
|
|
29
|
+
*/
|
|
25
30
|
export interface ResponsibleContainerProps<
|
|
26
31
|
T extends {},
|
|
27
32
|
F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate
|
|
@@ -99,6 +104,11 @@ export interface ResponsibleContainerProps<
|
|
|
99
104
|
*/
|
|
100
105
|
mRef?: React.MutableRefObject<GridMethodRef | undefined>;
|
|
101
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Pull to refresh data
|
|
109
|
+
*/
|
|
110
|
+
pullToRefresh?: boolean;
|
|
111
|
+
|
|
102
112
|
/**
|
|
103
113
|
* Searchbox SX
|
|
104
114
|
*/
|
|
@@ -111,10 +121,16 @@ export interface ResponsibleContainerProps<
|
|
|
111
121
|
}
|
|
112
122
|
|
|
113
123
|
interface LocalRefs {
|
|
114
|
-
|
|
124
|
+
rect?: DOMRect;
|
|
115
125
|
ref?: GridMethodRef;
|
|
126
|
+
mounted?: boolean;
|
|
116
127
|
}
|
|
117
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Responsible container
|
|
131
|
+
* @param props Props
|
|
132
|
+
* @returns Layout
|
|
133
|
+
*/
|
|
118
134
|
export function ResponsibleContainer<
|
|
119
135
|
T extends {},
|
|
120
136
|
F extends DataTypes.BasicTemplate = DataTypes.BasicTemplate
|
|
@@ -130,11 +146,15 @@ export function ResponsibleContainer<
|
|
|
130
146
|
listBoxSx,
|
|
131
147
|
loadData,
|
|
132
148
|
mRef,
|
|
149
|
+
pullToRefresh = true,
|
|
133
150
|
searchBoxSx,
|
|
134
151
|
sizeReadyMiliseconds = 0,
|
|
135
152
|
...rest
|
|
136
153
|
} = props;
|
|
137
154
|
|
|
155
|
+
// Labels
|
|
156
|
+
const labels = Labels.CommonPage;
|
|
157
|
+
|
|
138
158
|
// Refs
|
|
139
159
|
const refs = React.useRef<LocalRefs>({});
|
|
140
160
|
|
|
@@ -143,23 +163,72 @@ export function ResponsibleContainer<
|
|
|
143
163
|
refs.current.ref = ref;
|
|
144
164
|
});
|
|
145
165
|
|
|
166
|
+
// Update mounted state
|
|
167
|
+
React.useEffect(() => {
|
|
168
|
+
return () => {
|
|
169
|
+
refs.current.mounted = false;
|
|
170
|
+
};
|
|
171
|
+
}, []);
|
|
172
|
+
|
|
146
173
|
// Has fields
|
|
147
174
|
const hasFields = fields != null && fields.length > 0;
|
|
148
175
|
|
|
149
|
-
//
|
|
150
|
-
const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds);
|
|
151
|
-
const rect = dimensions[0][2];
|
|
152
|
-
|
|
176
|
+
// Load data
|
|
153
177
|
const localLoadData = (props: GridLoadDataProps) => {
|
|
178
|
+
refs.current.mounted = true;
|
|
154
179
|
const data = GridDataGet(props, fieldTemplate);
|
|
155
180
|
return loadData(data);
|
|
156
181
|
};
|
|
157
182
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (rect == null) return;
|
|
183
|
+
// On submit callback
|
|
184
|
+
const onSubmit = (data: FormData, _reset: boolean) => {
|
|
185
|
+
if (data == null || rect == null || refs.current.ref == null) return;
|
|
186
|
+
refs.current.ref.reset({ data });
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Watch container
|
|
190
|
+
const { dimensions } = useDimensions(
|
|
191
|
+
1,
|
|
192
|
+
undefined,
|
|
193
|
+
sizeReadyMiliseconds,
|
|
194
|
+
(_preRect, rect) => {
|
|
195
|
+
// Check
|
|
196
|
+
if (rect == null) return true;
|
|
197
|
+
|
|
198
|
+
// Last rect
|
|
199
|
+
const lastRect = refs.current.rect;
|
|
200
|
+
|
|
201
|
+
// 32 = scroll bar width
|
|
202
|
+
if (
|
|
203
|
+
lastRect != null &&
|
|
204
|
+
refs.current.mounted !== true &&
|
|
205
|
+
Math.abs(rect.width - lastRect.width) <= 32 &&
|
|
206
|
+
Math.abs(rect.height - lastRect.height) <= 32
|
|
207
|
+
)
|
|
208
|
+
return true;
|
|
209
|
+
|
|
210
|
+
// Hold the new rect
|
|
211
|
+
refs.current.rect = rect;
|
|
212
|
+
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// Rect
|
|
218
|
+
const rect = dimensions[0][2];
|
|
219
|
+
|
|
220
|
+
// Create list
|
|
221
|
+
const [list, showDataGrid] = (() => {
|
|
222
|
+
// No layout
|
|
223
|
+
if (rect == null) return [null, false];
|
|
161
224
|
|
|
162
|
-
//
|
|
225
|
+
// Width
|
|
226
|
+
const width = rect.width;
|
|
227
|
+
|
|
228
|
+
// Show DataGrid or List dependng on width
|
|
229
|
+
const showDataGrid = width >= dataGridMinWidth;
|
|
230
|
+
|
|
231
|
+
// Height
|
|
163
232
|
let heightLocal: number;
|
|
164
233
|
if (height != null) {
|
|
165
234
|
heightLocal = height;
|
|
@@ -177,31 +246,24 @@ export function ResponsibleContainer<
|
|
|
177
246
|
}
|
|
178
247
|
}
|
|
179
248
|
|
|
180
|
-
console.log(rect, heightLocal, refs.current.height);
|
|
181
|
-
|
|
182
|
-
// Same height
|
|
183
|
-
if (heightLocal === refs.current.height) return;
|
|
184
|
-
refs.current.height = heightLocal;
|
|
185
|
-
|
|
186
|
-
// Show DataGrid or List dependng on width
|
|
187
|
-
const showDataGrid = rect.width >= dataGridMinWidth;
|
|
188
|
-
|
|
189
249
|
if (showDataGrid) {
|
|
190
250
|
// Delete
|
|
191
251
|
delete rest.itemRenderer;
|
|
192
252
|
|
|
193
|
-
return
|
|
253
|
+
return [
|
|
194
254
|
<Box sx={listBoxSx == null ? undefined : listBoxSx(true)}>
|
|
195
255
|
<DataGridEx<T>
|
|
196
256
|
autoLoad={!hasFields}
|
|
197
257
|
height={heightLocal}
|
|
258
|
+
width={rect.width}
|
|
198
259
|
loadData={localLoadData}
|
|
199
260
|
mRef={mRefs}
|
|
200
261
|
columns={columns}
|
|
201
262
|
{...rest}
|
|
202
263
|
/>
|
|
203
|
-
</Box
|
|
204
|
-
|
|
264
|
+
</Box>,
|
|
265
|
+
true
|
|
266
|
+
];
|
|
205
267
|
}
|
|
206
268
|
|
|
207
269
|
// Delete
|
|
@@ -214,34 +276,53 @@ export function ResponsibleContainer<
|
|
|
214
276
|
delete rest.hoverColor;
|
|
215
277
|
delete rest.selectable;
|
|
216
278
|
|
|
217
|
-
return
|
|
279
|
+
return [
|
|
218
280
|
<Box sx={listBoxSx == null ? undefined : listBoxSx(false)}>
|
|
219
281
|
<ScrollerListEx<T>
|
|
220
282
|
autoLoad={!hasFields}
|
|
283
|
+
width={rect.width}
|
|
221
284
|
height={heightLocal}
|
|
222
285
|
loadData={localLoadData}
|
|
223
286
|
mRef={mRefs}
|
|
224
287
|
{...rest}
|
|
225
288
|
/>
|
|
226
|
-
</Box
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
289
|
+
</Box>,
|
|
290
|
+
false
|
|
291
|
+
];
|
|
292
|
+
})();
|
|
293
|
+
|
|
294
|
+
// Pull container
|
|
295
|
+
const pullContainer = list
|
|
296
|
+
? showDataGrid
|
|
297
|
+
? '.DataGridEx-Body'
|
|
298
|
+
: '.ScrollerListEx-Body'
|
|
299
|
+
: undefined;
|
|
235
300
|
|
|
236
301
|
// Layout
|
|
237
302
|
return (
|
|
238
|
-
<
|
|
239
|
-
|
|
303
|
+
<React.Fragment>
|
|
304
|
+
<Stack>
|
|
240
305
|
<Box ref={dimensions[0][0]} sx={searchBoxSx}>
|
|
241
|
-
|
|
306
|
+
{hasFields && (
|
|
307
|
+
<SearchBar fields={fields} onSubmit={onSubmit} />
|
|
308
|
+
)}
|
|
242
309
|
</Box>
|
|
310
|
+
{list}
|
|
311
|
+
</Stack>
|
|
312
|
+
{pullToRefresh && pullContainer && (
|
|
313
|
+
<PullToRefreshUI
|
|
314
|
+
mainElement={pullContainer}
|
|
315
|
+
triggerElement={pullContainer}
|
|
316
|
+
instructionsPullToRefresh={labels.pullToRefresh}
|
|
317
|
+
instructionsReleaseToRefresh={labels.releaseToRefresh}
|
|
318
|
+
instructionsRefreshing={labels.refreshing}
|
|
319
|
+
onRefresh={() => refs.current.ref?.reset()}
|
|
320
|
+
shouldPullToRefresh={() => {
|
|
321
|
+
const container = document.querySelector(pullContainer);
|
|
322
|
+
return !container?.scrollTop;
|
|
323
|
+
}}
|
|
324
|
+
/>
|
|
243
325
|
)}
|
|
244
|
-
|
|
245
|
-
</Stack>
|
|
326
|
+
</React.Fragment>
|
|
246
327
|
);
|
|
247
328
|
}
|
|
@@ -174,8 +174,7 @@ export function ScrollerListEx<T extends Record<string, unknown>>(
|
|
|
174
174
|
if (selectedData != null && selectedData[idField] === data[idField])
|
|
175
175
|
return;
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
selectedDiv.classList.remove(selectedClassName);
|
|
177
|
+
selectedDiv?.classList.remove(selectedClassName);
|
|
179
178
|
|
|
180
179
|
div.classList.add(selectedClassName);
|
|
181
180
|
|
package/src/mu/TableEx.tsx
CHANGED
|
@@ -145,43 +145,41 @@ export function TableEx<T extends Record<string, unknown>>(
|
|
|
145
145
|
rowsPerPageLocal = 10;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
// Rows
|
|
149
|
+
const [rows, updateRows] = React.useState<T[]>([]);
|
|
150
|
+
const setRows = (rows: T[]) => {
|
|
151
|
+
state.loadedItems = rows.length;
|
|
152
|
+
updateRows(rows);
|
|
153
|
+
};
|
|
154
|
+
|
|
148
155
|
// States
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
? columns.find((column) => column.field === defaultOrderBy)
|
|
164
|
-
?.sortAsc
|
|
165
|
-
: undefined,
|
|
166
|
-
rows: [],
|
|
167
|
-
batchSize: rowsPerPageLocal,
|
|
168
|
-
selectedItems: []
|
|
169
|
-
}
|
|
170
|
-
);
|
|
171
|
-
const isMounted = React.useRef(true);
|
|
156
|
+
const stateRefs = React.useRef<GridLoaderStates<T>>({
|
|
157
|
+
autoLoad,
|
|
158
|
+
currentPage: 0,
|
|
159
|
+
loadedItems: 0,
|
|
160
|
+
hasNextPage: true,
|
|
161
|
+
isNextPageLoading: false,
|
|
162
|
+
orderBy: defaultOrderBy,
|
|
163
|
+
orderByAsc: defaultOrderBy
|
|
164
|
+
? columns.find((column) => column.field === defaultOrderBy)?.sortAsc
|
|
165
|
+
: undefined,
|
|
166
|
+
batchSize: rowsPerPageLocal,
|
|
167
|
+
selectedItems: []
|
|
168
|
+
});
|
|
169
|
+
const state = stateRefs.current;
|
|
172
170
|
|
|
173
171
|
// Reset the state and load again
|
|
174
|
-
const reset = (add?:
|
|
175
|
-
const
|
|
172
|
+
const reset = (add?: Partial<GridLoaderStates<T>>) => {
|
|
173
|
+
const resetState: Partial<GridLoaderStates<T>> = {
|
|
176
174
|
autoLoad: true,
|
|
177
175
|
currentPage: 0,
|
|
176
|
+
loadedItems: 0,
|
|
178
177
|
hasNextPage: true,
|
|
179
178
|
isNextPageLoading: false,
|
|
180
179
|
lastLoadedItems: undefined,
|
|
181
|
-
rows: [],
|
|
182
180
|
...add
|
|
183
181
|
};
|
|
184
|
-
|
|
182
|
+
Object.assign(state, resetState);
|
|
185
183
|
};
|
|
186
184
|
|
|
187
185
|
React.useImperativeHandle(
|
|
@@ -211,7 +209,8 @@ export function TableEx<T extends Record<string, unknown>>(
|
|
|
211
209
|
state.isNextPageLoading = true;
|
|
212
210
|
|
|
213
211
|
// Parameters
|
|
214
|
-
const { currentPage, batchSize, orderBy, orderByAsc, data } =
|
|
212
|
+
const { currentPage, batchSize, orderBy, orderByAsc, data, isMounted } =
|
|
213
|
+
state;
|
|
215
214
|
|
|
216
215
|
const loadProps: GridLoadDataProps = {
|
|
217
216
|
currentPage,
|
|
@@ -222,18 +221,18 @@ export function TableEx<T extends Record<string, unknown>>(
|
|
|
222
221
|
};
|
|
223
222
|
|
|
224
223
|
loadData(loadProps).then((result) => {
|
|
225
|
-
|
|
224
|
+
state.isMounted = true;
|
|
225
|
+
if (result == null || isMounted === false) {
|
|
226
226
|
return;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
const newItems = result.length;
|
|
230
|
+
state.lastLoadedItems = newItems;
|
|
231
|
+
state.hasNextPage = newItems >= batchSize;
|
|
232
|
+
state.isNextPageLoading = false;
|
|
230
233
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
lastLoadedItems: newItems,
|
|
234
|
-
hasNextPage: newItems >= batchSize,
|
|
235
|
-
isNextPageLoading: false
|
|
236
|
-
});
|
|
234
|
+
// Update rows
|
|
235
|
+
setRows(result);
|
|
237
236
|
});
|
|
238
237
|
};
|
|
239
238
|
|
|
@@ -246,12 +245,12 @@ export function TableEx<T extends Record<string, unknown>>(
|
|
|
246
245
|
const handleChangeRowsPerPage = (
|
|
247
246
|
event: React.ChangeEvent<HTMLInputElement>
|
|
248
247
|
) => {
|
|
249
|
-
const
|
|
250
|
-
reset({
|
|
248
|
+
const batchSize = parseInt(event.target.value);
|
|
249
|
+
reset({ batchSize });
|
|
251
250
|
};
|
|
252
251
|
|
|
253
252
|
const handleSelect = (item: T, checked: Boolean) => {
|
|
254
|
-
const selectedItems =
|
|
253
|
+
const selectedItems = state.selectedItems;
|
|
255
254
|
|
|
256
255
|
const index = selectedItems.findIndex(
|
|
257
256
|
(selectedItem) => selectedItem[idField] === item[idField]
|
|
@@ -265,14 +264,12 @@ export function TableEx<T extends Record<string, unknown>>(
|
|
|
265
264
|
if (onSelectChange != null) {
|
|
266
265
|
onSelectChange(selectedItems);
|
|
267
266
|
}
|
|
268
|
-
|
|
269
|
-
stateUpdate({ selectedItems });
|
|
270
267
|
};
|
|
271
268
|
|
|
272
269
|
const handleSelectAll = (checked: boolean) => {
|
|
273
|
-
const selectedItems =
|
|
270
|
+
const selectedItems = state.selectedItems;
|
|
274
271
|
|
|
275
|
-
|
|
272
|
+
rows.forEach((row) => {
|
|
276
273
|
const index = selectedItems.findIndex(
|
|
277
274
|
(selectedItem) => selectedItem[idField] === row[idField]
|
|
278
275
|
);
|
|
@@ -287,8 +284,6 @@ export function TableEx<T extends Record<string, unknown>>(
|
|
|
287
284
|
if (onSelectChange != null) {
|
|
288
285
|
onSelectChange(selectedItems);
|
|
289
286
|
}
|
|
290
|
-
|
|
291
|
-
stateUpdate({ selectedItems });
|
|
292
287
|
};
|
|
293
288
|
|
|
294
289
|
// New sort
|
|
@@ -303,7 +298,6 @@ export function TableEx<T extends Record<string, unknown>>(
|
|
|
303
298
|
hasNextPage,
|
|
304
299
|
lastLoadedItems,
|
|
305
300
|
orderBy,
|
|
306
|
-
rows,
|
|
307
301
|
batchSize,
|
|
308
302
|
selectedItems
|
|
309
303
|
} = state;
|
|
@@ -333,7 +327,7 @@ export function TableEx<T extends Record<string, unknown>>(
|
|
|
333
327
|
|
|
334
328
|
React.useEffect(() => {
|
|
335
329
|
return () => {
|
|
336
|
-
isMounted
|
|
330
|
+
state.isMounted = false;
|
|
337
331
|
};
|
|
338
332
|
}, []);
|
|
339
333
|
|
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
import { DomUtils } from '@etsoo/shared';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
|
|
4
|
-
const createRef = (
|
|
5
|
-
source: [React.RefCallback<Element>, Element?, DOMRect?][],
|
|
6
|
-
index: number
|
|
7
|
-
): [React.RefCallback<Element>] => {
|
|
8
|
-
return [
|
|
9
|
-
(instance) => {
|
|
10
|
-
if (instance != null) source[index][1] = instance;
|
|
11
|
-
}
|
|
12
|
-
];
|
|
13
|
-
};
|
|
14
|
-
|
|
15
4
|
interface states {
|
|
16
5
|
count: number;
|
|
17
6
|
indices: number[];
|