@onehat/ui 0.4.104 → 0.4.106
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/package.json +1 -1
- package/src/Components/Form/Form.js +15 -2
- package/src/Components/Hoc/withEditor.js +234 -19
- package/src/Components/Pms/Editor/MetersEditor.js +5 -0
- package/src/Components/Viewer/Viewer.js +19 -1
- package/src/Contexts/EditorModeContext.js +5 -0
- package/src/PlatformImports/Web/Attachments.js +26 -0
- package/src/Constants/EditorModes.js +0 -2
package/package.json
CHANGED
|
@@ -139,6 +139,7 @@ function Form(props) {
|
|
|
139
139
|
onDelete,
|
|
140
140
|
editorStateRef,
|
|
141
141
|
disableView,
|
|
142
|
+
isEditorModeControlledByParent = false,
|
|
142
143
|
|
|
143
144
|
// parent container
|
|
144
145
|
selectorId,
|
|
@@ -968,6 +969,14 @@ function Form(props) {
|
|
|
968
969
|
buildAncillary = () => {
|
|
969
970
|
const
|
|
970
971
|
validAncillaryItems = _.filter(ancillaryItems, (item) => !!item), // filter out any null/undefined items
|
|
972
|
+
parentEditorModeRaw = getEditorMode?.() || props.editorMode || null,
|
|
973
|
+
parentEditorMode = parentEditorModeRaw === EDITOR_MODE__ADD
|
|
974
|
+
? EDITOR_MODE__EDIT
|
|
975
|
+
: parentEditorModeRaw,
|
|
976
|
+
normalizedParentEditorMode =
|
|
977
|
+
parentEditorMode === EDITOR_MODE__EDIT || parentEditorMode === EDITOR_MODE__VIEW
|
|
978
|
+
? parentEditorMode
|
|
979
|
+
: null,
|
|
971
980
|
components = [];
|
|
972
981
|
setAncillaryButtons([]);
|
|
973
982
|
if (validAncillaryItems.length) {
|
|
@@ -1010,6 +1019,8 @@ function Form(props) {
|
|
|
1010
1019
|
}
|
|
1011
1020
|
|
|
1012
1021
|
const
|
|
1022
|
+
ancillaryEditorMode = itemPropsToPass.editorMode ?? normalizedParentEditorMode,
|
|
1023
|
+
ancillaryInitialEditorMode = itemPropsToPass.initialEditorMode ?? ancillaryEditorMode ?? undefined,
|
|
1013
1024
|
Element = getComponentFromType(type),
|
|
1014
1025
|
element = <Element
|
|
1015
1026
|
{...testProps('ancillary-' + type)}
|
|
@@ -1019,6 +1030,8 @@ function Form(props) {
|
|
|
1019
1030
|
uniqueRepository={true}
|
|
1020
1031
|
parent={self}
|
|
1021
1032
|
{...itemPropsToPass}
|
|
1033
|
+
editorMode={ancillaryEditorMode}
|
|
1034
|
+
initialEditorMode={ancillaryInitialEditorMode}
|
|
1022
1035
|
/>;
|
|
1023
1036
|
if (title) {
|
|
1024
1037
|
if (record?.displayValue) {
|
|
@@ -1262,7 +1275,7 @@ function Form(props) {
|
|
|
1262
1275
|
|
|
1263
1276
|
if (inArray(editorType, [EDITOR_TYPE__SIDE, EDITOR_TYPE__SMART, EDITOR_TYPE__WINDOWED]) &&
|
|
1264
1277
|
isSingle && getEditorMode() === EDITOR_MODE__EDIT &&
|
|
1265
|
-
(onBack || onViewMode)) {
|
|
1278
|
+
(onBack || onViewMode || isEditorModeControlledByParent)) {
|
|
1266
1279
|
modeHeader = <Toolbar>
|
|
1267
1280
|
<HStack className="flex-1 items-center">
|
|
1268
1281
|
{onBack &&
|
|
@@ -1280,7 +1293,7 @@ function Form(props) {
|
|
|
1280
1293
|
)}
|
|
1281
1294
|
text="Back"
|
|
1282
1295
|
/>}
|
|
1283
|
-
<Text className="text-[20px] ml-1 text-grey-500">Edit Mode</Text>
|
|
1296
|
+
<Text className="text-[20px] ml-1 text-grey-500">{isEditorModeControlledByParent ? 'Edit Mode (Inherited)' : 'Edit Mode'}</Text>
|
|
1284
1297
|
</HStack>
|
|
1285
1298
|
{onViewMode && !disableView && (!canUser || canUser(VIEW)) &&
|
|
1286
1299
|
<Button
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { forwardRef, useEffect, useState, useRef, } from 'react';
|
|
1
|
+
import { forwardRef, useContext, useEffect, useState, useRef, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ADD,
|
|
4
4
|
EDIT,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from '../../Constants/Editor.js';
|
|
16
16
|
import useForceUpdate from '../../Hooks/useForceUpdate.js'
|
|
17
17
|
import Button from '../Buttons/Button.js';
|
|
18
|
+
import EditorModeContext from '../../Contexts/EditorModeContext.js';
|
|
18
19
|
import UiGlobals from '../../UiGlobals.js';
|
|
19
20
|
import _ from 'lodash';
|
|
20
21
|
|
|
@@ -56,8 +57,10 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
56
57
|
newEntityDisplayValue,
|
|
57
58
|
newEntityDisplayProperty, // in case the field to set for newEntityDisplayValue is different from model
|
|
58
59
|
defaultValues,
|
|
60
|
+
editorMode: parentEditorModeProp,
|
|
59
61
|
initialEditorMode = EDITOR_MODE__VIEW,
|
|
60
62
|
stayInEditModeOnSelectionChange = false,
|
|
63
|
+
inheritParentEditorMode = true,
|
|
61
64
|
|
|
62
65
|
// withComponent
|
|
63
66
|
self,
|
|
@@ -84,6 +87,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
84
87
|
confirm,
|
|
85
88
|
hideAlert,
|
|
86
89
|
} = props,
|
|
90
|
+
parentEditorModeContext = useContext(EditorModeContext),
|
|
87
91
|
forceUpdate = useForceUpdate(),
|
|
88
92
|
listeners = useRef({}),
|
|
89
93
|
editorStateRef = useRef(),
|
|
@@ -170,13 +174,99 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
170
174
|
forceUpdate();
|
|
171
175
|
}
|
|
172
176
|
},
|
|
177
|
+
getParentEditorMode = () => {
|
|
178
|
+
const contextMode = parentEditorModeContext?.effectiveEditorMode || null;
|
|
179
|
+
if (contextMode) {
|
|
180
|
+
return contextMode;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Some modal implementations break React context boundaries. Fall back to
|
|
184
|
+
// an explicitly-passed parent mode so nested ancillary editors still inherit.
|
|
185
|
+
if (parentEditorModeProp === EDITOR_MODE__ADD) {
|
|
186
|
+
return EDITOR_MODE__EDIT;
|
|
187
|
+
}
|
|
188
|
+
if (parentEditorModeProp === EDITOR_MODE__EDIT || parentEditorModeProp === EDITOR_MODE__VIEW) {
|
|
189
|
+
return parentEditorModeProp;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return null;
|
|
193
|
+
},
|
|
194
|
+
getInheritedEditorMode = () => {
|
|
195
|
+
if (!inheritParentEditorMode) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
const parentMode = getParentEditorMode();
|
|
199
|
+
if (parentMode === EDITOR_MODE__ADD) {
|
|
200
|
+
return EDITOR_MODE__EDIT;
|
|
201
|
+
}
|
|
202
|
+
if (parentMode === EDITOR_MODE__EDIT || parentMode === EDITOR_MODE__VIEW) {
|
|
203
|
+
return parentMode;
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
},
|
|
207
|
+
getIsParentSaveLocked = () => {
|
|
208
|
+
return !!parentEditorModeContext?.isAnyAncestorUnsaved;
|
|
209
|
+
},
|
|
210
|
+
getIsCurrentSelectionUnsaved = () => {
|
|
211
|
+
const selection = getSelection();
|
|
212
|
+
if (!selection || selection.length !== 1) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
const record = selection[0];
|
|
216
|
+
if (!record || record.isDestroyed) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
return !!(record.isPhantom || record.isRemotePhantom);
|
|
220
|
+
},
|
|
221
|
+
getIsEditorDisabledByParent = () => {
|
|
222
|
+
return getIsParentSaveLocked();
|
|
223
|
+
},
|
|
224
|
+
getIsEditorModeControlledByParent = () => {
|
|
225
|
+
return !!getInheritedEditorMode();
|
|
226
|
+
},
|
|
173
227
|
getNewEntityDisplayValue = () => {
|
|
174
228
|
return newEntityDisplayValueRef.current;
|
|
175
229
|
},
|
|
176
230
|
setNewEntityDisplayValue = (val) => {
|
|
177
231
|
newEntityDisplayValueRef.current = val;
|
|
178
232
|
},
|
|
233
|
+
showViewFallback = async () => {
|
|
234
|
+
// helper for doEdit
|
|
235
|
+
// If the editor is forced into EDIT mode by parent inheritance,
|
|
236
|
+
// but the child editor cannot actually be in edit mode due to permissions or configuration,
|
|
237
|
+
// switch the mode to VIEW.
|
|
238
|
+
|
|
239
|
+
if (!userCanView) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (canUser && !canUser(VIEW)) {
|
|
243
|
+
showPermissionsError(VIEW);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (canProceedWithCrud && !canProceedWithCrud()) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (editorType === EDITOR_TYPE__INLINE) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const selection = getSelection();
|
|
253
|
+
if (selection.length !== 1) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
setIsEditorViewOnly(true);
|
|
257
|
+
setEditorMode(EDITOR_MODE__VIEW);
|
|
258
|
+
setIsEditorShown(true);
|
|
259
|
+
if (getListeners().onAfterView) {
|
|
260
|
+
await getListeners().onAfterView();
|
|
261
|
+
}
|
|
262
|
+
},
|
|
179
263
|
doAdd = async (e, values) => {
|
|
264
|
+
if (getIsEditorDisabledByParent()) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (getInheritedEditorMode() === EDITOR_MODE__VIEW) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
180
270
|
if (canUser && !canUser(ADD)) {
|
|
181
271
|
showPermissionsError(ADD);
|
|
182
272
|
return;
|
|
@@ -295,7 +385,23 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
295
385
|
setIsEditorShown(true);
|
|
296
386
|
},
|
|
297
387
|
doEdit = async () => {
|
|
388
|
+
if (getIsEditorDisabledByParent()) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const inheritedEditorMode = getInheritedEditorMode();
|
|
392
|
+
if (inheritedEditorMode === EDITOR_MODE__VIEW) {
|
|
393
|
+
await doView(false);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (inheritedEditorMode === EDITOR_MODE__EDIT && (!userCanEdit || disableEdit || canEditorViewOnly)) {
|
|
397
|
+
await showViewFallback();
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
298
400
|
if (canUser && !canUser(EDIT)) {
|
|
401
|
+
if (inheritedEditorMode === EDITOR_MODE__EDIT) {
|
|
402
|
+
await showViewFallback();
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
299
405
|
showPermissionsError(EDIT);
|
|
300
406
|
return;
|
|
301
407
|
}
|
|
@@ -317,6 +423,12 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
317
423
|
setIsEditorShown(true);
|
|
318
424
|
},
|
|
319
425
|
doDelete = async (args) => {
|
|
426
|
+
if (getIsEditorDisabledByParent()) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (getInheritedEditorMode() === EDITOR_MODE__VIEW) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
320
432
|
if (canUser && !canUser(DELETE)) {
|
|
321
433
|
showPermissionsError(DELETE);
|
|
322
434
|
return;
|
|
@@ -430,6 +542,17 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
430
542
|
}
|
|
431
543
|
},
|
|
432
544
|
doView = async (allowEditing = false) => {
|
|
545
|
+
if (getIsEditorDisabledByParent()) {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
const inheritedEditorMode = getInheritedEditorMode();
|
|
549
|
+
if (inheritedEditorMode === EDITOR_MODE__EDIT) {
|
|
550
|
+
await doEdit();
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (inheritedEditorMode === EDITOR_MODE__VIEW) {
|
|
554
|
+
allowEditing = false;
|
|
555
|
+
}
|
|
433
556
|
if (!userCanView) {
|
|
434
557
|
return;
|
|
435
558
|
}
|
|
@@ -460,6 +583,12 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
460
583
|
}
|
|
461
584
|
},
|
|
462
585
|
doDuplicate = async () => {
|
|
586
|
+
if (getIsEditorDisabledByParent()) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
if (getInheritedEditorMode() === EDITOR_MODE__VIEW) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
463
592
|
if (!userCanEdit || disableDuplicate) {
|
|
464
593
|
return;
|
|
465
594
|
}
|
|
@@ -533,6 +662,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
533
662
|
}
|
|
534
663
|
},
|
|
535
664
|
doEditorSave = async (data, e) => {
|
|
665
|
+
if (getIsEditorDisabledByParent()) {
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
536
668
|
let mode = getEditorMode() === EDITOR_MODE__ADD ? ADD : EDIT;
|
|
537
669
|
if (canUser && !canUser(mode)) {
|
|
538
670
|
showPermissionsError(mode);
|
|
@@ -651,6 +783,12 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
651
783
|
setIsEditorShown(false);
|
|
652
784
|
},
|
|
653
785
|
doEditorDelete = async () => {
|
|
786
|
+
if (getIsEditorDisabledByParent()) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (getInheritedEditorMode() === EDITOR_MODE__VIEW) {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
654
792
|
if (canUser && !canUser(DELETE)) {
|
|
655
793
|
showPermissionsError(DELETE);
|
|
656
794
|
return;
|
|
@@ -662,6 +800,46 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
662
800
|
});
|
|
663
801
|
},
|
|
664
802
|
calculateEditorMode = () => {
|
|
803
|
+
// Calculate the editor's effective mode based on parent inheritance, permissions, and local selection state.
|
|
804
|
+
// Priority order:
|
|
805
|
+
// 1. If parent is save-locked (unsaved ancestor), force VIEW mode
|
|
806
|
+
// 2. If parent forces VIEW mode via inheritance, return VIEW (child cannot edit if parent is view-only)
|
|
807
|
+
// 3. If parent forces EDIT mode via inheritance, check child permissions:
|
|
808
|
+
// a. If parent disabled, child disabled, or child cannot edit, return VIEW
|
|
809
|
+
// b. If single phantom record, return ADD (new record being created)
|
|
810
|
+
// c. Otherwise return EDIT or VIEW based on selection count
|
|
811
|
+
// 4. Fall back to local selection heuristics (multiple→EDIT, single→VIEW, stays in previous mode if configured)
|
|
812
|
+
const
|
|
813
|
+
selection = getSelection(),
|
|
814
|
+
inheritedEditorMode = getInheritedEditorMode();
|
|
815
|
+
|
|
816
|
+
if (getIsEditorDisabledByParent()) {
|
|
817
|
+
return EDITOR_MODE__VIEW;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
if (inheritedEditorMode === EDITOR_MODE__VIEW) {
|
|
821
|
+
return EDITOR_MODE__VIEW;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
if (inheritedEditorMode === EDITOR_MODE__EDIT) {
|
|
825
|
+
if (!getCanEditorBeInEditMode()) {
|
|
826
|
+
return EDITOR_MODE__VIEW;
|
|
827
|
+
}
|
|
828
|
+
if (canEditorViewOnly || !userCanEdit || disableEdit) {
|
|
829
|
+
return EDITOR_MODE__VIEW;
|
|
830
|
+
}
|
|
831
|
+
if (canUser && !canUser(EDIT)) {
|
|
832
|
+
return EDITOR_MODE__VIEW;
|
|
833
|
+
}
|
|
834
|
+
if (canRecordBeEdited && canRecordBeEdited(selection) === false) {
|
|
835
|
+
return EDITOR_MODE__VIEW;
|
|
836
|
+
}
|
|
837
|
+
if (selection.length === 1 && !selection[0].isDestroyed && (selection[0].isPhantom || selection[0].isRemotePhantom) && !disableAdd) {
|
|
838
|
+
return EDITOR_MODE__ADD;
|
|
839
|
+
}
|
|
840
|
+
return selection.length ? EDITOR_MODE__EDIT : EDITOR_MODE__VIEW;
|
|
841
|
+
}
|
|
842
|
+
|
|
665
843
|
if (!getCanEditorBeInEditMode()) { // this is a result of canRecordBeEdited returning false
|
|
666
844
|
return EDITOR_MODE__VIEW;
|
|
667
845
|
}
|
|
@@ -677,7 +855,6 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
677
855
|
}
|
|
678
856
|
|
|
679
857
|
// calculateEditorMode gets called only on selection changes
|
|
680
|
-
const selection = getSelection();
|
|
681
858
|
let mode;
|
|
682
859
|
if (editorType === EDITOR_TYPE__SIDE && !_.isNil(UiGlobals.isSideEditorAlwaysEditMode) && UiGlobals.isSideEditorAlwaysEditMode) {
|
|
683
860
|
// special case: side editor is always edit mode
|
|
@@ -705,6 +882,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
705
882
|
return mode;
|
|
706
883
|
},
|
|
707
884
|
setEditMode = () => {
|
|
885
|
+
if (getIsEditorDisabledByParent() || getIsEditorModeControlledByParent()) {
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
708
888
|
if (canUser && !canUser(EDIT)) {
|
|
709
889
|
showPermissionsError(EDIT);
|
|
710
890
|
return;
|
|
@@ -713,6 +893,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
713
893
|
setEditorMode(EDITOR_MODE__EDIT);
|
|
714
894
|
},
|
|
715
895
|
setViewMode = () => {
|
|
896
|
+
if (getIsEditorDisabledByParent() || getIsEditorModeControlledByParent()) {
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
716
899
|
if (canUser && !canUser(VIEW)) {
|
|
717
900
|
showPermissionsError(VIEW);
|
|
718
901
|
return;
|
|
@@ -729,10 +912,20 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
729
912
|
}
|
|
730
913
|
};
|
|
731
914
|
|
|
915
|
+
const
|
|
916
|
+
inheritedEditorMode = getInheritedEditorMode(),
|
|
917
|
+
isEditorDisabledByParent = getIsEditorDisabledByParent(),
|
|
918
|
+
isEditorModeControlledByParent = getIsEditorModeControlledByParent(),
|
|
919
|
+
isCurrentSelectionUnsaved = getIsCurrentSelectionUnsaved(),
|
|
920
|
+
isAnyAncestorUnsaved = getIsParentSaveLocked() || isCurrentSelectionUnsaved,
|
|
921
|
+
isCrudBlockedByInheritedView = inheritedEditorMode === EDITOR_MODE__VIEW;
|
|
922
|
+
|
|
732
923
|
useEffect(() => {
|
|
733
924
|
|
|
734
925
|
if (editorType === EDITOR_TYPE__SIDE) {
|
|
735
|
-
if (
|
|
926
|
+
if (isEditorDisabledByParent) {
|
|
927
|
+
setIsEditorShown(false);
|
|
928
|
+
} else if (selection?.length) { // || isAdding
|
|
736
929
|
// there is a selection, so show the editor
|
|
737
930
|
setIsEditorShown(true);
|
|
738
931
|
} else {
|
|
@@ -746,6 +939,19 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
746
939
|
} else {
|
|
747
940
|
setCanEditorBeInEditMode(true);
|
|
748
941
|
}
|
|
942
|
+
|
|
943
|
+
if (isEditorDisabledByParent || inheritedEditorMode === EDITOR_MODE__VIEW) {
|
|
944
|
+
setIsEditorViewOnly(true);
|
|
945
|
+
} else if (inheritedEditorMode === EDITOR_MODE__EDIT) {
|
|
946
|
+
const canEditInInheritedMode =
|
|
947
|
+
!canEditorViewOnly &&
|
|
948
|
+
userCanEdit &&
|
|
949
|
+
!disableEdit &&
|
|
950
|
+
(!canUser || canUser(EDIT)) &&
|
|
951
|
+
(!canRecordBeEdited || canRecordBeEdited(selection));
|
|
952
|
+
setIsEditorViewOnly(!canEditInInheritedMode);
|
|
953
|
+
}
|
|
954
|
+
|
|
749
955
|
setEditorMode(calculateEditorMode());
|
|
750
956
|
setLastSelection(selection);
|
|
751
957
|
|
|
@@ -755,7 +961,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
755
961
|
Promise.resolve().then(() => {
|
|
756
962
|
setIsIgnoreNextSelectionChange(false);
|
|
757
963
|
});
|
|
758
|
-
}, [selection]);
|
|
964
|
+
}, [selection, inheritedEditorMode, isEditorDisabledByParent]);
|
|
759
965
|
|
|
760
966
|
if (self) {
|
|
761
967
|
self.add = doAdd;
|
|
@@ -776,7 +982,13 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
776
982
|
setEditorMode(calculateEditorMode());
|
|
777
983
|
}
|
|
778
984
|
|
|
779
|
-
|
|
985
|
+
const editorModeContextValue = {
|
|
986
|
+
effectiveEditorMode: getEditorMode(),
|
|
987
|
+
isAnyAncestorUnsaved,
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
return <EditorModeContext.Provider value={editorModeContextValue}>
|
|
991
|
+
<WrappedComponent
|
|
780
992
|
{...props}
|
|
781
993
|
ref={ref}
|
|
782
994
|
disableWithEditor={false}
|
|
@@ -786,35 +998,38 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
786
998
|
isEditorShown={getIsEditorShown()}
|
|
787
999
|
getIsEditorShown={getIsEditorShown}
|
|
788
1000
|
isEditorViewOnly={isEditorViewOnly}
|
|
1001
|
+
isEditorModeControlledByParent={isEditorModeControlledByParent}
|
|
1002
|
+
isEditorDisabledByParent={isEditorDisabledByParent}
|
|
789
1003
|
isAdding={isAdding}
|
|
790
1004
|
isSaving={isSaving}
|
|
791
1005
|
editorMode={getEditorMode()}
|
|
792
1006
|
getEditorMode={getEditorMode}
|
|
793
|
-
onEditMode={setEditMode}
|
|
794
|
-
onViewMode={setViewMode}
|
|
1007
|
+
onEditMode={(isEditorModeControlledByParent || isEditorDisabledByParent) ? null : setEditMode}
|
|
1008
|
+
onViewMode={(isEditorModeControlledByParent || isEditorDisabledByParent) ? null : setViewMode}
|
|
795
1009
|
editorStateRef={editorStateRef}
|
|
796
1010
|
setIsEditorShown={setIsEditorShown}
|
|
797
1011
|
setIsIgnoreNextSelectionChange={setIsIgnoreNextSelectionChange}
|
|
798
|
-
onAdd={(!userCanEdit || disableAdd) ? null : doAdd}
|
|
799
|
-
onEdit={(!userCanEdit || disableEdit || (canRecordBeEdited && !canRecordBeEdited(selection))) ? null : doEdit}
|
|
800
|
-
onDelete={(!userCanEdit || disableDelete || (canRecordBeDeleted && !canRecordBeDeleted(selection))) ? null : doDelete}
|
|
801
|
-
onView={doView}
|
|
802
|
-
onDuplicate={doDuplicate}
|
|
1012
|
+
onAdd={(isEditorDisabledByParent || isCrudBlockedByInheritedView || !userCanEdit || disableAdd) ? null : doAdd}
|
|
1013
|
+
onEdit={(isEditorDisabledByParent || isCrudBlockedByInheritedView || !userCanEdit || disableEdit || (canRecordBeEdited && !canRecordBeEdited(selection))) ? null : doEdit}
|
|
1014
|
+
onDelete={(isEditorDisabledByParent || isCrudBlockedByInheritedView || !userCanEdit || disableDelete || (canRecordBeDeleted && !canRecordBeDeleted(selection))) ? null : doDelete}
|
|
1015
|
+
onView={isEditorDisabledByParent ? null : doView}
|
|
1016
|
+
onDuplicate={(isEditorDisabledByParent || isCrudBlockedByInheritedView) ? null : doDuplicate}
|
|
803
1017
|
onEditorSave={doEditorSave}
|
|
804
1018
|
onEditorCancel={doEditorCancel}
|
|
805
|
-
onEditorDelete={(!userCanEdit || disableDelete) ? null : doEditorDelete}
|
|
1019
|
+
onEditorDelete={(isEditorDisabledByParent || isCrudBlockedByInheritedView || !userCanEdit || disableDelete) ? null : doEditorDelete}
|
|
806
1020
|
onEditorClose={doEditorClose}
|
|
807
1021
|
setWithEditListeners={setListeners}
|
|
808
1022
|
isEditor={true}
|
|
809
1023
|
userCanEdit={userCanEdit}
|
|
810
1024
|
userCanView={userCanView}
|
|
811
|
-
disableAdd={disableAdd}
|
|
812
|
-
disableEdit={disableEdit}
|
|
813
|
-
disableDelete={disableDelete}
|
|
814
|
-
disableDuplicate={disableDuplicate}
|
|
815
|
-
disableView ={disableView}
|
|
1025
|
+
disableAdd={disableAdd || isEditorDisabledByParent || isCrudBlockedByInheritedView}
|
|
1026
|
+
disableEdit={disableEdit || isEditorDisabledByParent || isCrudBlockedByInheritedView}
|
|
1027
|
+
disableDelete={disableDelete || isEditorDisabledByParent || isCrudBlockedByInheritedView}
|
|
1028
|
+
disableDuplicate={disableDuplicate || isEditorDisabledByParent || isCrudBlockedByInheritedView}
|
|
1029
|
+
disableView ={disableView || isEditorDisabledByParent}
|
|
816
1030
|
setSelection={setSelectionDecorated}
|
|
817
1031
|
isTree={isTree}
|
|
818
|
-
|
|
1032
|
+
/>
|
|
1033
|
+
</EditorModeContext.Provider>;
|
|
819
1034
|
});
|
|
820
1035
|
}
|
|
@@ -126,6 +126,11 @@ export default function MetersEditor(props) {
|
|
|
126
126
|
isEditable: false,
|
|
127
127
|
isEditingEnabledInPlainEditor: true,
|
|
128
128
|
},
|
|
129
|
+
{
|
|
130
|
+
name: 'meters__latest_meter_reading_date',
|
|
131
|
+
isEditable: false,
|
|
132
|
+
isEditingEnabledInPlainEditor: true,
|
|
133
|
+
},
|
|
129
134
|
...(includeExtendedCalculatedFields ? [
|
|
130
135
|
{
|
|
131
136
|
name: 'meters__latest_inspection_date',
|
|
@@ -18,6 +18,9 @@ import {
|
|
|
18
18
|
EDIT,
|
|
19
19
|
} from '../../Constants/Commands.js';
|
|
20
20
|
import {
|
|
21
|
+
EDITOR_MODE__ADD,
|
|
22
|
+
EDITOR_MODE__EDIT,
|
|
23
|
+
EDITOR_MODE__VIEW,
|
|
21
24
|
EDITOR_TYPE__SIDE,
|
|
22
25
|
EDITOR_TYPE__SMART,
|
|
23
26
|
} from '../../Constants/Editor.js';
|
|
@@ -69,9 +72,12 @@ function Viewer(props) {
|
|
|
69
72
|
|
|
70
73
|
// withEditor
|
|
71
74
|
editorType,
|
|
75
|
+
getEditorMode,
|
|
76
|
+
editorMode,
|
|
72
77
|
onEditMode,
|
|
73
78
|
onClose,
|
|
74
79
|
onDelete,
|
|
80
|
+
isEditorModeControlledByParent = false,
|
|
75
81
|
|
|
76
82
|
// parent container
|
|
77
83
|
selectorId,
|
|
@@ -352,6 +358,14 @@ function Viewer(props) {
|
|
|
352
358
|
buildAncillary = () => {
|
|
353
359
|
const
|
|
354
360
|
validAncillaryItems = _.filter(ancillaryItems, (item) => !!item), // filter out any null/undefined items
|
|
361
|
+
parentEditorModeRaw = (getEditorMode && getEditorMode()) || editorMode || null,
|
|
362
|
+
parentEditorMode = parentEditorModeRaw === EDITOR_MODE__ADD
|
|
363
|
+
? EDITOR_MODE__EDIT
|
|
364
|
+
: parentEditorModeRaw,
|
|
365
|
+
normalizedParentEditorMode =
|
|
366
|
+
parentEditorMode === EDITOR_MODE__EDIT || parentEditorMode === EDITOR_MODE__VIEW
|
|
367
|
+
? parentEditorMode
|
|
368
|
+
: null,
|
|
355
369
|
components = [];
|
|
356
370
|
setAncillaryButtons([]);
|
|
357
371
|
if (validAncillaryItems.length) {
|
|
@@ -395,6 +409,8 @@ function Viewer(props) {
|
|
|
395
409
|
}
|
|
396
410
|
|
|
397
411
|
const
|
|
412
|
+
ancillaryEditorMode = itemPropsToPass.editorMode ?? normalizedParentEditorMode,
|
|
413
|
+
ancillaryInitialEditorMode = itemPropsToPass.initialEditorMode ?? ancillaryEditorMode ?? undefined,
|
|
398
414
|
Element = getComponentFromType(type),
|
|
399
415
|
element = <Element
|
|
400
416
|
{...testProps('ancillary-' + type)}
|
|
@@ -406,6 +422,8 @@ function Viewer(props) {
|
|
|
406
422
|
uniqueRepository={true}
|
|
407
423
|
parent={self}
|
|
408
424
|
{...itemPropsToPass}
|
|
425
|
+
editorMode={ancillaryEditorMode}
|
|
426
|
+
initialEditorMode={ancillaryInitialEditorMode}
|
|
409
427
|
className={className}
|
|
410
428
|
canRowsReorder={false}
|
|
411
429
|
/>;
|
|
@@ -563,7 +581,7 @@ function Viewer(props) {
|
|
|
563
581
|
|
|
564
582
|
<Toolbar className="justify-end">
|
|
565
583
|
<HStack className="flex-1 items-center">
|
|
566
|
-
|
|
584
|
+
<Text className="text-[20px] ml-1 text-grey-500">{isEditorModeControlledByParent ? 'View Mode (Inherited)' : 'View Mode'}</Text>
|
|
567
585
|
</HStack>
|
|
568
586
|
{onEditMode && (!canUser || canUser(EDIT)) &&
|
|
569
587
|
<Button
|
|
@@ -388,6 +388,19 @@ function AttachmentsElement(props) {
|
|
|
388
388
|
downloadInBackground(url, {}, Attachments.headers);
|
|
389
389
|
}
|
|
390
390
|
},
|
|
391
|
+
onDownloadAll = () => {
|
|
392
|
+
if (!model || _.isNil(modelid.current) || _.isArray(modelid.current)) {
|
|
393
|
+
alert('Cannot download all attachments without a single selected model and model id.');
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const url = Attachments.api.baseURL + 'Attachments/downloadAll/' + encodeURIComponent(model) + '/' + encodeURIComponent(modelid.current);
|
|
398
|
+
if (isPwa) {
|
|
399
|
+
alert('Files cannot be downloaded and viewed within an iOS PWA. Please use the Safari browser instead.');
|
|
400
|
+
} else {
|
|
401
|
+
downloadInBackground(url, {}, Attachments.headers);
|
|
402
|
+
}
|
|
403
|
+
},
|
|
391
404
|
|
|
392
405
|
// dropzone
|
|
393
406
|
onDropzoneChange = async (files) => {
|
|
@@ -1087,6 +1100,19 @@ function AttachmentsElement(props) {
|
|
|
1087
1100
|
)}
|
|
1088
1101
|
tooltip="List View"
|
|
1089
1102
|
/>
|
|
1103
|
+
<Box className="spacer flex-1" />
|
|
1104
|
+
<IconButton
|
|
1105
|
+
onPress={onDownloadAll}
|
|
1106
|
+
icon={Download}
|
|
1107
|
+
className={clsx(
|
|
1108
|
+
'w-[25px]',
|
|
1109
|
+
'h-[25px]',
|
|
1110
|
+
'px-[2px]',
|
|
1111
|
+
'py-[2px]',
|
|
1112
|
+
)}
|
|
1113
|
+
isDisabled={!model || _.isNil(modelid.current) || _.isArray(modelid.current) || files.length === 0}
|
|
1114
|
+
tooltip="Download All"
|
|
1115
|
+
/>
|
|
1090
1116
|
</HStack>
|
|
1091
1117
|
|
|
1092
1118
|
{content}
|