@onehat/ui 0.4.82 → 0.4.84
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 +7 -6
- package/src/Components/Container/Container.js +336 -147
- package/src/Components/Form/Field/Combo/Combo.js +84 -15
- package/src/Components/Form/Field/Hidden.js +13 -0
- package/src/Components/Form/Field/Json.js +2 -1
- package/src/Components/Form/Field/Tag/Tag.js +93 -78
- package/src/Components/Form/Field/Tag/ValueBox.js +2 -2
- package/src/Components/Form/Form.js +3 -2
- package/src/Components/Grid/Grid.js +79 -42
- package/src/Components/Grid/GridRow.js +10 -0
- package/src/Components/Hoc/Secondary/withSecondaryEditor.js +137 -43
- package/src/Components/Hoc/Secondary/withSecondarySideEditor.js +6 -4
- package/src/Components/Hoc/withContextMenu.js +13 -4
- package/src/Components/Hoc/withDraggable.js +7 -3
- package/src/Components/Hoc/withSideEditor.js +7 -4
- package/src/Components/Layout/AsyncOperation.js +54 -25
- package/src/Components/Panel/Mask.js +1 -14
- package/src/Components/Screens/Manager.js +4 -5
- package/src/Components/Toolbar/Pagination.js +108 -106
- package/src/Components/Tree/Tree.js +24 -0
- package/src/Components/Tree/TreeNode.js +2 -1
- package/src/Components/Viewer/Viewer.js +2 -5
- package/src/Components/index.js +2 -0
- package/src/Constants/Progress.js +6 -1
- package/src/PlatformImports/Web/Attachments.js +3 -3
|
@@ -77,6 +77,7 @@ import Toolbar from '../Toolbar/Toolbar.js';
|
|
|
77
77
|
import NoReorderRows from '../Icons/NoReorderRows.js';
|
|
78
78
|
import ReorderRows from '../Icons/ReorderRows.js';
|
|
79
79
|
import Unauthorized from '../Messages/Unauthorized.js';
|
|
80
|
+
import Mask from '../Panel/Mask.js';
|
|
80
81
|
import _ from 'lodash';
|
|
81
82
|
|
|
82
83
|
|
|
@@ -133,6 +134,7 @@ function GridComponent(props) {
|
|
|
133
134
|
flatListProps = {},
|
|
134
135
|
onRowPress,
|
|
135
136
|
onRender,
|
|
137
|
+
onLayout,
|
|
136
138
|
disableLoadOnRender = false,
|
|
137
139
|
forceLoadOnRender = false,
|
|
138
140
|
pullToRefresh = true,
|
|
@@ -146,6 +148,7 @@ function GridComponent(props) {
|
|
|
146
148
|
showSelectHandle = true,
|
|
147
149
|
isRowSelectable = true,
|
|
148
150
|
isRowHoverable = true,
|
|
151
|
+
isDisabled = false,
|
|
149
152
|
canColumnsSort = true,
|
|
150
153
|
canColumnsReorder = true,
|
|
151
154
|
canColumnsResize = true,
|
|
@@ -521,6 +524,33 @@ function GridComponent(props) {
|
|
|
521
524
|
onContextMenu(item, e, newSelection);
|
|
522
525
|
}
|
|
523
526
|
}}
|
|
527
|
+
onContextMenu={(e) => {
|
|
528
|
+
// web only; happens before onLongPress triggers
|
|
529
|
+
// different behavior here than onLongPress:
|
|
530
|
+
// if user clicks on a header row or phantom record, or if onContextMenu is not set, pass to the browser's context menu
|
|
531
|
+
if (isHeaderRow || isReorderMode) {
|
|
532
|
+
return
|
|
533
|
+
}
|
|
534
|
+
if (selection && selection[0] && selection[0].isRemotePhantom) {
|
|
535
|
+
return; // block context menu or changing selection when a remote phantom is already selected
|
|
536
|
+
}
|
|
537
|
+
if (onContextMenu) {
|
|
538
|
+
e.preventDefault();
|
|
539
|
+
e.stopPropagation(); // disallow browser's default behavior for context menu
|
|
540
|
+
|
|
541
|
+
// if the right-clicked item is not in the current selection,
|
|
542
|
+
// set the selection only to this one item.
|
|
543
|
+
let newSelection = selection;
|
|
544
|
+
if (!isInSelection(item)) {
|
|
545
|
+
newSelection = [item];
|
|
546
|
+
if (!disableWithSelection) {
|
|
547
|
+
setSelection(newSelection);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
onContextMenu(item, e, newSelection);
|
|
552
|
+
}
|
|
553
|
+
}}
|
|
524
554
|
className={clsx(
|
|
525
555
|
'Pressable',
|
|
526
556
|
'Row',
|
|
@@ -1092,6 +1122,9 @@ function GridComponent(props) {
|
|
|
1092
1122
|
if (!Repository || Repository.isDestroyed) { // This method gets delayed, so it's possible for Repository to have been destroyed. Check for this
|
|
1093
1123
|
return;
|
|
1094
1124
|
}
|
|
1125
|
+
if (onLayout) {
|
|
1126
|
+
onLayout(e);
|
|
1127
|
+
}
|
|
1095
1128
|
if (DEBUG) {
|
|
1096
1129
|
console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight A`);
|
|
1097
1130
|
}
|
|
@@ -1675,51 +1708,55 @@ function GridComponent(props) {
|
|
|
1675
1708
|
className += ' ' + props.className;
|
|
1676
1709
|
}
|
|
1677
1710
|
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1711
|
+
if (isDisabled) {
|
|
1712
|
+
grid = <Mask />;
|
|
1713
|
+
} else {
|
|
1714
|
+
grid = <VStackNative
|
|
1715
|
+
{...testProps(self)}
|
|
1716
|
+
ref={containerRef}
|
|
1717
|
+
tabIndex={0}
|
|
1718
|
+
onKeyDown={onGridKeyDown}
|
|
1719
|
+
onLayout={(e) => debouncedAdjustPageSizeToHeight(e)}
|
|
1720
|
+
className={className}
|
|
1721
|
+
style={style}
|
|
1722
|
+
>
|
|
1723
|
+
{topToolbar &&
|
|
1724
|
+
<VStack ref={topToolbarRef}>
|
|
1725
|
+
{topToolbar}
|
|
1726
|
+
</VStack>}
|
|
1727
|
+
|
|
1728
|
+
<VStack
|
|
1729
|
+
ref={gridContainerRef}
|
|
1730
|
+
onClick={() => {
|
|
1731
|
+
if (!isReorderMode && !isInlineEditorShown && deselectAll) {
|
|
1732
|
+
deselectAll();
|
|
1733
|
+
}
|
|
1734
|
+
}}
|
|
1735
|
+
className={clsx(
|
|
1736
|
+
'gridContainer',
|
|
1737
|
+
'w-full',
|
|
1738
|
+
// 'h-full',
|
|
1739
|
+
'flex-1',
|
|
1740
|
+
'min-h-[40px]',
|
|
1741
|
+
'relative', // Enable positioning for overlay
|
|
1742
|
+
gridContainerBorderClassName,
|
|
1743
|
+
)}
|
|
1744
|
+
>
|
|
1745
|
+
{grid}
|
|
1746
|
+
{/* Loading overlay during measurement phases to prevent visual flashing */}
|
|
1747
|
+
{autoAdjustPageSizeToHeight &&
|
|
1748
|
+
(getMeasurementPhase() === PHASES__INITIAL || getMeasurementPhase() === PHASES__MEASURING) &&
|
|
1749
|
+
entities?.length > 0 && (
|
|
1750
|
+
<VStack className="absolute inset-0 z-10 bg-white">
|
|
1751
|
+
<Loading isScreen={true} />
|
|
1752
|
+
</VStack>
|
|
1753
|
+
)}
|
|
1716
1754
|
</VStack>
|
|
1717
|
-
)}
|
|
1718
|
-
</VStack>
|
|
1719
1755
|
|
|
1720
|
-
|
|
1756
|
+
{listFooterComponent}
|
|
1721
1757
|
|
|
1722
|
-
|
|
1758
|
+
</VStackNative>;
|
|
1759
|
+
}
|
|
1723
1760
|
|
|
1724
1761
|
if (isDropTarget) {
|
|
1725
1762
|
grid = <VStackNative
|
|
@@ -285,6 +285,9 @@ const GridRow = forwardRef((props, ref) => {
|
|
|
285
285
|
'py-3',
|
|
286
286
|
'block',
|
|
287
287
|
areCellsScrollable ? 'overflow-auto' : 'overflow-hidden',
|
|
288
|
+
'[&::-webkit-scrollbar]:h-2',
|
|
289
|
+
'[&::-webkit-scrollbar-thumb]:bg-gray-300',
|
|
290
|
+
'[&::-webkit-scrollbar-thumb]:rounded-full',
|
|
288
291
|
colClassName,
|
|
289
292
|
styles.GRID_CELL_CLASSNAME,
|
|
290
293
|
styles.GRID_ROW_MAX_HEIGHT_NORMAL,
|
|
@@ -347,10 +350,17 @@ const GridRow = forwardRef((props, ref) => {
|
|
|
347
350
|
if (config.getCellProps) {
|
|
348
351
|
_.assign(elementProps, config.getCellProps(item));
|
|
349
352
|
}
|
|
353
|
+
|
|
354
|
+
// TODO: incorporate better scrollbar formatting with
|
|
355
|
+
// tailwind plugin 'tailwind-scrollbar' (already installed, just not yet used here)
|
|
356
|
+
|
|
350
357
|
let textClassName = clsx(
|
|
351
358
|
'GridRow-TextNative',
|
|
352
359
|
'self-center',
|
|
353
360
|
areCellsScrollable ? 'overflow-auto' : 'overflow-hidden',
|
|
361
|
+
'[&::-webkit-scrollbar]:h-2',
|
|
362
|
+
'[&::-webkit-scrollbar-thumb]:bg-gray-300',
|
|
363
|
+
'[&::-webkit-scrollbar-thumb]:rounded-full',
|
|
354
364
|
colClassName,
|
|
355
365
|
styles.GRID_CELL_CLASSNAME,
|
|
356
366
|
styles.GRID_ROW_MAX_HEIGHT_EXTRA,
|
|
@@ -32,6 +32,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
32
32
|
secondaryUserCanEdit = true, // not permissions, but capability
|
|
33
33
|
secondaryUserCanView = true,
|
|
34
34
|
secondaryCanEditorViewOnly = false, // whether the editor can *ever* change state out of 'View' mode
|
|
35
|
+
secondaryCanProceedWithCrud, // fn returns bool on if the CRUD operation can proceed
|
|
35
36
|
secondaryDisableAdd = false,
|
|
36
37
|
secondaryDisableEdit = false,
|
|
37
38
|
secondaryDisableDelete = false,
|
|
@@ -47,12 +48,14 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
47
48
|
secondaryEditorType,
|
|
48
49
|
secondaryOnAdd,
|
|
49
50
|
secondaryOnChange, // any kind of crud change
|
|
51
|
+
secondaryOnBeforeDelete,
|
|
50
52
|
secondaryOnDelete,
|
|
51
53
|
secondaryOnSave, // this could also be called 'onEdit'
|
|
52
54
|
secondaryOnEditorClose,
|
|
53
55
|
secondaryNewEntityDisplayValue,
|
|
54
56
|
secondaryNewEntityDisplayProperty, // in case the field to set for newEntityDisplayValue is different from model
|
|
55
57
|
secondaryDefaultValues,
|
|
58
|
+
secondaryInitialEditorMode = EDITOR_MODE__VIEW,
|
|
56
59
|
secondaryStayInEditModeOnSelectionChange = false,
|
|
57
60
|
|
|
58
61
|
// withComponent
|
|
@@ -86,11 +89,11 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
86
89
|
secondaryNewEntityDisplayValueRef = useRef(),
|
|
87
90
|
secondaryEditorModeRef = useRef(EDITOR_MODE__VIEW),
|
|
88
91
|
secondaryIsIgnoreNextSelectionChangeRef = useRef(false),
|
|
92
|
+
secondaryIsEditorShownRef = useRef(false),
|
|
89
93
|
secondaryModel = SecondaryRepository?.schema?.name,
|
|
90
94
|
[secondaryCurrentRecord, secondarySetCurrentRecord] = useState(null),
|
|
91
95
|
[secondaryIsAdding, setIsAdding] = useState(false),
|
|
92
96
|
[secondaryIsSaving, setIsSaving] = useState(false),
|
|
93
|
-
[secondaryIsEditorShown, secondarySetIsEditorShownRaw] = useState(false),
|
|
94
97
|
[secondaryIsEditorViewOnly, setIsEditorViewOnly] = useState(secondaryCanEditorViewOnly), // current state of whether editor is in view-only mode
|
|
95
98
|
[secondaryLastSelection, setLastSelection] = useState(),
|
|
96
99
|
secondarySetIsIgnoreNextSelectionChange = (bool) => {
|
|
@@ -100,11 +103,24 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
100
103
|
return secondaryIsIgnoreNextSelectionChangeRef.current;
|
|
101
104
|
},
|
|
102
105
|
secondarySetIsEditorShown = (bool) => {
|
|
103
|
-
|
|
106
|
+
secondaryIsEditorShownRef.current = bool;
|
|
107
|
+
forceUpdate();
|
|
104
108
|
if (!bool && secondaryOnEditorClose) {
|
|
105
109
|
secondaryOnEditorClose();
|
|
106
110
|
}
|
|
107
111
|
},
|
|
112
|
+
secondaryGetIsEditorShown = () => {
|
|
113
|
+
return secondaryIsEditorShownRef.current;
|
|
114
|
+
},
|
|
115
|
+
secondarySetIsWaitModalShown = (bool) => {
|
|
116
|
+
const
|
|
117
|
+
dispatch = UiGlobals.redux?.dispatch,
|
|
118
|
+
setIsWaitModalShownAction = UiGlobals.systemReducer?.setIsWaitModalShownAction;
|
|
119
|
+
if (setIsWaitModalShownAction) {
|
|
120
|
+
console.log('withSecondaryEditor:setIsWaitModalShownAction', bool);
|
|
121
|
+
dispatch(setIsWaitModalShownAction(bool));
|
|
122
|
+
}
|
|
123
|
+
},
|
|
108
124
|
secondarySetSelectionDecorated = (newSelection) => {
|
|
109
125
|
function doIt() {
|
|
110
126
|
secondarySetSelection(newSelection);
|
|
@@ -139,7 +155,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
139
155
|
forceUpdate();
|
|
140
156
|
}
|
|
141
157
|
},
|
|
142
|
-
|
|
158
|
+
secondaryGetNewEntityDisplayValue = () => {
|
|
143
159
|
return secondaryNewEntityDisplayValueRef.current;
|
|
144
160
|
},
|
|
145
161
|
secondaryDoAdd = async (e, values) => {
|
|
@@ -147,6 +163,9 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
147
163
|
showPermissionsError(ADD, secondaryModel);
|
|
148
164
|
return;
|
|
149
165
|
}
|
|
166
|
+
if (secondaryCanProceedWithCrud && !secondaryCanProceedWithCrud()) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
150
169
|
|
|
151
170
|
const secondarySelection = secondaryGetSelection();
|
|
152
171
|
let addValues = values;
|
|
@@ -165,20 +184,20 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
165
184
|
// 1. directlty submit 'values' to use in secondaryDoAdd(), or
|
|
166
185
|
// 2. Use the repository's default values (defined on each property as 'defaultValue'), or
|
|
167
186
|
// 3. Individually override the repository's default values with submitted 'defaultValues' (given as a prop to this HOC)
|
|
168
|
-
let
|
|
187
|
+
let secondaryDefaultValuesToUse = SecondaryRepository.getSchema().getDefaultValues();
|
|
169
188
|
if (secondaryDefaultValues) {
|
|
170
|
-
_.merge(
|
|
189
|
+
_.merge(secondaryDefaultValuesToUse, secondaryDefaultValues);
|
|
171
190
|
}
|
|
172
|
-
addValues = {...
|
|
191
|
+
addValues = {...secondaryDefaultValuesToUse};
|
|
173
192
|
}
|
|
174
193
|
|
|
175
194
|
if (secondarySelectorId && !_.isEmpty(secondarySelectorSelected)) {
|
|
176
195
|
addValues[secondarySelectorId] = secondarySelectorSelected[secondarySelectorSelectedField];
|
|
177
196
|
}
|
|
178
197
|
|
|
179
|
-
if (
|
|
198
|
+
if (secondaryGetNewEntityDisplayValue()) {
|
|
180
199
|
const displayPropertyName = secondaryNewEntityDisplayProperty || SecondaryRepository.getSchema().model.displayProperty;
|
|
181
|
-
addValues[displayPropertyName] =
|
|
200
|
+
addValues[displayPropertyName] = secondaryGetNewEntityDisplayValue();
|
|
182
201
|
}
|
|
183
202
|
|
|
184
203
|
if (getListeners().onBeforeAdd) {
|
|
@@ -246,6 +265,9 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
246
265
|
showPermissionsError(EDIT, secondaryModel);
|
|
247
266
|
return;
|
|
248
267
|
}
|
|
268
|
+
if (secondaryCanProceedWithCrud && !secondaryCanProceedWithCrud()) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
249
271
|
const secondarySelection = secondaryGetSelection();
|
|
250
272
|
if (_.isEmpty(secondarySelection) || (_.isArray(secondarySelection) && (secondarySelection.length > 1 || secondarySelection[0]?.isDestroyed))) {
|
|
251
273
|
return;
|
|
@@ -265,6 +287,9 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
265
287
|
showPermissionsError(DELETE, secondaryModel);
|
|
266
288
|
return;
|
|
267
289
|
}
|
|
290
|
+
if (secondaryCanProceedWithCrud && !secondaryCanProceedWithCrud()) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
268
293
|
let cb = null;
|
|
269
294
|
if (_.isFunction(args)) {
|
|
270
295
|
cb = args;
|
|
@@ -273,7 +298,15 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
273
298
|
if (_.isEmpty(secondarySelection) || (_.isArray(secondarySelection) && (secondarySelection.length > 1 || secondarySelection[0]?.isDestroyed))) {
|
|
274
299
|
return;
|
|
275
300
|
}
|
|
301
|
+
if (secondaryOnBeforeDelete) {
|
|
302
|
+
// This listener is set by parent components using a prop
|
|
303
|
+
const listenerResult = await secondaryOnBeforeDelete(secondarySelection);
|
|
304
|
+
if (listenerResult === false) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
276
308
|
if (getListeners().onBeforeDelete) {
|
|
309
|
+
// This listener is set by child components using setWithEditListeners()
|
|
277
310
|
const listenerResult = await getListeners().onBeforeDelete();
|
|
278
311
|
if (listenerResult === false) {
|
|
279
312
|
return;
|
|
@@ -309,26 +342,33 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
309
342
|
});
|
|
310
343
|
} else
|
|
311
344
|
if (isSingle && isPhantom) {
|
|
312
|
-
|
|
345
|
+
secondaryDeleteRecord(cb);
|
|
313
346
|
} else {
|
|
314
347
|
const identifier = secondaryGetRecordIdentifier(secondarySelection);
|
|
315
|
-
confirm('Are you sure you want to delete the ' + identifier, () =>
|
|
348
|
+
confirm('Are you sure you want to delete the ' + identifier, () => secondaryDeleteRecord(null, cb));
|
|
316
349
|
}
|
|
317
350
|
},
|
|
318
351
|
secondaryDoMoveChildren = (cb) => {
|
|
319
352
|
hideAlert();
|
|
320
|
-
|
|
353
|
+
secondaryDeleteRecord(true, cb);
|
|
321
354
|
},
|
|
322
355
|
secondaryDoDeleteChildren = (cb) => {
|
|
323
356
|
hideAlert();
|
|
324
|
-
|
|
357
|
+
secondaryDeleteRecord(false, cb);
|
|
325
358
|
},
|
|
326
|
-
|
|
359
|
+
secondaryDeleteRecord = async (moveSubtreeUp, cb) => {
|
|
327
360
|
if (canUser && !canUser(DELETE, secondaryModel)) {
|
|
328
361
|
showPermissionsError(DELETE, secondaryModel);
|
|
329
362
|
return;
|
|
330
363
|
}
|
|
331
364
|
const secondarySelection = secondaryGetSelection();
|
|
365
|
+
if (secondaryOnBeforeDelete) {
|
|
366
|
+
// This listener is set by parent components using a prop
|
|
367
|
+
const listenerResult = await secondaryOnBeforeDelete(secondarySelection);
|
|
368
|
+
if (listenerResult === false) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
332
372
|
if (getListeners().onBeforeDelete) {
|
|
333
373
|
const listenerResult = await getListeners().onBeforeDelete(secondarySelection);
|
|
334
374
|
if (listenerResult === false) {
|
|
@@ -362,6 +402,9 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
362
402
|
showPermissionsError(VIEW, secondaryModel);
|
|
363
403
|
return;
|
|
364
404
|
}
|
|
405
|
+
if (secondaryCanProceedWithCrud && !secondaryCanProceedWithCrud()) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
365
408
|
if (secondaryEditorType === EDITOR_TYPE__INLINE) {
|
|
366
409
|
alert('Cannot view in inline editor.');
|
|
367
410
|
return; // inline editor doesn't have a view mode
|
|
@@ -389,37 +432,70 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
389
432
|
showPermissionsError(DUPLICATE, secondaryModel);
|
|
390
433
|
return;
|
|
391
434
|
}
|
|
392
|
-
|
|
393
|
-
|
|
435
|
+
if (secondaryCanProceedWithCrud && !secondaryCanProceedWithCrud()) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
394
438
|
|
|
395
439
|
const secondarySelection = secondaryGetSelection();
|
|
396
440
|
if (secondarySelection.length !== 1) {
|
|
397
441
|
return;
|
|
398
442
|
}
|
|
443
|
+
|
|
399
444
|
if (secondaryUseRemoteDuplicate) {
|
|
400
|
-
|
|
401
|
-
|
|
445
|
+
return await secondaryOnRemoteDuplicate();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
let isSuccess = false,
|
|
449
|
+
duplicateEntity;
|
|
450
|
+
try {
|
|
451
|
+
const
|
|
452
|
+
entity = secondarySelection[0],
|
|
453
|
+
idProperty = SecondaryRepository.getSchema().model.idProperty,
|
|
454
|
+
rawValues = _.omit(entity.getOriginalData(), idProperty);
|
|
455
|
+
rawValues.id = null; // unset the id of the duplicate
|
|
456
|
+
|
|
457
|
+
setIsWaitModalShown(true);
|
|
458
|
+
|
|
459
|
+
duplicateEntity = await SecondaryRepository.add(rawValues, false, true);
|
|
460
|
+
isSuccess = true;
|
|
461
|
+
|
|
462
|
+
} catch(err) {
|
|
463
|
+
// do nothing
|
|
464
|
+
} finally {
|
|
465
|
+
setIsWaitModalShown(false);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (isSuccess) {
|
|
469
|
+
secondarySetIsIgnoreNextSelectionChange(true);
|
|
470
|
+
secondarySetSelection([duplicateEntity]);
|
|
471
|
+
secondarySetEditorMode(EDITOR_MODE__EDIT);
|
|
472
|
+
secondarySetIsEditorShown(true);
|
|
402
473
|
}
|
|
403
|
-
const
|
|
404
|
-
entity = secondarySelection[0],
|
|
405
|
-
idProperty = SecondaryRepository.getSchema().model.idProperty,
|
|
406
|
-
rawValues = _.omit(entity.getOriginalData(), idProperty);
|
|
407
|
-
rawValues.id = null; // unset the id of the duplicate
|
|
408
|
-
const duplicate = await SecondaryRepository.add(rawValues, false, true);
|
|
409
|
-
secondarySetIsIgnoreNextSelectionChange(true);
|
|
410
|
-
secondarySetSelection([duplicate]);
|
|
411
|
-
secondarySetEditorMode(EDITOR_MODE__EDIT);
|
|
412
|
-
secondarySetIsEditorShown(true);
|
|
413
474
|
},
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
475
|
+
secondaryOnRemoteDuplicate = async () => {
|
|
476
|
+
let isSuccess = false,
|
|
477
|
+
duplicateEntity;
|
|
478
|
+
try {
|
|
479
|
+
const
|
|
480
|
+
secondarySelection = secondaryGetSelection(),
|
|
481
|
+
entity = secondarySelection[0];
|
|
482
|
+
|
|
483
|
+
setIsWaitModalShown(true);
|
|
419
484
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
485
|
+
duplicateEntity = await SecondaryRepository.remoteDuplicate(entity);
|
|
486
|
+
isSuccess = true;
|
|
487
|
+
|
|
488
|
+
} catch(err) {
|
|
489
|
+
// do nothing
|
|
490
|
+
} finally {
|
|
491
|
+
setIsWaitModalShown(false);
|
|
492
|
+
}
|
|
493
|
+
if (isSuccess) {
|
|
494
|
+
secondarySetIsIgnoreNextSelectionChange(true);
|
|
495
|
+
secondarySetSelection([duplicateEntity]);
|
|
496
|
+
secondaryDoEdit();
|
|
497
|
+
return duplicateEntity;
|
|
498
|
+
}
|
|
423
499
|
},
|
|
424
500
|
secondaryDoEditorSave = async (data, e) => {
|
|
425
501
|
let mode = secondaryGetEditorMode() === EDITOR_MODE__ADD ? ADD : EDIT;
|
|
@@ -516,7 +592,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
516
592
|
isSingle = secondarySelection.length === 1,
|
|
517
593
|
isPhantom = secondarySelection[0] && !secondarySelection[0]?.isDestroyed && secondarySelection[0].isPhantom;
|
|
518
594
|
if (isSingle && isPhantom) {
|
|
519
|
-
await
|
|
595
|
+
await secondaryDeleteRecord();
|
|
520
596
|
}
|
|
521
597
|
|
|
522
598
|
setIsAdding(false);
|
|
@@ -551,7 +627,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
551
627
|
secondarySetIsEditorShown(false);
|
|
552
628
|
});
|
|
553
629
|
},
|
|
554
|
-
|
|
630
|
+
secondaryCalculateEditorMode = () => {
|
|
555
631
|
|
|
556
632
|
let secondaryIsIgnoreNextSelectionChange = secondaryGetIsIgnoreNextSelectionChange(),
|
|
557
633
|
doStayInEditModeOnSelectionChange = secondaryStayInEditModeOnSelectionChange;
|
|
@@ -563,7 +639,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
563
639
|
secondaryIsIgnoreNextSelectionChange = true;
|
|
564
640
|
}
|
|
565
641
|
|
|
566
|
-
//
|
|
642
|
+
// secondaryCalculateEditorMode gets called only on selection changes
|
|
567
643
|
const secondarySelection = secondaryGetSelection();
|
|
568
644
|
let mode;
|
|
569
645
|
if (secondaryEditorType === EDITOR_TYPE__SIDE && !_.isNil(UiGlobals.isSideEditorAlwaysEditMode) && UiGlobals.isSideEditorAlwaysEditMode) {
|
|
@@ -617,10 +693,26 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
617
693
|
};
|
|
618
694
|
|
|
619
695
|
useEffect(() => {
|
|
620
|
-
secondarySetEditorMode(calculateEditorMode());
|
|
621
696
|
|
|
622
|
-
|
|
697
|
+
if (secondaryEditorType === EDITOR_TYPE__SIDE) {
|
|
698
|
+
if (secondarySelection?.length) { // || isAdding
|
|
699
|
+
// there is a selection, so show the editor
|
|
700
|
+
secondarySetIsEditorShown(true);
|
|
701
|
+
} else {
|
|
702
|
+
// no selection, so close the editor
|
|
703
|
+
secondarySetIsEditorShown(false);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
secondarySetEditorMode(secondaryCalculateEditorMode());
|
|
623
708
|
setLastSelection(secondarySelection);
|
|
709
|
+
|
|
710
|
+
// Push isIgnoreNextSelectionChange until after a microtask to ensure all
|
|
711
|
+
// synchronous operations (including listener callbacks) are complete
|
|
712
|
+
// (this is to prevent the editor from immediately switching modes on doAdd in Tree)
|
|
713
|
+
Promise.resolve().then(() => {
|
|
714
|
+
secondarySetIsIgnoreNextSelectionChange(false);
|
|
715
|
+
});
|
|
624
716
|
}, [secondarySelection]);
|
|
625
717
|
|
|
626
718
|
if (self) {
|
|
@@ -638,21 +730,23 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
638
730
|
// NOTE: If I don't calculate this on the fly for selection changes,
|
|
639
731
|
// we see a flash of the previous state, since useEffect hasn't yet run.
|
|
640
732
|
// (basically redo what's in the useEffect, above)
|
|
641
|
-
secondarySetEditorMode(
|
|
733
|
+
secondarySetEditorMode(secondaryCalculateEditorMode());
|
|
642
734
|
}
|
|
643
735
|
|
|
644
736
|
return <WrappedComponent
|
|
645
737
|
{...props}
|
|
738
|
+
ref={ref}
|
|
646
739
|
secondaryDisableWithEditor={false}
|
|
647
740
|
secondaryAlreadyHasWithEditor={true}
|
|
648
|
-
ref={ref}
|
|
649
741
|
secondaryCurrentRecord={secondaryCurrentRecord}
|
|
650
742
|
secondarySetCurrentRecord={secondarySetCurrentRecord}
|
|
651
|
-
secondaryIsEditorShown={
|
|
743
|
+
secondaryIsEditorShown={secondaryGetIsEditorShown()}
|
|
744
|
+
secondaryGetIsEditorShown={secondaryGetIsEditorShown}
|
|
652
745
|
secondaryIsEditorViewOnly={secondaryIsEditorViewOnly}
|
|
653
746
|
secondaryIsAdding={secondaryIsAdding}
|
|
654
747
|
secondaryIsSaving={secondaryIsSaving}
|
|
655
748
|
secondaryEditorMode={secondaryGetEditorMode()}
|
|
749
|
+
secondaryGetEditorMode={secondaryGetEditorMode}
|
|
656
750
|
secondaryOnEditMode={secondarySetEditMode}
|
|
657
751
|
secondaryOnViewMode={secondarySetViewMode}
|
|
658
752
|
secondaryEditorStateRef={secondaryEditorStateRef}
|
|
@@ -47,11 +47,12 @@ export default function withSecondarySideEditor(WrappedComponent, isTree = false
|
|
|
47
47
|
throw Error('SecondaryEditor is not defined');
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
const containerProps = {};
|
|
50
51
|
if (isResizable) {
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
containerProps.eastIsResizable = true;
|
|
53
|
+
containerProps.eastInitialWidth = 500;
|
|
53
54
|
} else {
|
|
54
|
-
|
|
55
|
+
containerProps.eastInitialFlex = secondarySideFlex;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
if (!secondaryEditorProps.className) {
|
|
@@ -68,13 +69,14 @@ export default function withSecondarySideEditor(WrappedComponent, isTree = false
|
|
|
68
69
|
isSideEditor={true}
|
|
69
70
|
{...props}
|
|
70
71
|
/>}
|
|
71
|
-
east={<Editor
|
|
72
|
+
east={props.secondaryIsEditorShown && <Editor
|
|
72
73
|
{...propsToPass}
|
|
73
74
|
editorType={EDITOR_TYPE__SIDE}
|
|
74
75
|
{...secondaryEditorProps}
|
|
75
76
|
parent={self}
|
|
76
77
|
reference="secondaryEditor"
|
|
77
78
|
/>}
|
|
79
|
+
{...containerProps}
|
|
78
80
|
/>;
|
|
79
81
|
});
|
|
80
82
|
return withAdditionalProps(withSecondaryEditor(SideEditor, isTree));
|
|
@@ -95,9 +95,8 @@ export default function withContextMenu(WrappedComponent) {
|
|
|
95
95
|
action="secondary"
|
|
96
96
|
/>;
|
|
97
97
|
});
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
contextMenuItemComponents.push(<Text key="idViewer" className="flex-1 py-2 px-4 select-none">id: {selection?.[0]?.id}</Text>);
|
|
98
|
+
if (UiGlobals.isLocal) {
|
|
99
|
+
contextMenuItemComponents.push(<Text key="idViewer" className="flex-1 py-2 px-4 select-none">id: {selection?.[0]?.actualId || selection?.[0]?.id}</Text>);
|
|
101
100
|
}
|
|
102
101
|
return contextMenuItemComponents;
|
|
103
102
|
};
|
|
@@ -122,9 +121,19 @@ export default function withContextMenu(WrappedComponent) {
|
|
|
122
121
|
// may change based on the selection; and this is why we're using
|
|
123
122
|
// useEffect to show the context menu instead of onContextMenu.
|
|
124
123
|
|
|
124
|
+
// TODO: There might be a bug here. As the comment above suggests, useEffect()
|
|
125
|
+
// was running if contextMenuItems changed. But the comment next to the args
|
|
126
|
+
// for useEffect() says we're not including contextMenuItems in the args
|
|
127
|
+
// to avoid infinite loops. Is this a problem??
|
|
128
|
+
|
|
129
|
+
const contextMenuItemComponents = createContextMenuItemComponents();
|
|
130
|
+
if (contextMenuItemComponents.length === 0) {
|
|
131
|
+
// No items to show
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
125
135
|
// show context menu
|
|
126
136
|
const
|
|
127
|
-
contextMenuItemComponents = createContextMenuItemComponents(),
|
|
128
137
|
className = clsx(
|
|
129
138
|
'context-menu-container',
|
|
130
139
|
'absolute',
|
|
@@ -14,8 +14,6 @@ import {
|
|
|
14
14
|
} from 'uuid';
|
|
15
15
|
import getComponentFromType from '../../Functions/getComponentFromType.js';
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
17
|
// Note on modes:
|
|
20
18
|
// HORIZONTAL means the component moves along the X axis.
|
|
21
19
|
// VERTICAL means the component moves along the Y axis.
|
|
@@ -62,6 +60,8 @@ export default function withDraggable(WrappedComponent) {
|
|
|
62
60
|
if (isDragging) {
|
|
63
61
|
return;
|
|
64
62
|
}
|
|
63
|
+
|
|
64
|
+
// console.log('start x', info.x);
|
|
65
65
|
|
|
66
66
|
const
|
|
67
67
|
node = getDraggableNodeFromNode(info.node),
|
|
@@ -132,6 +132,9 @@ export default function withDraggable(WrappedComponent) {
|
|
|
132
132
|
deltaY,
|
|
133
133
|
} = info;
|
|
134
134
|
|
|
135
|
+
|
|
136
|
+
// console.log('drag x', info.x);
|
|
137
|
+
|
|
135
138
|
// Move the proxy to where it should be
|
|
136
139
|
const
|
|
137
140
|
proxy = document.getElementById('dragproxy'),
|
|
@@ -157,7 +160,8 @@ export default function withDraggable(WrappedComponent) {
|
|
|
157
160
|
return;
|
|
158
161
|
}
|
|
159
162
|
|
|
160
|
-
// console.log('end', info);
|
|
163
|
+
// console.log('end x', info.x);
|
|
164
|
+
|
|
161
165
|
// remove proxy
|
|
162
166
|
const proxy = document.getElementById('dragproxy');
|
|
163
167
|
proxy.remove();
|