@onehat/ui 0.4.102 → 0.4.104
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/.github/copilot-instructions.md.bak.20260307094051 +65 -0
- package/package.json +1 -1
- package/src/Components/Accordion/Accordion.js +65 -6
- package/src/Components/Container/Container.js +10 -4
- package/src/Components/Form/Field/Combo/Combo.js +8 -2
- package/src/Components/Form/Form.js +17 -9
- package/src/Components/Grid/Grid.js +234 -154
- package/src/Components/Grid/GridRow.js +5 -1
- package/src/Components/Hoc/Secondary/withSecondaryEditor.js +18 -1
- package/src/Components/Hoc/withEditor.js +18 -1
- package/src/Components/Hoc/withPdfButtons.js +3 -0
- package/src/Components/Hoc/withPresetButtons.js +20 -6
- package/src/Components/Icons/ArrowsLeftRight.js +10 -0
- package/src/Components/Icons/Bar.js +10 -0
- package/src/Components/Icons/Box.js +11 -0
- package/src/Components/Icons/BoxOpen.js +11 -0
- package/src/Components/Icons/Bucket.js +10 -0
- package/src/Components/Icons/Bump.js +21 -0
- package/src/Components/Icons/Calculator.js +12 -0
- package/src/Components/Icons/Dots.js +20 -0
- package/src/Components/Icons/Fleets.js +26 -0
- package/src/Components/Icons/Microchip.js +12 -0
- package/src/Components/Icons/Num1.js +10 -0
- package/src/Components/Icons/Num2.js +10 -0
- package/src/Components/Icons/Num3.js +10 -0
- package/src/Components/Icons/Num4.js +10 -0
- package/src/Components/Icons/OilCan.js +11 -0
- package/src/Components/Icons/Operations.js +10 -0
- package/src/Components/Icons/OverduePms.js +10 -0
- package/src/Components/Icons/SackDollar.js +11 -0
- package/src/Components/Icons/ShortBar.js +15 -0
- package/src/Components/Icons/Tower.js +10 -0
- package/src/Components/Icons/UpcomingPms.js +10 -0
- package/src/Components/Layout/ScreenHeader.js +35 -3
- package/src/Components/Layout/SetupButton.js +31 -0
- package/src/Components/Layout/UserIndicator.js +35 -0
- package/src/Components/Panel/Panel.js +37 -9
- package/src/Components/Pms/Editor/BumpPmsEditor.js +9 -0
- package/src/Components/Pms/Editor/MetersEditor.js +173 -0
- package/src/Components/Pms/Editor/PmEventsEditor.js +291 -0
- package/src/Components/Pms/Grid/UpcomingPmsGrid.js +569 -0
- package/src/Components/Pms/Layout/TreeSpecific/MakeTreeSelection.js +11 -0
- package/src/Components/Pms/Layout/TreeSpecific/TreeSpecific.js +30 -0
- package/src/Components/Pms/Modals/BulkAssignTechnician.js +104 -0
- package/src/Components/Pms/Screens/PmsManager.js +136 -0
- package/src/Components/Pms/Window/BumpPmsEditorWindow.js +25 -0
- package/src/Components/Screens/Manager.js +5 -1
- package/src/Components/Toolbar/PaginationToolbar.js +5 -3
- package/src/Components/Tree/Tree.js +15 -6
- package/src/Components/Viewer/PmCalcDebugViewer.js +164 -146
- package/src/Components/Viewer/TextWithLinks.js +9 -1
- package/src/Components/Viewer/Viewer.js +43 -33
- package/src/Constants/PmSchedules.js +1 -0
- package/src/Functions/buildAdditionalButtons.js +5 -0
- package/src/Functions/flatten.js +39 -0
- package/src/Functions/verifyCanCrudPmEvents.js +33 -0
|
@@ -296,6 +296,11 @@ function GridComponent(props) {
|
|
|
296
296
|
footerToolbarRef = useRef(null),
|
|
297
297
|
rowRefs = useRef([]),
|
|
298
298
|
previousEntitiesLength = useRef(0),
|
|
299
|
+
paginationSelectionGuardRef = useRef({
|
|
300
|
+
source: null,
|
|
301
|
+
pending: new Set(),
|
|
302
|
+
expiresAt: 0,
|
|
303
|
+
}),
|
|
299
304
|
hasRemeasuredAfterRowsAppeared = useRef(false),
|
|
300
305
|
[isInited, setIsInited] = useState(false),
|
|
301
306
|
[isReady, setIsReady] = useState(false),
|
|
@@ -305,20 +310,6 @@ function GridComponent(props) {
|
|
|
305
310
|
showRowHandle = showSelectHandle || areRowsDragSource || (canRowsReorder && isReorderMode),
|
|
306
311
|
rowLongPressDelay = rowLongPressDelayMs ?? ((areRowsDragSource || canRowsReorder) ? 800 : undefined),
|
|
307
312
|
[lastMeasuredContainerHeight, setLastMeasuredContainerHeight] = useState(0),
|
|
308
|
-
getMeasurementPhase = () => {
|
|
309
|
-
return measurementPhaseRaw.current;
|
|
310
|
-
},
|
|
311
|
-
setMeasurementPhase = (phase) => {
|
|
312
|
-
measurementPhaseRaw.current = phase;
|
|
313
|
-
forceUpdate();
|
|
314
|
-
},
|
|
315
|
-
getMeasuredRowHeight = () => {
|
|
316
|
-
return measuredRowHeightRaw.current;
|
|
317
|
-
},
|
|
318
|
-
setMeasuredRowHeight = (height) => {
|
|
319
|
-
measuredRowHeightRaw.current = height;
|
|
320
|
-
forceUpdate();
|
|
321
|
-
},
|
|
322
313
|
getIsExpanded = (index) => {
|
|
323
314
|
return !!expandedRowsRef.current[index];
|
|
324
315
|
},
|
|
@@ -427,13 +418,15 @@ function GridComponent(props) {
|
|
|
427
418
|
}
|
|
428
419
|
},
|
|
429
420
|
getFooterToolbarItems = () => {
|
|
430
|
-
// Process additionalToolbarButtons to evaluate
|
|
421
|
+
// Process additionalToolbarButtons to evaluate functions
|
|
431
422
|
const processedButtons = _.map(additionalToolbarButtons, (config) => {
|
|
432
423
|
const processedConfig = { ...config };
|
|
433
|
-
// If the button has an getIsButtonDisabled function, evaluate it with current selection
|
|
434
424
|
if (_.isFunction(config.getIsButtonDisabled)) {
|
|
435
425
|
processedConfig.isDisabled = config.getIsButtonDisabled(selection);
|
|
436
426
|
}
|
|
427
|
+
if (_.isFunction(config.getText)) {
|
|
428
|
+
processedConfig.text = config.getText(selection);
|
|
429
|
+
}
|
|
437
430
|
return processedConfig;
|
|
438
431
|
});
|
|
439
432
|
const items = _.map(processedButtons, (config, ix) => getIconButtonFromConfig(config, ix, self));
|
|
@@ -596,6 +589,8 @@ function GridComponent(props) {
|
|
|
596
589
|
'flex-row',
|
|
597
590
|
'grow',
|
|
598
591
|
'max-h-[80px]',
|
|
592
|
+
'focus:outline-none', // hide the focus outline
|
|
593
|
+
'focus-visible:outline-none',
|
|
599
594
|
)}
|
|
600
595
|
>
|
|
601
596
|
{({
|
|
@@ -1018,6 +1013,151 @@ function GridComponent(props) {
|
|
|
1018
1013
|
marker.remove();
|
|
1019
1014
|
cachedDragElements.current = null;
|
|
1020
1015
|
},
|
|
1016
|
+
applySelectorSelected = () => {
|
|
1017
|
+
if (disableSelectorSelected || !selectorId) {
|
|
1018
|
+
return
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
if (previousSelectorId.current && selectorId !== previousSelectorId.current) {
|
|
1022
|
+
Repository.pauseEvents();
|
|
1023
|
+
Repository.clearFilters(previousSelectorId.current);
|
|
1024
|
+
Repository.resumeEvents();
|
|
1025
|
+
}
|
|
1026
|
+
previousSelectorId.current = selectorId;
|
|
1027
|
+
|
|
1028
|
+
let value = null;
|
|
1029
|
+
if (selectorSelected) {
|
|
1030
|
+
value = selectorSelected[selectorSelectedField];
|
|
1031
|
+
}
|
|
1032
|
+
if (noSelectorMeansNoResults && _.isEmpty(selectorSelected)) {
|
|
1033
|
+
value = 'NO_MATCHES';
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
Repository.filter(selectorId, value, false); // false so it doesn't clear existing filters
|
|
1037
|
+
},
|
|
1038
|
+
onGridKeyDown = (e) => {
|
|
1039
|
+
if (isInlineEditorShown) {
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
if (disableWithSelection) {
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
const {
|
|
1046
|
+
shiftKey = false,
|
|
1047
|
+
} = e;
|
|
1048
|
+
if (selectionMode === SELECTION_MODE_MULTI && shiftKey) {
|
|
1049
|
+
switch(e.key) {
|
|
1050
|
+
case 'ArrowDown':
|
|
1051
|
+
e.preventDefault();
|
|
1052
|
+
addNextToSelection();
|
|
1053
|
+
break;
|
|
1054
|
+
case 'ArrowUp':
|
|
1055
|
+
e.preventDefault();
|
|
1056
|
+
addPrevToSelection();
|
|
1057
|
+
break;
|
|
1058
|
+
}
|
|
1059
|
+
} else {
|
|
1060
|
+
// selectionMode is SELECTION_MODE_SINGLE
|
|
1061
|
+
switch(e.key) {
|
|
1062
|
+
case 'Enter':
|
|
1063
|
+
// NOTE: This is never being reached.
|
|
1064
|
+
// The event is getting captured somwhere else,
|
|
1065
|
+
// but I can't find where.
|
|
1066
|
+
// e.preventDefault();
|
|
1067
|
+
|
|
1068
|
+
// launch inline or windowed editor
|
|
1069
|
+
// const p = props;
|
|
1070
|
+
// debugger;
|
|
1071
|
+
break;
|
|
1072
|
+
case 'ArrowDown':
|
|
1073
|
+
e.preventDefault();
|
|
1074
|
+
selectNext();
|
|
1075
|
+
break;
|
|
1076
|
+
case 'ArrowUp':
|
|
1077
|
+
e.preventDefault();
|
|
1078
|
+
selectPrev();
|
|
1079
|
+
break;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
},
|
|
1083
|
+
showColumnsSelector = () => {
|
|
1084
|
+
const
|
|
1085
|
+
modalItems = _.map(localColumnsConfig, (config, ix) => {
|
|
1086
|
+
return {
|
|
1087
|
+
name: config.id,
|
|
1088
|
+
label: config.header,
|
|
1089
|
+
type: config.isHidable ? 'Checkbox' : 'Text',
|
|
1090
|
+
isEditable: config.isHidable ?? false,
|
|
1091
|
+
};
|
|
1092
|
+
}),
|
|
1093
|
+
startingValues = (() => {
|
|
1094
|
+
const startingValues = {};
|
|
1095
|
+
_.each(localColumnsConfig, (config) => {
|
|
1096
|
+
const value = !config.isHidden; // checkbox implies to show it, so flip the polarity
|
|
1097
|
+
startingValues[config.id] = config.isHidable ? value : 'Always shown';
|
|
1098
|
+
});
|
|
1099
|
+
return startingValues;
|
|
1100
|
+
})();
|
|
1101
|
+
|
|
1102
|
+
showModal({
|
|
1103
|
+
title: 'Column Selector',
|
|
1104
|
+
includeReset: true,
|
|
1105
|
+
includeCancel: true,
|
|
1106
|
+
h: 800,
|
|
1107
|
+
w: styles.FORM_STACK_ROW_THRESHOLD + 10,
|
|
1108
|
+
body: <Form
|
|
1109
|
+
editorType={EDITOR_TYPE__PLAIN}
|
|
1110
|
+
columnDefaults={{
|
|
1111
|
+
labelWidth: '250px',
|
|
1112
|
+
}}
|
|
1113
|
+
items={[
|
|
1114
|
+
{
|
|
1115
|
+
name: 'instructions',
|
|
1116
|
+
type: 'DisplayField',
|
|
1117
|
+
text: 'Please select which columns to show in the grid.',
|
|
1118
|
+
className: 'mb-3',
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
type: 'FieldSet',
|
|
1122
|
+
title: 'Columns',
|
|
1123
|
+
reference: 'columns',
|
|
1124
|
+
showToggleAllCheckbox: true,
|
|
1125
|
+
items: [
|
|
1126
|
+
...modalItems,
|
|
1127
|
+
],
|
|
1128
|
+
}
|
|
1129
|
+
]}
|
|
1130
|
+
startingValues={startingValues}
|
|
1131
|
+
onSave={(values)=> {
|
|
1132
|
+
hideModal();
|
|
1133
|
+
|
|
1134
|
+
const newColumnsConfig = _.cloneDeep(localColumnsConfig);
|
|
1135
|
+
_.each(newColumnsConfig, (config, ix) => {
|
|
1136
|
+
if (config.isHidable) {
|
|
1137
|
+
newColumnsConfig[ix].isHidden = !values[config.id]; // checkbox implies to show it, so flip the polarity
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
1140
|
+
setLocalColumnsConfig(newColumnsConfig);
|
|
1141
|
+
}}
|
|
1142
|
+
/>,
|
|
1143
|
+
});
|
|
1144
|
+
},
|
|
1145
|
+
|
|
1146
|
+
// These methods relate to auto-pageSize measurement and adjustment:
|
|
1147
|
+
getMeasurementPhase = () => {
|
|
1148
|
+
return measurementPhaseRaw.current;
|
|
1149
|
+
},
|
|
1150
|
+
setMeasurementPhase = (phase) => {
|
|
1151
|
+
measurementPhaseRaw.current = phase;
|
|
1152
|
+
forceUpdate();
|
|
1153
|
+
},
|
|
1154
|
+
getMeasuredRowHeight = () => {
|
|
1155
|
+
return measuredRowHeightRaw.current;
|
|
1156
|
+
},
|
|
1157
|
+
setMeasuredRowHeight = (height) => {
|
|
1158
|
+
measuredRowHeightRaw.current = height;
|
|
1159
|
+
forceUpdate();
|
|
1160
|
+
},
|
|
1021
1161
|
calculatePageSize = (containerHeight, useActualMeasurements = false) => {
|
|
1022
1162
|
if (DEBUG) {
|
|
1023
1163
|
console.log(`${getMeasurementPhase()}, calculatePageSize A containerHeight=${containerHeight}, useActualMeasurements=${useActualMeasurements}, measuredRowHeight=${getMeasuredRowHeight()}`);
|
|
@@ -1202,6 +1342,7 @@ function GridComponent(props) {
|
|
|
1202
1342
|
if (DEBUG) {
|
|
1203
1343
|
console.log(`${getMeasurementPhase()}, applyMeasuredRowHeight B Repository.setPageSize(${newPageSize})`);
|
|
1204
1344
|
}
|
|
1345
|
+
startAutoPageSizeSelectionGuard();
|
|
1205
1346
|
Repository.setPageSize(newPageSize);
|
|
1206
1347
|
}
|
|
1207
1348
|
}
|
|
@@ -1261,6 +1402,7 @@ function GridComponent(props) {
|
|
|
1261
1402
|
if (DEBUG) {
|
|
1262
1403
|
console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight D Repository.setPageSize(${pageSize})`);
|
|
1263
1404
|
}
|
|
1405
|
+
startAutoPageSizeSelectionGuard();
|
|
1264
1406
|
Repository.setPageSize(pageSize);
|
|
1265
1407
|
}
|
|
1266
1408
|
}
|
|
@@ -1279,136 +1421,51 @@ function GridComponent(props) {
|
|
|
1279
1421
|
}
|
|
1280
1422
|
},
|
|
1281
1423
|
debouncedAdjustPageSizeToHeight = useCallback(_.debounce(adjustPageSizeToHeight, 200), []),
|
|
1282
|
-
applySelectorSelected = () => {
|
|
1283
|
-
if (disableSelectorSelected || !selectorId) {
|
|
1284
|
-
return
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
if (previousSelectorId.current && selectorId !== previousSelectorId.current) {
|
|
1288
|
-
Repository.pauseEvents();
|
|
1289
|
-
Repository.clearFilters(previousSelectorId.current);
|
|
1290
|
-
Repository.resumeEvents();
|
|
1291
|
-
}
|
|
1292
|
-
previousSelectorId.current = selectorId;
|
|
1293
1424
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1425
|
+
// These methods guard for selection/pagination interaction
|
|
1426
|
+
// (If user makes a selection during the auto-pageSize adjustment,
|
|
1427
|
+
// we don't want to clear the selection if the current selection
|
|
1428
|
+
// would still be valid with the new page size):
|
|
1429
|
+
clearPaginationSelectionGuard = () => {
|
|
1430
|
+
paginationSelectionGuardRef.current = {
|
|
1431
|
+
source: null,
|
|
1432
|
+
pending: new Set(),
|
|
1433
|
+
expiresAt: 0,
|
|
1434
|
+
};
|
|
1303
1435
|
},
|
|
1304
|
-
|
|
1305
|
-
if (
|
|
1436
|
+
startAutoPageSizeSelectionGuard = () => {
|
|
1437
|
+
if (disableWithSelection || !deselectAll) {
|
|
1306
1438
|
return;
|
|
1307
1439
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1440
|
+
paginationSelectionGuardRef.current = {
|
|
1441
|
+
source: 'autoPageSize',
|
|
1442
|
+
pending: new Set(['changePage', 'changePageSize',]),
|
|
1443
|
+
expiresAt: Date.now() + 5000,
|
|
1444
|
+
};
|
|
1445
|
+
},
|
|
1446
|
+
consumePaginationSelectionGuard = (eventType) => {
|
|
1447
|
+
const guard = paginationSelectionGuardRef.current;
|
|
1448
|
+
if (guard.source !== 'autoPageSize') {
|
|
1449
|
+
return false;
|
|
1310
1450
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
e.preventDefault();
|
|
1318
|
-
addNextToSelection();
|
|
1319
|
-
break;
|
|
1320
|
-
case 'ArrowUp':
|
|
1321
|
-
e.preventDefault();
|
|
1322
|
-
addPrevToSelection();
|
|
1323
|
-
break;
|
|
1324
|
-
}
|
|
1325
|
-
} else {
|
|
1326
|
-
// selectionMode is SELECTION_MODE_SINGLE
|
|
1327
|
-
switch(e.key) {
|
|
1328
|
-
case 'Enter':
|
|
1329
|
-
// NOTE: This is never being reached.
|
|
1330
|
-
// The event is getting captured somwhere else,
|
|
1331
|
-
// but I can't find where.
|
|
1332
|
-
// e.preventDefault();
|
|
1333
|
-
|
|
1334
|
-
// launch inline or windowed editor
|
|
1335
|
-
// const p = props;
|
|
1336
|
-
// debugger;
|
|
1337
|
-
break;
|
|
1338
|
-
case 'ArrowDown':
|
|
1339
|
-
e.preventDefault();
|
|
1340
|
-
selectNext();
|
|
1341
|
-
break;
|
|
1342
|
-
case 'ArrowUp':
|
|
1343
|
-
e.preventDefault();
|
|
1344
|
-
selectPrev();
|
|
1345
|
-
break;
|
|
1346
|
-
}
|
|
1451
|
+
if (Date.now() > guard.expiresAt) {
|
|
1452
|
+
clearPaginationSelectionGuard();
|
|
1453
|
+
return false;
|
|
1454
|
+
}
|
|
1455
|
+
if (!guard.pending.has(eventType)) {
|
|
1456
|
+
return false;
|
|
1347
1457
|
}
|
|
1348
|
-
},
|
|
1349
|
-
showColumnsSelector = () => {
|
|
1350
|
-
const
|
|
1351
|
-
modalItems = _.map(localColumnsConfig, (config, ix) => {
|
|
1352
|
-
return {
|
|
1353
|
-
name: config.id,
|
|
1354
|
-
label: config.header,
|
|
1355
|
-
type: config.isHidable ? 'Checkbox' : 'Text',
|
|
1356
|
-
isEditable: config.isHidable ?? false,
|
|
1357
|
-
};
|
|
1358
|
-
}),
|
|
1359
|
-
startingValues = (() => {
|
|
1360
|
-
const startingValues = {};
|
|
1361
|
-
_.each(localColumnsConfig, (config) => {
|
|
1362
|
-
const value = !config.isHidden; // checkbox implies to show it, so flip the polarity
|
|
1363
|
-
startingValues[config.id] = config.isHidable ? value : 'Always shown';
|
|
1364
|
-
});
|
|
1365
|
-
return startingValues;
|
|
1366
|
-
})();
|
|
1367
|
-
|
|
1368
|
-
showModal({
|
|
1369
|
-
title: 'Column Selector',
|
|
1370
|
-
includeReset: true,
|
|
1371
|
-
includeCancel: true,
|
|
1372
|
-
h: 800,
|
|
1373
|
-
w: styles.FORM_STACK_ROW_THRESHOLD + 10,
|
|
1374
|
-
body: <Form
|
|
1375
|
-
editorType={EDITOR_TYPE__PLAIN}
|
|
1376
|
-
columnDefaults={{
|
|
1377
|
-
labelWidth: '250px',
|
|
1378
|
-
}}
|
|
1379
|
-
items={[
|
|
1380
|
-
{
|
|
1381
|
-
name: 'instructions',
|
|
1382
|
-
type: 'DisplayField',
|
|
1383
|
-
text: 'Please select which columns to show in the grid.',
|
|
1384
|
-
className: 'mb-3',
|
|
1385
|
-
},
|
|
1386
|
-
{
|
|
1387
|
-
type: 'FieldSet',
|
|
1388
|
-
title: 'Columns',
|
|
1389
|
-
reference: 'columns',
|
|
1390
|
-
showToggleAllCheckbox: true,
|
|
1391
|
-
items: [
|
|
1392
|
-
...modalItems,
|
|
1393
|
-
],
|
|
1394
|
-
}
|
|
1395
|
-
]}
|
|
1396
|
-
startingValues={startingValues}
|
|
1397
|
-
onSave={(values)=> {
|
|
1398
|
-
hideModal();
|
|
1399
1458
|
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
}}
|
|
1408
|
-
/>,
|
|
1409
|
-
});
|
|
1459
|
+
guard.pending.delete(eventType);
|
|
1460
|
+
|
|
1461
|
+
if (!guard.pending.size) {
|
|
1462
|
+
clearPaginationSelectionGuard();
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
return true;
|
|
1410
1466
|
};
|
|
1411
1467
|
|
|
1468
|
+
|
|
1412
1469
|
if (forceLoadOnRender && disableLoadOnRender) {
|
|
1413
1470
|
throw new Error('incompatible config! forceLoadOnRender and disableLoadOnRender cannot both be true');
|
|
1414
1471
|
}
|
|
@@ -1531,7 +1588,27 @@ function GridComponent(props) {
|
|
|
1531
1588
|
// set up @onehat/data repository
|
|
1532
1589
|
const
|
|
1533
1590
|
setTrue = () => setIsLoading(true),
|
|
1534
|
-
setFalse = () =>
|
|
1591
|
+
setFalse = () => {
|
|
1592
|
+
setIsLoading(false);
|
|
1593
|
+
clearPaginationSelectionGuard();
|
|
1594
|
+
},
|
|
1595
|
+
onPaginationEvent = (eventType) => {
|
|
1596
|
+
if (consumePaginationSelectionGuard(eventType)) {
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
if (!disableWithSelection && deselectAll) {
|
|
1600
|
+
deselectAll();
|
|
1601
|
+
}
|
|
1602
|
+
if (eventType === 'changePage' && showRowExpander) {
|
|
1603
|
+
expandedRowsRef.current = {}; // clear expanded rows
|
|
1604
|
+
}
|
|
1605
|
+
},
|
|
1606
|
+
onChangePageSize = () => {
|
|
1607
|
+
if (disableWithSelection || !deselectAll) {
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
onPaginationEvent('changePageSize');
|
|
1611
|
+
},
|
|
1535
1612
|
onChangeFilters = () => {
|
|
1536
1613
|
if (DEBUG) {
|
|
1537
1614
|
console.log('onChangeFilters, reload and re-measure');
|
|
@@ -1546,20 +1623,16 @@ function GridComponent(props) {
|
|
|
1546
1623
|
}
|
|
1547
1624
|
},
|
|
1548
1625
|
onChangePage = () => {
|
|
1549
|
-
|
|
1550
|
-
expandedRowsRef.current = {}; // clear expanded rows
|
|
1551
|
-
}
|
|
1626
|
+
onPaginationEvent('changePage');
|
|
1552
1627
|
};
|
|
1553
1628
|
|
|
1554
1629
|
Repository.on('beforeLoad', setTrue);
|
|
1555
1630
|
Repository.on('load', setFalse);
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
}
|
|
1631
|
+
Repository.on('changePage', onChangePage);
|
|
1632
|
+
Repository.on('changePageSize', onChangePageSize);
|
|
1559
1633
|
Repository.ons(['changeData', 'change'], forceUpdate);
|
|
1560
1634
|
Repository.on('changeFilters', onChangeFilters);
|
|
1561
1635
|
Repository.on('changeSorters', onChangeSorters);
|
|
1562
|
-
Repository.on('changePage', onChangePage);
|
|
1563
1636
|
|
|
1564
1637
|
applySelectorSelected();
|
|
1565
1638
|
|
|
@@ -1578,13 +1651,11 @@ function GridComponent(props) {
|
|
|
1578
1651
|
return () => {
|
|
1579
1652
|
Repository.off('beforeLoad', setTrue);
|
|
1580
1653
|
Repository.off('load', setFalse);
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
}
|
|
1654
|
+
Repository.off('changePage', onChangePage);
|
|
1655
|
+
Repository.off('changePageSize', onChangePageSize);
|
|
1584
1656
|
Repository.offs(['changeData', 'change'], forceUpdate);
|
|
1585
1657
|
Repository.off('changeFilters', onChangeFilters);
|
|
1586
1658
|
Repository.off('changeSorters', onChangeSorters);
|
|
1587
|
-
Repository.off('changePage', onChangePage);
|
|
1588
1659
|
};
|
|
1589
1660
|
}, [isInited]);
|
|
1590
1661
|
|
|
@@ -1600,8 +1671,8 @@ function GridComponent(props) {
|
|
|
1600
1671
|
|
|
1601
1672
|
}, [selectorId, selectorSelected]);
|
|
1602
1673
|
|
|
1603
|
-
// Effect to trigger row height measurement after render
|
|
1604
1674
|
useEffect(() => {
|
|
1675
|
+
// trigger row height measurement after render
|
|
1605
1676
|
if (getMeasurementPhase() === PHASES__MEASURING) {
|
|
1606
1677
|
// Small delay to ensure elements are fully rendered
|
|
1607
1678
|
const timer = setTimeout(async () => {
|
|
@@ -1637,13 +1708,13 @@ function GridComponent(props) {
|
|
|
1637
1708
|
}
|
|
1638
1709
|
}, [autoAdjustPageSizeToHeight]);
|
|
1639
1710
|
|
|
1640
|
-
// Reset measurement when rows were first empty then became populated
|
|
1641
1711
|
useEffect(() => {
|
|
1712
|
+
// Reset measurement when rows were first empty then became populated
|
|
1642
1713
|
const
|
|
1643
1714
|
currentLength = entities?.length || 0,
|
|
1644
1715
|
wasEmpty = previousEntitiesLength.current === 0,
|
|
1645
1716
|
isNowPopulated = currentLength > 0,
|
|
1646
|
-
hasPhantomRecord = entities?.some(entity => entity?.isPhantom);
|
|
1717
|
+
hasPhantomRecord = entities?.some(entity => !entity.isDestroyed && entity?.isPhantom);
|
|
1647
1718
|
|
|
1648
1719
|
// NOTE: The Repository was reloading when a phantom record was added,
|
|
1649
1720
|
// and this broke the Editor because selection was being reset to zero.
|
|
@@ -1674,6 +1745,15 @@ function GridComponent(props) {
|
|
|
1674
1745
|
previousEntitiesLength.current = currentLength;
|
|
1675
1746
|
}, [entities?.length, autoAdjustPageSizeToHeight]);
|
|
1676
1747
|
|
|
1748
|
+
|
|
1749
|
+
// Memoize footer toolbar items to avoid unnecessary re-renders, but only if they don't have dynamic properties
|
|
1750
|
+
const
|
|
1751
|
+
hasDynamicFooterToolbarItems = useMemo(() => _.some(additionalToolbarButtons, (config) => {
|
|
1752
|
+
return _.isFunction(config.getIsButtonDisabled) || _.isFunction(config.getText);
|
|
1753
|
+
}), [additionalToolbarButtons]),
|
|
1754
|
+
memoizedFooterToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [Repository?.hash, additionalToolbarButtons, isReorderMode]),
|
|
1755
|
+
footerToolbarItemComponents = hasDynamicFooterToolbarItems ? getFooterToolbarItems() : memoizedFooterToolbarItemComponents;
|
|
1756
|
+
|
|
1677
1757
|
if (canUser && !canUser('view')) {
|
|
1678
1758
|
return <Unauthorized />;
|
|
1679
1759
|
}
|
|
@@ -1685,8 +1765,6 @@ function GridComponent(props) {
|
|
|
1685
1765
|
|
|
1686
1766
|
isAddingRaw.current = isAdding;
|
|
1687
1767
|
|
|
1688
|
-
const footerToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [Repository?.hash, additionalToolbarButtons, isReorderMode]);
|
|
1689
|
-
|
|
1690
1768
|
if (!isInited) {
|
|
1691
1769
|
// first time through, render a placeholder so we can get container dimensions
|
|
1692
1770
|
return <VStackNative
|
|
@@ -1838,6 +1916,8 @@ function GridComponent(props) {
|
|
|
1838
1916
|
'w-full',
|
|
1839
1917
|
'border',
|
|
1840
1918
|
'border-grey-300',
|
|
1919
|
+
'focus:outline-none', // hide the focus outline
|
|
1920
|
+
'focus-visible:outline-none',
|
|
1841
1921
|
);
|
|
1842
1922
|
if (props.className) {
|
|
1843
1923
|
className += ' ' + props.className;
|
|
@@ -490,7 +490,11 @@ const GridRow = forwardRef((props, ref) => {
|
|
|
490
490
|
}}
|
|
491
491
|
>{rowContents}</HStackNative>;
|
|
492
492
|
if (rowProps.tooltip) {
|
|
493
|
-
row = <Tooltip
|
|
493
|
+
row = <Tooltip
|
|
494
|
+
label={rowProps.tooltip}
|
|
495
|
+
placement="bottom left"
|
|
496
|
+
triggerClassName={rowClassName}
|
|
497
|
+
>{row}</Tooltip>;
|
|
494
498
|
}
|
|
495
499
|
return row;
|
|
496
500
|
}, [
|
|
@@ -47,6 +47,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
47
47
|
},
|
|
48
48
|
secondaryEditorType,
|
|
49
49
|
secondaryOnAdd,
|
|
50
|
+
secondaryOnBeforeAdd,
|
|
50
51
|
secondaryOnChange, // any kind of crud change
|
|
51
52
|
secondaryOnBeforeDelete,
|
|
52
53
|
secondaryOnDelete,
|
|
@@ -201,10 +202,26 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
if (getListeners().onBeforeAdd) {
|
|
204
|
-
|
|
205
|
+
// This listener is set by child components using setWithEditListeners()
|
|
206
|
+
const listenerResult = await getListeners().onBeforeAdd(addValues);
|
|
207
|
+
if (listenerResult === false) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (listenerResult) {
|
|
211
|
+
// allow the listener to override the addValues by returning an object
|
|
212
|
+
addValues = listenerResult;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (secondaryOnBeforeAdd) {
|
|
216
|
+
// This listener is set by parent components using a prop
|
|
217
|
+
const listenerResult = await secondaryOnBeforeAdd(addValues);
|
|
205
218
|
if (listenerResult === false) {
|
|
206
219
|
return;
|
|
207
220
|
}
|
|
221
|
+
if (listenerResult) {
|
|
222
|
+
// allow the listener to override the addValues by returning an object
|
|
223
|
+
addValues = listenerResult;
|
|
224
|
+
}
|
|
208
225
|
}
|
|
209
226
|
|
|
210
227
|
if (isTree) {
|
|
@@ -47,6 +47,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
47
47
|
},
|
|
48
48
|
editorType,
|
|
49
49
|
onAdd,
|
|
50
|
+
onBeforeAdd,
|
|
50
51
|
onChange, // any kind of crud change
|
|
51
52
|
onBeforeDelete,
|
|
52
53
|
onDelete,
|
|
@@ -218,10 +219,26 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
218
219
|
}
|
|
219
220
|
|
|
220
221
|
if (getListeners().onBeforeAdd) {
|
|
221
|
-
|
|
222
|
+
// This listener is set by child components using setWithEditListeners()
|
|
223
|
+
const listenerResult = await getListeners().onBeforeAdd(addValues);
|
|
224
|
+
if (listenerResult === false) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (listenerResult) {
|
|
228
|
+
// allow the listener to override the addValues by returning an object
|
|
229
|
+
addValues = listenerResult;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (onBeforeAdd) {
|
|
233
|
+
// This listener is set by parent components using a prop
|
|
234
|
+
const listenerResult = await onBeforeAdd(addValues);
|
|
222
235
|
if (listenerResult === false) {
|
|
223
236
|
return;
|
|
224
237
|
}
|
|
238
|
+
if (listenerResult) {
|
|
239
|
+
// allow the listener to override the addValues by returning an object
|
|
240
|
+
addValues = listenerResult;
|
|
241
|
+
}
|
|
225
242
|
}
|
|
226
243
|
|
|
227
244
|
if (isTree) {
|
|
@@ -418,6 +418,9 @@ export default function withPdfButtons(WrappedComponent) {
|
|
|
418
418
|
},
|
|
419
419
|
];
|
|
420
420
|
_.each(buttons, (button) => {
|
|
421
|
+
if (!button) {
|
|
422
|
+
return; // guard against null/undefined
|
|
423
|
+
}
|
|
421
424
|
if (!_.find(additionalEditButtons, btn => button.key === btn.key)) {
|
|
422
425
|
additionalEditButtons.push(button);
|
|
423
426
|
}
|
|
@@ -39,7 +39,7 @@ const presetButtons = [
|
|
|
39
39
|
DOWNLOAD,
|
|
40
40
|
];
|
|
41
41
|
|
|
42
|
-
export default function withPresetButtons(WrappedComponent
|
|
42
|
+
export default function withPresetButtons(WrappedComponent) {
|
|
43
43
|
return forwardRef((props, ref) => {
|
|
44
44
|
|
|
45
45
|
if (props.disablePresetButtons || props.alreadyHasWithPresetButtons) {
|
|
@@ -64,20 +64,21 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
64
64
|
{
|
|
65
65
|
// extract and pass down
|
|
66
66
|
isEditor = false,
|
|
67
|
-
isTree = false,
|
|
67
|
+
isTree = false, // from withWindowedEditor or withSideEditor in Tree
|
|
68
68
|
canDeleteRootNode = false,
|
|
69
69
|
isSideEditor = false,
|
|
70
70
|
canEditorViewOnly = false,
|
|
71
|
+
canRecordBeAdded, // fn(selection) returns bool on if the current record(s) can be added
|
|
71
72
|
canRecordBeEdited, // fn(selection) returns bool on if the current record(s) can be edited
|
|
72
73
|
canRecordBeDeleted, // fn(selection) returns bool on if the current record(s) can be deleted
|
|
73
74
|
canRecordBeDuplicated, // fn(selection) returns bool on if the current record(s) can be duplicated
|
|
74
75
|
disableAdd = !isEditor,
|
|
75
76
|
disableEdit = !isEditor,
|
|
76
77
|
disableDelete = !isEditor,
|
|
77
|
-
disableView =
|
|
78
|
-
disableCopy =
|
|
78
|
+
disableView = isTree,
|
|
79
|
+
disableCopy = isTree,
|
|
79
80
|
disableDuplicate = !isEditor,
|
|
80
|
-
disablePrint =
|
|
81
|
+
disablePrint = isTree,
|
|
81
82
|
protectedValues, // records with these values cannot be edited or deleted
|
|
82
83
|
addDisplayMsg,
|
|
83
84
|
editDisplayMsg,
|
|
@@ -239,6 +240,7 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
239
240
|
};
|
|
240
241
|
icon = Plus;
|
|
241
242
|
if (isNoSelectorSelected() ||
|
|
243
|
+
(canRecordBeAdded && !canRecordBeAdded(selection)) ||
|
|
242
244
|
(isTree && isEmptySelection())
|
|
243
245
|
) {
|
|
244
246
|
isDisabled = true;
|
|
@@ -434,6 +436,18 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
434
436
|
}
|
|
435
437
|
},
|
|
436
438
|
onUploadDownload = () => {
|
|
439
|
+
const onUploadDecorator = async () => {
|
|
440
|
+
if (onUpload) {
|
|
441
|
+
await onUpload();
|
|
442
|
+
}
|
|
443
|
+
if (Repository && !Repository.isDestroyed) {
|
|
444
|
+
if (Repository.loadRootNodes) {
|
|
445
|
+
await Repository.loadRootNodes(1);
|
|
446
|
+
} else {
|
|
447
|
+
await Repository.reload();
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
};
|
|
437
451
|
showModal({
|
|
438
452
|
body: <UploadsDownloadsWindow
|
|
439
453
|
reference="uploadsDownloads"
|
|
@@ -442,7 +456,7 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
|
|
|
442
456
|
columnsConfig={props.columnsConfig}
|
|
443
457
|
uploadHeaders={uploadHeaders}
|
|
444
458
|
uploadParams={uploadParams}
|
|
445
|
-
onUpload={
|
|
459
|
+
onUpload={onUploadDecorator}
|
|
446
460
|
downloadHeaders={downloadHeaders}
|
|
447
461
|
downloadParams={downloadParams}
|
|
448
462
|
/>,
|