@etsoo/react 1.4.17 → 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 +90 -40
- 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 +130 -47
- 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,58 +163,107 @@ 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
|
|
|
176
|
+
// Load data
|
|
177
|
+
const localLoadData = (props: GridLoadDataProps) => {
|
|
178
|
+
refs.current.mounted = true;
|
|
179
|
+
const data = GridDataGet(props, fieldTemplate);
|
|
180
|
+
return loadData(data);
|
|
181
|
+
};
|
|
182
|
+
|
|
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
|
+
|
|
149
189
|
// Watch container
|
|
150
|
-
const { dimensions } = useDimensions(
|
|
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
|
|
151
218
|
const rect = dimensions[0][2];
|
|
152
|
-
const showDataGrid = (rect?.width ?? 0) >= dataGridMinWidth;
|
|
153
219
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
220
|
+
// Create list
|
|
221
|
+
const [list, showDataGrid] = (() => {
|
|
222
|
+
// No layout
|
|
223
|
+
if (rect == null) return [null, false];
|
|
224
|
+
|
|
225
|
+
// Width
|
|
226
|
+
const width = rect.width;
|
|
227
|
+
|
|
228
|
+
// Show DataGrid or List dependng on width
|
|
229
|
+
const showDataGrid = width >= dataGridMinWidth;
|
|
230
|
+
|
|
231
|
+
// Height
|
|
232
|
+
let heightLocal: number;
|
|
233
|
+
if (height != null) {
|
|
234
|
+
heightLocal = height;
|
|
235
|
+
} else {
|
|
236
|
+
// Auto calculation
|
|
237
|
+
heightLocal =
|
|
157
238
|
window.innerHeight - Math.round(rect.top + rect.height + 1);
|
|
158
239
|
|
|
159
240
|
const style = window.getComputedStyle(dimensions[0][1]!);
|
|
160
241
|
const boxPadding = parseFloat(style.paddingLeft);
|
|
161
|
-
if (!isNaN(boxPadding))
|
|
242
|
+
if (!isNaN(boxPadding)) heightLocal -= 2 * boxPadding;
|
|
162
243
|
|
|
163
244
|
if (adjustHeight != null) {
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (gridHeight !== refs.current.height) {
|
|
168
|
-
refs.current.height = gridHeight;
|
|
245
|
+
heightLocal -= adjustHeight(heightLocal);
|
|
169
246
|
}
|
|
170
247
|
}
|
|
171
|
-
}, [rect]);
|
|
172
|
-
|
|
173
|
-
const localLoadData = (props: GridLoadDataProps) => {
|
|
174
|
-
const data = GridDataGet(props, fieldTemplate);
|
|
175
|
-
return loadData(data);
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
const gridHeight = refs.current.height;
|
|
179
|
-
const list = React.useMemo(() => {
|
|
180
|
-
if (gridHeight == null) return;
|
|
181
248
|
|
|
182
249
|
if (showDataGrid) {
|
|
183
250
|
// Delete
|
|
184
251
|
delete rest.itemRenderer;
|
|
185
252
|
|
|
186
|
-
return
|
|
253
|
+
return [
|
|
187
254
|
<Box sx={listBoxSx == null ? undefined : listBoxSx(true)}>
|
|
188
255
|
<DataGridEx<T>
|
|
189
256
|
autoLoad={!hasFields}
|
|
190
|
-
height={
|
|
257
|
+
height={heightLocal}
|
|
258
|
+
width={rect.width}
|
|
191
259
|
loadData={localLoadData}
|
|
192
260
|
mRef={mRefs}
|
|
193
261
|
columns={columns}
|
|
194
262
|
{...rest}
|
|
195
263
|
/>
|
|
196
|
-
</Box
|
|
197
|
-
|
|
264
|
+
</Box>,
|
|
265
|
+
true
|
|
266
|
+
];
|
|
198
267
|
}
|
|
199
268
|
|
|
200
269
|
// Delete
|
|
@@ -207,39 +276,53 @@ export function ResponsibleContainer<
|
|
|
207
276
|
delete rest.hoverColor;
|
|
208
277
|
delete rest.selectable;
|
|
209
278
|
|
|
210
|
-
return
|
|
279
|
+
return [
|
|
211
280
|
<Box sx={listBoxSx == null ? undefined : listBoxSx(false)}>
|
|
212
281
|
<ScrollerListEx<T>
|
|
213
282
|
autoLoad={!hasFields}
|
|
214
|
-
|
|
283
|
+
width={rect.width}
|
|
284
|
+
height={heightLocal}
|
|
215
285
|
loadData={localLoadData}
|
|
216
286
|
mRef={mRefs}
|
|
217
287
|
{...rest}
|
|
218
288
|
/>
|
|
219
|
-
</Box
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
React.useEffect(() => {
|
|
231
|
-
console.log('useEffect', rect, refs.current.ref, showDataGrid);
|
|
232
|
-
}, []);
|
|
289
|
+
</Box>,
|
|
290
|
+
false
|
|
291
|
+
];
|
|
292
|
+
})();
|
|
293
|
+
|
|
294
|
+
// Pull container
|
|
295
|
+
const pullContainer = list
|
|
296
|
+
? showDataGrid
|
|
297
|
+
? '.DataGridEx-Body'
|
|
298
|
+
: '.ScrollerListEx-Body'
|
|
299
|
+
: undefined;
|
|
233
300
|
|
|
234
301
|
// Layout
|
|
235
302
|
return (
|
|
236
|
-
<
|
|
237
|
-
|
|
303
|
+
<React.Fragment>
|
|
304
|
+
<Stack>
|
|
238
305
|
<Box ref={dimensions[0][0]} sx={searchBoxSx}>
|
|
239
|
-
|
|
306
|
+
{hasFields && (
|
|
307
|
+
<SearchBar fields={fields} onSubmit={onSubmit} />
|
|
308
|
+
)}
|
|
240
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
|
+
/>
|
|
241
325
|
)}
|
|
242
|
-
|
|
243
|
-
</Stack>
|
|
326
|
+
</React.Fragment>
|
|
244
327
|
);
|
|
245
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[];
|