@onehat/ui 0.4.104 → 0.4.105
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 +3 -2
- package/src/Components/Hoc/withEditor.js +219 -19
- package/src/Components/Viewer/Viewer.js +2 -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,
|
|
@@ -1262,7 +1263,7 @@ function Form(props) {
|
|
|
1262
1263
|
|
|
1263
1264
|
if (inArray(editorType, [EDITOR_TYPE__SIDE, EDITOR_TYPE__SMART, EDITOR_TYPE__WINDOWED]) &&
|
|
1264
1265
|
isSingle && getEditorMode() === EDITOR_MODE__EDIT &&
|
|
1265
|
-
(onBack || onViewMode)) {
|
|
1266
|
+
(onBack || onViewMode || isEditorModeControlledByParent)) {
|
|
1266
1267
|
modeHeader = <Toolbar>
|
|
1267
1268
|
<HStack className="flex-1 items-center">
|
|
1268
1269
|
{onBack &&
|
|
@@ -1280,7 +1281,7 @@ function Form(props) {
|
|
|
1280
1281
|
)}
|
|
1281
1282
|
text="Back"
|
|
1282
1283
|
/>}
|
|
1283
|
-
<Text className="text-[20px] ml-1 text-grey-500">Edit Mode</Text>
|
|
1284
|
+
<Text className="text-[20px] ml-1 text-grey-500">{isEditorModeControlledByParent ? 'Edit Mode (Inherited)' : 'Edit Mode'}</Text>
|
|
1284
1285
|
</HStack>
|
|
1285
1286
|
{onViewMode && !disableView && (!canUser || canUser(VIEW)) &&
|
|
1286
1287
|
<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
|
|
|
@@ -58,6 +59,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
58
59
|
defaultValues,
|
|
59
60
|
initialEditorMode = EDITOR_MODE__VIEW,
|
|
60
61
|
stayInEditModeOnSelectionChange = false,
|
|
62
|
+
inheritParentEditorMode = true,
|
|
61
63
|
|
|
62
64
|
// withComponent
|
|
63
65
|
self,
|
|
@@ -84,6 +86,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
84
86
|
confirm,
|
|
85
87
|
hideAlert,
|
|
86
88
|
} = props,
|
|
89
|
+
parentEditorModeContext = useContext(EditorModeContext),
|
|
87
90
|
forceUpdate = useForceUpdate(),
|
|
88
91
|
listeners = useRef({}),
|
|
89
92
|
editorStateRef = useRef(),
|
|
@@ -170,13 +173,85 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
170
173
|
forceUpdate();
|
|
171
174
|
}
|
|
172
175
|
},
|
|
176
|
+
getParentEditorMode = () => {
|
|
177
|
+
return parentEditorModeContext?.effectiveEditorMode || null;
|
|
178
|
+
},
|
|
179
|
+
getInheritedEditorMode = () => {
|
|
180
|
+
if (!inheritParentEditorMode) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
const parentMode = getParentEditorMode();
|
|
184
|
+
if (parentMode === EDITOR_MODE__ADD) {
|
|
185
|
+
return EDITOR_MODE__EDIT;
|
|
186
|
+
}
|
|
187
|
+
if (parentMode === EDITOR_MODE__EDIT || parentMode === EDITOR_MODE__VIEW) {
|
|
188
|
+
return parentMode;
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
},
|
|
192
|
+
getIsParentSaveLocked = () => {
|
|
193
|
+
return !!parentEditorModeContext?.isAnyAncestorUnsaved;
|
|
194
|
+
},
|
|
195
|
+
getIsCurrentSelectionUnsaved = () => {
|
|
196
|
+
const selection = getSelection();
|
|
197
|
+
if (!selection || selection.length !== 1) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
const record = selection[0];
|
|
201
|
+
if (!record || record.isDestroyed) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
return !!(record.isPhantom || record.isRemotePhantom);
|
|
205
|
+
},
|
|
206
|
+
getIsEditorDisabledByParent = () => {
|
|
207
|
+
return getIsParentSaveLocked();
|
|
208
|
+
},
|
|
209
|
+
getIsEditorModeControlledByParent = () => {
|
|
210
|
+
return !!getInheritedEditorMode();
|
|
211
|
+
},
|
|
173
212
|
getNewEntityDisplayValue = () => {
|
|
174
213
|
return newEntityDisplayValueRef.current;
|
|
175
214
|
},
|
|
176
215
|
setNewEntityDisplayValue = (val) => {
|
|
177
216
|
newEntityDisplayValueRef.current = val;
|
|
178
217
|
},
|
|
218
|
+
showViewFallback = async () => {
|
|
219
|
+
// helper for doEdit
|
|
220
|
+
// If the editor is forced into EDIT mode by parent inheritance,
|
|
221
|
+
// but the child editor cannot actually be in edit mode due to permissions or configuration,
|
|
222
|
+
// switch the mode to VIEW.
|
|
223
|
+
|
|
224
|
+
if (!userCanView) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (canUser && !canUser(VIEW)) {
|
|
228
|
+
showPermissionsError(VIEW);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (canProceedWithCrud && !canProceedWithCrud()) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (editorType === EDITOR_TYPE__INLINE) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const selection = getSelection();
|
|
238
|
+
if (selection.length !== 1) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
setIsEditorViewOnly(true);
|
|
242
|
+
setEditorMode(EDITOR_MODE__VIEW);
|
|
243
|
+
setIsEditorShown(true);
|
|
244
|
+
if (getListeners().onAfterView) {
|
|
245
|
+
await getListeners().onAfterView();
|
|
246
|
+
}
|
|
247
|
+
},
|
|
179
248
|
doAdd = async (e, values) => {
|
|
249
|
+
if (getIsEditorDisabledByParent()) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (getInheritedEditorMode() === EDITOR_MODE__VIEW) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
180
255
|
if (canUser && !canUser(ADD)) {
|
|
181
256
|
showPermissionsError(ADD);
|
|
182
257
|
return;
|
|
@@ -295,7 +370,23 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
295
370
|
setIsEditorShown(true);
|
|
296
371
|
},
|
|
297
372
|
doEdit = async () => {
|
|
373
|
+
if (getIsEditorDisabledByParent()) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const inheritedEditorMode = getInheritedEditorMode();
|
|
377
|
+
if (inheritedEditorMode === EDITOR_MODE__VIEW) {
|
|
378
|
+
await doView(false);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (inheritedEditorMode === EDITOR_MODE__EDIT && (!userCanEdit || disableEdit || canEditorViewOnly)) {
|
|
382
|
+
await showViewFallback();
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
298
385
|
if (canUser && !canUser(EDIT)) {
|
|
386
|
+
if (inheritedEditorMode === EDITOR_MODE__EDIT) {
|
|
387
|
+
await showViewFallback();
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
299
390
|
showPermissionsError(EDIT);
|
|
300
391
|
return;
|
|
301
392
|
}
|
|
@@ -317,6 +408,12 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
317
408
|
setIsEditorShown(true);
|
|
318
409
|
},
|
|
319
410
|
doDelete = async (args) => {
|
|
411
|
+
if (getIsEditorDisabledByParent()) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (getInheritedEditorMode() === EDITOR_MODE__VIEW) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
320
417
|
if (canUser && !canUser(DELETE)) {
|
|
321
418
|
showPermissionsError(DELETE);
|
|
322
419
|
return;
|
|
@@ -430,6 +527,17 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
430
527
|
}
|
|
431
528
|
},
|
|
432
529
|
doView = async (allowEditing = false) => {
|
|
530
|
+
if (getIsEditorDisabledByParent()) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const inheritedEditorMode = getInheritedEditorMode();
|
|
534
|
+
if (inheritedEditorMode === EDITOR_MODE__EDIT) {
|
|
535
|
+
await doEdit();
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (inheritedEditorMode === EDITOR_MODE__VIEW) {
|
|
539
|
+
allowEditing = false;
|
|
540
|
+
}
|
|
433
541
|
if (!userCanView) {
|
|
434
542
|
return;
|
|
435
543
|
}
|
|
@@ -460,6 +568,12 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
460
568
|
}
|
|
461
569
|
},
|
|
462
570
|
doDuplicate = async () => {
|
|
571
|
+
if (getIsEditorDisabledByParent()) {
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
if (getInheritedEditorMode() === EDITOR_MODE__VIEW) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
463
577
|
if (!userCanEdit || disableDuplicate) {
|
|
464
578
|
return;
|
|
465
579
|
}
|
|
@@ -533,6 +647,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
533
647
|
}
|
|
534
648
|
},
|
|
535
649
|
doEditorSave = async (data, e) => {
|
|
650
|
+
if (getIsEditorDisabledByParent()) {
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
536
653
|
let mode = getEditorMode() === EDITOR_MODE__ADD ? ADD : EDIT;
|
|
537
654
|
if (canUser && !canUser(mode)) {
|
|
538
655
|
showPermissionsError(mode);
|
|
@@ -651,6 +768,12 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
651
768
|
setIsEditorShown(false);
|
|
652
769
|
},
|
|
653
770
|
doEditorDelete = async () => {
|
|
771
|
+
if (getIsEditorDisabledByParent()) {
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
if (getInheritedEditorMode() === EDITOR_MODE__VIEW) {
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
654
777
|
if (canUser && !canUser(DELETE)) {
|
|
655
778
|
showPermissionsError(DELETE);
|
|
656
779
|
return;
|
|
@@ -662,6 +785,46 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
662
785
|
});
|
|
663
786
|
},
|
|
664
787
|
calculateEditorMode = () => {
|
|
788
|
+
// Calculate the editor's effective mode based on parent inheritance, permissions, and local selection state.
|
|
789
|
+
// Priority order:
|
|
790
|
+
// 1. If parent is save-locked (unsaved ancestor), force VIEW mode
|
|
791
|
+
// 2. If parent forces VIEW mode via inheritance, return VIEW (child cannot edit if parent is view-only)
|
|
792
|
+
// 3. If parent forces EDIT mode via inheritance, check child permissions:
|
|
793
|
+
// a. If parent disabled, child disabled, or child cannot edit, return VIEW
|
|
794
|
+
// b. If single phantom record, return ADD (new record being created)
|
|
795
|
+
// c. Otherwise return EDIT or VIEW based on selection count
|
|
796
|
+
// 4. Fall back to local selection heuristics (multiple→EDIT, single→VIEW, stays in previous mode if configured)
|
|
797
|
+
const
|
|
798
|
+
selection = getSelection(),
|
|
799
|
+
inheritedEditorMode = getInheritedEditorMode();
|
|
800
|
+
|
|
801
|
+
if (getIsEditorDisabledByParent()) {
|
|
802
|
+
return EDITOR_MODE__VIEW;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
if (inheritedEditorMode === EDITOR_MODE__VIEW) {
|
|
806
|
+
return EDITOR_MODE__VIEW;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
if (inheritedEditorMode === EDITOR_MODE__EDIT) {
|
|
810
|
+
if (!getCanEditorBeInEditMode()) {
|
|
811
|
+
return EDITOR_MODE__VIEW;
|
|
812
|
+
}
|
|
813
|
+
if (canEditorViewOnly || !userCanEdit || disableEdit) {
|
|
814
|
+
return EDITOR_MODE__VIEW;
|
|
815
|
+
}
|
|
816
|
+
if (canUser && !canUser(EDIT)) {
|
|
817
|
+
return EDITOR_MODE__VIEW;
|
|
818
|
+
}
|
|
819
|
+
if (canRecordBeEdited && canRecordBeEdited(selection) === false) {
|
|
820
|
+
return EDITOR_MODE__VIEW;
|
|
821
|
+
}
|
|
822
|
+
if (selection.length === 1 && !selection[0].isDestroyed && (selection[0].isPhantom || selection[0].isRemotePhantom) && !disableAdd) {
|
|
823
|
+
return EDITOR_MODE__ADD;
|
|
824
|
+
}
|
|
825
|
+
return selection.length ? EDITOR_MODE__EDIT : EDITOR_MODE__VIEW;
|
|
826
|
+
}
|
|
827
|
+
|
|
665
828
|
if (!getCanEditorBeInEditMode()) { // this is a result of canRecordBeEdited returning false
|
|
666
829
|
return EDITOR_MODE__VIEW;
|
|
667
830
|
}
|
|
@@ -677,7 +840,6 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
677
840
|
}
|
|
678
841
|
|
|
679
842
|
// calculateEditorMode gets called only on selection changes
|
|
680
|
-
const selection = getSelection();
|
|
681
843
|
let mode;
|
|
682
844
|
if (editorType === EDITOR_TYPE__SIDE && !_.isNil(UiGlobals.isSideEditorAlwaysEditMode) && UiGlobals.isSideEditorAlwaysEditMode) {
|
|
683
845
|
// special case: side editor is always edit mode
|
|
@@ -705,6 +867,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
705
867
|
return mode;
|
|
706
868
|
},
|
|
707
869
|
setEditMode = () => {
|
|
870
|
+
if (getIsEditorDisabledByParent() || getIsEditorModeControlledByParent()) {
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
708
873
|
if (canUser && !canUser(EDIT)) {
|
|
709
874
|
showPermissionsError(EDIT);
|
|
710
875
|
return;
|
|
@@ -713,6 +878,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
713
878
|
setEditorMode(EDITOR_MODE__EDIT);
|
|
714
879
|
},
|
|
715
880
|
setViewMode = () => {
|
|
881
|
+
if (getIsEditorDisabledByParent() || getIsEditorModeControlledByParent()) {
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
716
884
|
if (canUser && !canUser(VIEW)) {
|
|
717
885
|
showPermissionsError(VIEW);
|
|
718
886
|
return;
|
|
@@ -729,10 +897,20 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
729
897
|
}
|
|
730
898
|
};
|
|
731
899
|
|
|
900
|
+
const
|
|
901
|
+
inheritedEditorMode = getInheritedEditorMode(),
|
|
902
|
+
isEditorDisabledByParent = getIsEditorDisabledByParent(),
|
|
903
|
+
isEditorModeControlledByParent = getIsEditorModeControlledByParent(),
|
|
904
|
+
isCurrentSelectionUnsaved = getIsCurrentSelectionUnsaved(),
|
|
905
|
+
isAnyAncestorUnsaved = getIsParentSaveLocked() || isCurrentSelectionUnsaved,
|
|
906
|
+
isCrudBlockedByInheritedView = inheritedEditorMode === EDITOR_MODE__VIEW;
|
|
907
|
+
|
|
732
908
|
useEffect(() => {
|
|
733
909
|
|
|
734
910
|
if (editorType === EDITOR_TYPE__SIDE) {
|
|
735
|
-
if (
|
|
911
|
+
if (isEditorDisabledByParent) {
|
|
912
|
+
setIsEditorShown(false);
|
|
913
|
+
} else if (selection?.length) { // || isAdding
|
|
736
914
|
// there is a selection, so show the editor
|
|
737
915
|
setIsEditorShown(true);
|
|
738
916
|
} else {
|
|
@@ -746,6 +924,19 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
746
924
|
} else {
|
|
747
925
|
setCanEditorBeInEditMode(true);
|
|
748
926
|
}
|
|
927
|
+
|
|
928
|
+
if (isEditorDisabledByParent || inheritedEditorMode === EDITOR_MODE__VIEW) {
|
|
929
|
+
setIsEditorViewOnly(true);
|
|
930
|
+
} else if (inheritedEditorMode === EDITOR_MODE__EDIT) {
|
|
931
|
+
const canEditInInheritedMode =
|
|
932
|
+
!canEditorViewOnly &&
|
|
933
|
+
userCanEdit &&
|
|
934
|
+
!disableEdit &&
|
|
935
|
+
(!canUser || canUser(EDIT)) &&
|
|
936
|
+
(!canRecordBeEdited || canRecordBeEdited(selection));
|
|
937
|
+
setIsEditorViewOnly(!canEditInInheritedMode);
|
|
938
|
+
}
|
|
939
|
+
|
|
749
940
|
setEditorMode(calculateEditorMode());
|
|
750
941
|
setLastSelection(selection);
|
|
751
942
|
|
|
@@ -755,7 +946,7 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
755
946
|
Promise.resolve().then(() => {
|
|
756
947
|
setIsIgnoreNextSelectionChange(false);
|
|
757
948
|
});
|
|
758
|
-
}, [selection]);
|
|
949
|
+
}, [selection, inheritedEditorMode, isEditorDisabledByParent]);
|
|
759
950
|
|
|
760
951
|
if (self) {
|
|
761
952
|
self.add = doAdd;
|
|
@@ -776,7 +967,13 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
776
967
|
setEditorMode(calculateEditorMode());
|
|
777
968
|
}
|
|
778
969
|
|
|
779
|
-
|
|
970
|
+
const editorModeContextValue = {
|
|
971
|
+
effectiveEditorMode: getEditorMode(),
|
|
972
|
+
isAnyAncestorUnsaved,
|
|
973
|
+
};
|
|
974
|
+
|
|
975
|
+
return <EditorModeContext.Provider value={editorModeContextValue}>
|
|
976
|
+
<WrappedComponent
|
|
780
977
|
{...props}
|
|
781
978
|
ref={ref}
|
|
782
979
|
disableWithEditor={false}
|
|
@@ -786,35 +983,38 @@ export default function withEditor(WrappedComponent, isTree = false) {
|
|
|
786
983
|
isEditorShown={getIsEditorShown()}
|
|
787
984
|
getIsEditorShown={getIsEditorShown}
|
|
788
985
|
isEditorViewOnly={isEditorViewOnly}
|
|
986
|
+
isEditorModeControlledByParent={isEditorModeControlledByParent}
|
|
987
|
+
isEditorDisabledByParent={isEditorDisabledByParent}
|
|
789
988
|
isAdding={isAdding}
|
|
790
989
|
isSaving={isSaving}
|
|
791
990
|
editorMode={getEditorMode()}
|
|
792
991
|
getEditorMode={getEditorMode}
|
|
793
|
-
onEditMode={setEditMode}
|
|
794
|
-
onViewMode={setViewMode}
|
|
992
|
+
onEditMode={(isEditorModeControlledByParent || isEditorDisabledByParent) ? null : setEditMode}
|
|
993
|
+
onViewMode={(isEditorModeControlledByParent || isEditorDisabledByParent) ? null : setViewMode}
|
|
795
994
|
editorStateRef={editorStateRef}
|
|
796
995
|
setIsEditorShown={setIsEditorShown}
|
|
797
996
|
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}
|
|
997
|
+
onAdd={(isEditorDisabledByParent || isCrudBlockedByInheritedView || !userCanEdit || disableAdd) ? null : doAdd}
|
|
998
|
+
onEdit={(isEditorDisabledByParent || isCrudBlockedByInheritedView || !userCanEdit || disableEdit || (canRecordBeEdited && !canRecordBeEdited(selection))) ? null : doEdit}
|
|
999
|
+
onDelete={(isEditorDisabledByParent || isCrudBlockedByInheritedView || !userCanEdit || disableDelete || (canRecordBeDeleted && !canRecordBeDeleted(selection))) ? null : doDelete}
|
|
1000
|
+
onView={isEditorDisabledByParent ? null : doView}
|
|
1001
|
+
onDuplicate={(isEditorDisabledByParent || isCrudBlockedByInheritedView) ? null : doDuplicate}
|
|
803
1002
|
onEditorSave={doEditorSave}
|
|
804
1003
|
onEditorCancel={doEditorCancel}
|
|
805
|
-
onEditorDelete={(!userCanEdit || disableDelete) ? null : doEditorDelete}
|
|
1004
|
+
onEditorDelete={(isEditorDisabledByParent || isCrudBlockedByInheritedView || !userCanEdit || disableDelete) ? null : doEditorDelete}
|
|
806
1005
|
onEditorClose={doEditorClose}
|
|
807
1006
|
setWithEditListeners={setListeners}
|
|
808
1007
|
isEditor={true}
|
|
809
1008
|
userCanEdit={userCanEdit}
|
|
810
1009
|
userCanView={userCanView}
|
|
811
|
-
disableAdd={disableAdd}
|
|
812
|
-
disableEdit={disableEdit}
|
|
813
|
-
disableDelete={disableDelete}
|
|
814
|
-
disableDuplicate={disableDuplicate}
|
|
815
|
-
disableView ={disableView}
|
|
1010
|
+
disableAdd={disableAdd || isEditorDisabledByParent || isCrudBlockedByInheritedView}
|
|
1011
|
+
disableEdit={disableEdit || isEditorDisabledByParent || isCrudBlockedByInheritedView}
|
|
1012
|
+
disableDelete={disableDelete || isEditorDisabledByParent || isCrudBlockedByInheritedView}
|
|
1013
|
+
disableDuplicate={disableDuplicate || isEditorDisabledByParent || isCrudBlockedByInheritedView}
|
|
1014
|
+
disableView ={disableView || isEditorDisabledByParent}
|
|
816
1015
|
setSelection={setSelectionDecorated}
|
|
817
1016
|
isTree={isTree}
|
|
818
|
-
|
|
1017
|
+
/>
|
|
1018
|
+
</EditorModeContext.Provider>;
|
|
819
1019
|
});
|
|
820
1020
|
}
|
|
@@ -72,6 +72,7 @@ function Viewer(props) {
|
|
|
72
72
|
onEditMode,
|
|
73
73
|
onClose,
|
|
74
74
|
onDelete,
|
|
75
|
+
isEditorModeControlledByParent = false,
|
|
75
76
|
|
|
76
77
|
// parent container
|
|
77
78
|
selectorId,
|
|
@@ -563,7 +564,7 @@ function Viewer(props) {
|
|
|
563
564
|
|
|
564
565
|
<Toolbar className="justify-end">
|
|
565
566
|
<HStack className="flex-1 items-center">
|
|
566
|
-
|
|
567
|
+
<Text className="text-[20px] ml-1 text-grey-500">{isEditorModeControlledByParent ? 'View Mode (Inherited)' : 'View Mode'}</Text>
|
|
567
568
|
</HStack>
|
|
568
569
|
{onEditMode && (!canUser || canUser(EDIT)) &&
|
|
569
570
|
<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}
|