@onehat/ui 0.4.83 → 0.4.85
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/Container/Container.js +336 -147
- package/src/Components/Form/Field/Date.js +1 -1
- package/src/Components/Form/Field/Hidden.js +13 -0
- package/src/Components/Form/Field/Tag/Tag.js +0 -3
- package/src/Components/Form/Form.js +2 -2
- package/src/Components/Grid/Grid.js +75 -42
- package/src/Components/Grid/GridRow.js +8 -1
- package/src/Components/Hoc/Secondary/withSecondarySideEditor.js +5 -3
- 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/Tree/Tree.js +24 -0
- package/src/Components/index.js +2 -0
- package/src/Constants/Progress.js +6 -1
- package/src/PlatformImports/Web/Attachments.js +3 -3
|
@@ -205,7 +205,7 @@ function Form(props) {
|
|
|
205
205
|
|
|
206
206
|
// Fallback to empty schema that allows any fields and defaults to valid
|
|
207
207
|
return yup.object().noUnknown(false).default({});
|
|
208
|
-
})(),
|
|
208
|
+
})() || yup.object().shape({}), // on rare occasions, validatorToUse was null. This fixes it
|
|
209
209
|
{
|
|
210
210
|
control,
|
|
211
211
|
formState,
|
|
@@ -1549,7 +1549,7 @@ function Form(props) {
|
|
|
1549
1549
|
function disableRequiredYupFields(validator) {
|
|
1550
1550
|
// based on https://github.com/jquense/yup/issues/1466#issuecomment-944386480
|
|
1551
1551
|
if (!validator) {
|
|
1552
|
-
return null
|
|
1552
|
+
return yup.object().shape({}); // Return valid empty schema instead of null
|
|
1553
1553
|
}
|
|
1554
1554
|
|
|
1555
1555
|
const nextSchema = validator.clone();
|
|
@@ -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
|
|
|
@@ -147,6 +148,7 @@ function GridComponent(props) {
|
|
|
147
148
|
showSelectHandle = true,
|
|
148
149
|
isRowSelectable = true,
|
|
149
150
|
isRowHoverable = true,
|
|
151
|
+
isDisabled = false,
|
|
150
152
|
canColumnsSort = true,
|
|
151
153
|
canColumnsReorder = true,
|
|
152
154
|
canColumnsResize = true,
|
|
@@ -522,6 +524,33 @@ function GridComponent(props) {
|
|
|
522
524
|
onContextMenu(item, e, newSelection);
|
|
523
525
|
}
|
|
524
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
|
+
}}
|
|
525
554
|
className={clsx(
|
|
526
555
|
'Pressable',
|
|
527
556
|
'Row',
|
|
@@ -1679,51 +1708,55 @@ function GridComponent(props) {
|
|
|
1679
1708
|
className += ' ' + props.className;
|
|
1680
1709
|
}
|
|
1681
1710
|
|
|
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
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
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
|
+
)}
|
|
1720
1754
|
</VStack>
|
|
1721
|
-
)}
|
|
1722
|
-
</VStack>
|
|
1723
1755
|
|
|
1724
|
-
|
|
1756
|
+
{listFooterComponent}
|
|
1725
1757
|
|
|
1726
|
-
|
|
1758
|
+
</VStackNative>;
|
|
1759
|
+
}
|
|
1727
1760
|
|
|
1728
1761
|
if (isDropTarget) {
|
|
1729
1762
|
grid = <VStackNative
|
|
@@ -286,7 +286,7 @@ const GridRow = forwardRef((props, ref) => {
|
|
|
286
286
|
'block',
|
|
287
287
|
areCellsScrollable ? 'overflow-auto' : 'overflow-hidden',
|
|
288
288
|
'[&::-webkit-scrollbar]:h-2',
|
|
289
|
-
'[&::-webkit-scrollbar-thumb]:bg-gray-
|
|
289
|
+
'[&::-webkit-scrollbar-thumb]:bg-gray-300',
|
|
290
290
|
'[&::-webkit-scrollbar-thumb]:rounded-full',
|
|
291
291
|
colClassName,
|
|
292
292
|
styles.GRID_CELL_CLASSNAME,
|
|
@@ -350,10 +350,17 @@ const GridRow = forwardRef((props, ref) => {
|
|
|
350
350
|
if (config.getCellProps) {
|
|
351
351
|
_.assign(elementProps, config.getCellProps(item));
|
|
352
352
|
}
|
|
353
|
+
|
|
354
|
+
// TODO: incorporate better scrollbar formatting with
|
|
355
|
+
// tailwind plugin 'tailwind-scrollbar' (already installed, just not yet used here)
|
|
356
|
+
|
|
353
357
|
let textClassName = clsx(
|
|
354
358
|
'GridRow-TextNative',
|
|
355
359
|
'self-center',
|
|
356
360
|
areCellsScrollable ? 'overflow-auto' : 'overflow-hidden',
|
|
361
|
+
'[&::-webkit-scrollbar]:h-2',
|
|
362
|
+
'[&::-webkit-scrollbar-thumb]:bg-gray-300',
|
|
363
|
+
'[&::-webkit-scrollbar-thumb]:rounded-full',
|
|
357
364
|
colClassName,
|
|
358
365
|
styles.GRID_CELL_CLASSNAME,
|
|
359
366
|
styles.GRID_ROW_MAX_HEIGHT_EXTRA,
|
|
@@ -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) {
|
|
@@ -75,6 +76,7 @@ export default function withSecondarySideEditor(WrappedComponent, isTree = false
|
|
|
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();
|
|
@@ -45,11 +45,12 @@ export default function withSideEditor(WrappedComponent, isTree = false) {
|
|
|
45
45
|
throw Error('Editor is not defined');
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
const containerProps = {};
|
|
48
49
|
if (isResizable) {
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
containerProps.eastIsResizable = true;
|
|
51
|
+
containerProps.eastInitialWidth = 500;
|
|
51
52
|
} else {
|
|
52
|
-
|
|
53
|
+
containerProps.eastInitialFlex = sideFlex;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
if (!_editor.className) {
|
|
@@ -61,10 +62,10 @@ export default function withSideEditor(WrappedComponent, isTree = false) {
|
|
|
61
62
|
parent={self}
|
|
62
63
|
reference="SideEditor"
|
|
63
64
|
center={<WrappedComponent
|
|
65
|
+
{...props}
|
|
64
66
|
ref={ref}
|
|
65
67
|
isTree={isTree}
|
|
66
68
|
isSideEditor={true}
|
|
67
|
-
{...props}
|
|
68
69
|
/>}
|
|
69
70
|
east={props.isEditorShown && <Editor
|
|
70
71
|
{...propsToPass}
|
|
@@ -73,6 +74,8 @@ export default function withSideEditor(WrappedComponent, isTree = false) {
|
|
|
73
74
|
parent={self}
|
|
74
75
|
reference="editor"
|
|
75
76
|
/>}
|
|
77
|
+
{...containerProps}
|
|
78
|
+
isDisabled={props.isDisabled}
|
|
76
79
|
/>;
|
|
77
80
|
});
|
|
78
81
|
return withAdditionalProps(withEditor(SideEditor, isTree));
|
|
@@ -9,6 +9,9 @@ import {
|
|
|
9
9
|
import clsx from 'clsx';
|
|
10
10
|
import * as Progress from 'react-native-progress';
|
|
11
11
|
import useForceUpdate from '../../Hooks/useForceUpdate';
|
|
12
|
+
import {
|
|
13
|
+
EDITOR_TYPE__PLAIN,
|
|
14
|
+
} from '../../Constants/Editor.js';
|
|
12
15
|
import {
|
|
13
16
|
PROGRESS__NONE_FOUND,
|
|
14
17
|
PROGRESS__IN_PROCESS,
|
|
@@ -16,10 +19,15 @@ import {
|
|
|
16
19
|
PROGRESS__FAILED,
|
|
17
20
|
PROGRESS__STUCK,
|
|
18
21
|
PROGRESS__UNSTUCK,
|
|
22
|
+
ASYNC_OPERATION_MODES__INIT,
|
|
23
|
+
ASYNC_OPERATION_MODES__START,
|
|
24
|
+
ASYNC_OPERATION_MODES__PROCESSING,
|
|
25
|
+
ASYNC_OPERATION_MODES__RESULTS,
|
|
19
26
|
} from '../../Constants/Progress.js';
|
|
20
27
|
import {
|
|
21
28
|
MOMENT_DATE_FORMAT_2,
|
|
22
29
|
} from '../../Constants/Dates.js';
|
|
30
|
+
import inArray from '../../Functions/inArray.js';
|
|
23
31
|
import isJson from '../../Functions/isJson.js';
|
|
24
32
|
import Form from '../Form/Form.js';
|
|
25
33
|
import Button from '../Buttons/Button.js';
|
|
@@ -38,11 +46,12 @@ import Toolbar from '../Toolbar/Toolbar.js';
|
|
|
38
46
|
import moment from 'moment';
|
|
39
47
|
import _ from 'lodash';
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
// MODES:
|
|
50
|
+
// ASYNC_OPERATION_MODES__INIT - no footer shown; used when component initially loads, to see if operation is already in progress
|
|
51
|
+
// ASYNC_OPERATION_MODES__START - shows the start form
|
|
52
|
+
// ASYNC_OPERATION_MODES__PROCESSING - shows the loading indicator while starting the operation
|
|
53
|
+
// ASYNC_OPERATION_MODES__RESULTS - shows the results of the operation, or any in-progress updates
|
|
54
|
+
|
|
46
55
|
|
|
47
56
|
// If getProgressUpdates is false, the component will show the start form initially.
|
|
48
57
|
// If getProgressUpdates is true, the component will initially query the server to see if
|
|
@@ -63,9 +72,11 @@ function AsyncOperation(props) {
|
|
|
63
72
|
formStartingValues = {},
|
|
64
73
|
_form = {},
|
|
65
74
|
getProgressUpdates = false,
|
|
75
|
+
getInitialProgress = true, // applies only if getProgressUpdates is true
|
|
66
76
|
parseProgress, // optional fn, accepts 'response' as arg and returns an object like this: { status, errors, started, lastUpdated, timeElapsed, count, current, total, percentage }
|
|
67
77
|
updateInterval = 10000, // ms
|
|
68
78
|
progressColor = '#666',
|
|
79
|
+
onChangeMode,
|
|
69
80
|
|
|
70
81
|
// withComponent
|
|
71
82
|
self,
|
|
@@ -81,15 +92,18 @@ function AsyncOperation(props) {
|
|
|
81
92
|
getIsValid = () => {
|
|
82
93
|
return isValid.current;
|
|
83
94
|
},
|
|
84
|
-
|
|
85
|
-
setMode = (
|
|
86
|
-
|
|
95
|
+
modeRef = useRef(ASYNC_OPERATION_MODES__INIT),
|
|
96
|
+
setMode = (mode) => {
|
|
97
|
+
modeRef.current = mode;
|
|
98
|
+
|
|
99
|
+
if (onChangeMode) {
|
|
100
|
+
onChangeMode(mode);
|
|
101
|
+
}
|
|
87
102
|
},
|
|
88
103
|
getMode = () => {
|
|
89
|
-
return
|
|
104
|
+
return modeRef.current;
|
|
90
105
|
},
|
|
91
|
-
isInProcess = getMode() ===
|
|
92
|
-
currentTabIx = (getMode() === PROCESSING ? 1 : (getMode() === RESULTS ? 2 : 0)),
|
|
106
|
+
isInProcess = getMode() === ASYNC_OPERATION_MODES__PROCESSING,
|
|
93
107
|
intervalRef = useRef(null),
|
|
94
108
|
getInterval = () => {
|
|
95
109
|
return intervalRef.current;
|
|
@@ -113,9 +127,9 @@ function AsyncOperation(props) {
|
|
|
113
127
|
},
|
|
114
128
|
getFooter = () => {
|
|
115
129
|
switch(getMode()) {
|
|
116
|
-
case
|
|
130
|
+
case ASYNC_OPERATION_MODES__INIT:
|
|
117
131
|
return null;
|
|
118
|
-
case
|
|
132
|
+
case ASYNC_OPERATION_MODES__START:
|
|
119
133
|
return <Toolbar>
|
|
120
134
|
<Button
|
|
121
135
|
text="Start"
|
|
@@ -124,7 +138,7 @@ function AsyncOperation(props) {
|
|
|
124
138
|
isDisabled={!getIsValid()}
|
|
125
139
|
/>
|
|
126
140
|
</Toolbar>;
|
|
127
|
-
case
|
|
141
|
+
case ASYNC_OPERATION_MODES__PROCESSING:
|
|
128
142
|
// TODO: Add a cancellation option to the command.
|
|
129
143
|
// would require a backend controller action to support it
|
|
130
144
|
return null;
|
|
@@ -135,7 +149,7 @@ function AsyncOperation(props) {
|
|
|
135
149
|
// variant="link"
|
|
136
150
|
// />
|
|
137
151
|
// </Toolbar>;
|
|
138
|
-
case
|
|
152
|
+
case ASYNC_OPERATION_MODES__RESULTS:
|
|
139
153
|
let button;
|
|
140
154
|
if (getIsStuck()) {
|
|
141
155
|
button = <Button
|
|
@@ -160,13 +174,13 @@ function AsyncOperation(props) {
|
|
|
160
174
|
[progress, setProgress] = useState(null),
|
|
161
175
|
[isReady, setIsReady] = useState(false),
|
|
162
176
|
showResults = (results) => {
|
|
163
|
-
setMode(
|
|
177
|
+
setMode(ASYNC_OPERATION_MODES__RESULTS);
|
|
164
178
|
setFooter(getFooter());
|
|
165
179
|
setResults(results);
|
|
166
180
|
},
|
|
167
181
|
startProcess = async () => {
|
|
168
182
|
stopGettingProgress();
|
|
169
|
-
setMode(
|
|
183
|
+
setMode(ASYNC_OPERATION_MODES__PROCESSING);
|
|
170
184
|
setFooter(getFooter());
|
|
171
185
|
|
|
172
186
|
const
|
|
@@ -264,11 +278,11 @@ function AsyncOperation(props) {
|
|
|
264
278
|
statusMessage = '',
|
|
265
279
|
errorMessage = null;
|
|
266
280
|
if (status === PROGRESS__IN_PROCESS) {
|
|
267
|
-
setMode(
|
|
281
|
+
setMode(ASYNC_OPERATION_MODES__PROCESSING);
|
|
268
282
|
color = 'text-green-600';
|
|
269
283
|
statusMessage = 'In process...';
|
|
270
284
|
} else {
|
|
271
|
-
setMode(
|
|
285
|
+
setMode(ASYNC_OPERATION_MODES__RESULTS);
|
|
272
286
|
stopGettingProgress();
|
|
273
287
|
if (status === PROGRESS__COMPLETED) {
|
|
274
288
|
statusMessage = 'Completed';
|
|
@@ -328,7 +342,7 @@ function AsyncOperation(props) {
|
|
|
328
342
|
})}
|
|
329
343
|
</VStack>);
|
|
330
344
|
}
|
|
331
|
-
if (getMode() ===
|
|
345
|
+
if (getMode() === ASYNC_OPERATION_MODES__PROCESSING) {
|
|
332
346
|
setProgress(renderItems);
|
|
333
347
|
} else {
|
|
334
348
|
setResults(renderItems);
|
|
@@ -351,7 +365,7 @@ function AsyncOperation(props) {
|
|
|
351
365
|
},
|
|
352
366
|
unstick = async () => {
|
|
353
367
|
stopGettingProgress();
|
|
354
|
-
setMode(
|
|
368
|
+
setMode(ASYNC_OPERATION_MODES__PROCESSING);
|
|
355
369
|
setFooter(getFooter());
|
|
356
370
|
|
|
357
371
|
const
|
|
@@ -379,7 +393,7 @@ function AsyncOperation(props) {
|
|
|
379
393
|
resetToInitialState();
|
|
380
394
|
},
|
|
381
395
|
resetToInitialState = () => {
|
|
382
|
-
setMode(
|
|
396
|
+
setMode(ASYNC_OPERATION_MODES__START);
|
|
383
397
|
setFooter(getFooter());
|
|
384
398
|
setIsStuck(false);
|
|
385
399
|
stopGettingProgress();
|
|
@@ -394,10 +408,10 @@ function AsyncOperation(props) {
|
|
|
394
408
|
};
|
|
395
409
|
|
|
396
410
|
useEffect(() => {
|
|
397
|
-
if (getProgressUpdates) {
|
|
411
|
+
if (getProgressUpdates && getInitialProgress) {
|
|
398
412
|
getProgress(true);
|
|
399
413
|
} else {
|
|
400
|
-
setMode(
|
|
414
|
+
setMode(ASYNC_OPERATION_MODES__START);
|
|
401
415
|
setIsReady(true);
|
|
402
416
|
}
|
|
403
417
|
return () => {
|
|
@@ -409,6 +423,20 @@ function AsyncOperation(props) {
|
|
|
409
423
|
};
|
|
410
424
|
}, []);
|
|
411
425
|
|
|
426
|
+
let currentTabIx = 0;
|
|
427
|
+
switch(getMode()) {
|
|
428
|
+
case ASYNC_OPERATION_MODES__INIT:
|
|
429
|
+
case ASYNC_OPERATION_MODES__START:
|
|
430
|
+
currentTabIx = 0;
|
|
431
|
+
break;
|
|
432
|
+
case ASYNC_OPERATION_MODES__PROCESSING:
|
|
433
|
+
currentTabIx = 1;
|
|
434
|
+
break;
|
|
435
|
+
case ASYNC_OPERATION_MODES__RESULTS:
|
|
436
|
+
currentTabIx = 2;
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
|
|
412
440
|
return <Panel
|
|
413
441
|
{...props}
|
|
414
442
|
footer={footer}
|
|
@@ -421,10 +449,11 @@ function AsyncOperation(props) {
|
|
|
421
449
|
title: 'Start',
|
|
422
450
|
icon: Play,
|
|
423
451
|
isDisabled: currentTabIx !== 0,
|
|
424
|
-
content: getMode()
|
|
452
|
+
content: inArray(getMode(), [ASYNC_OPERATION_MODES__INIT, ASYNC_OPERATION_MODES__PROCESSING, ASYNC_OPERATION_MODES__RESULTS]) ?
|
|
425
453
|
<Loading /> :
|
|
426
454
|
<ScrollView className="ScrollView h-full w-full">
|
|
427
455
|
<Form
|
|
456
|
+
editorType={EDITOR_TYPE__PLAIN}
|
|
428
457
|
reference="form"
|
|
429
458
|
parent={self}
|
|
430
459
|
className="w-full h-full flex-1"
|
|
@@ -2,20 +2,7 @@ import {
|
|
|
2
2
|
Box,
|
|
3
3
|
} from '@project-components/Gluestack';
|
|
4
4
|
import clsx from 'clsx';
|
|
5
|
-
import {
|
|
6
|
-
CURRENT_MODE,
|
|
7
|
-
UI_MODE_WEB,
|
|
8
|
-
UI_MODE_NATIVE,
|
|
9
|
-
} from '../../Constants/UiModes.js';
|
|
10
5
|
|
|
11
6
|
export default function Mask(props) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return <div className="mask"></div>;
|
|
15
|
-
|
|
16
|
-
} else if (CURRENT_MODE === UI_MODE_NATIVE) {
|
|
17
|
-
|
|
18
|
-
return <Box className="absolute h-full w-full bg-grey-400:alpha.20 z-100000"></Box>;
|
|
19
|
-
|
|
20
|
-
}
|
|
7
|
+
return <Box className="mask flex-none absolute h-full w-full bg-grey-400/20 z-[100000]"></Box>;
|
|
21
8
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { cloneElement, useState, useEffect, } from 'react';
|
|
1
2
|
import {
|
|
2
3
|
VStackNative,
|
|
3
4
|
} from '@project-components/Gluestack';
|
|
4
5
|
import clsx from 'clsx';
|
|
5
|
-
import React, { useState, useEffect, } from 'react';
|
|
6
6
|
import {
|
|
7
7
|
SCREEN_MODES__FULL,
|
|
8
8
|
SCREEN_MODES__SIDE,
|
|
@@ -21,10 +21,9 @@ function ManagerScreen(props) {
|
|
|
21
21
|
sideModeComponent,
|
|
22
22
|
fullModeComponent,
|
|
23
23
|
onChangeMode,
|
|
24
|
-
|
|
25
|
-
// withComponent
|
|
26
|
-
self,
|
|
24
|
+
...propsToPass
|
|
27
25
|
} = props,
|
|
26
|
+
self = props.self,
|
|
28
27
|
id = props.id || props.self?.path,
|
|
29
28
|
[isRendered, setIsRendered] = useState(false),
|
|
30
29
|
[isModeSet, setIsModeSet] = useState(false),
|
|
@@ -79,7 +78,7 @@ function ManagerScreen(props) {
|
|
|
79
78
|
self.mode = actualMode;
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
const whichComponent = actualMode === SCREEN_MODES__FULL ? fullModeComponent : sideModeComponent;
|
|
81
|
+
const whichComponent = cloneElement((actualMode === SCREEN_MODES__FULL ? fullModeComponent : sideModeComponent), propsToPass);
|
|
83
82
|
|
|
84
83
|
return <VStackNative
|
|
85
84
|
{...testProps(self)}
|
|
@@ -1106,6 +1106,30 @@ function TreeComponent(props) {
|
|
|
1106
1106
|
onContextMenu(item, e, selection);
|
|
1107
1107
|
}
|
|
1108
1108
|
}}
|
|
1109
|
+
onContextMenu={(e) => {
|
|
1110
|
+
// web only; happens before onLongPress triggers
|
|
1111
|
+
// different behavior here than onLongPress:
|
|
1112
|
+
// if user clicks on a phantom record, or if onContextMenu is not set, pass to the browser's context menu
|
|
1113
|
+
if (selection && selection[0] && selection[0].isRemotePhantom) {
|
|
1114
|
+
return; // block context menu or changing selection when a remote phantom is already selected
|
|
1115
|
+
}
|
|
1116
|
+
if (onContextMenu) {
|
|
1117
|
+
e.preventDefault();
|
|
1118
|
+
e.stopPropagation(); // disallow browser's default behavior for context menu
|
|
1119
|
+
|
|
1120
|
+
// if the right-clicked item is not in the current selection,
|
|
1121
|
+
// set the selection only to this one item.
|
|
1122
|
+
let newSelection = selection;
|
|
1123
|
+
if (!isInSelection(item)) {
|
|
1124
|
+
newSelection = [item];
|
|
1125
|
+
setSelection(newSelection);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
if (onContextMenu) {
|
|
1129
|
+
onContextMenu(item, e, newSelection);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}}
|
|
1109
1133
|
className={clsx(
|
|
1110
1134
|
'Pressable',
|
|
1111
1135
|
'Node',
|
package/src/Components/index.js
CHANGED
|
@@ -264,6 +264,7 @@ import FiltersForm from './Form/FiltersForm.js';
|
|
|
264
264
|
import Form from './Form/Form.js';
|
|
265
265
|
import Grid from './Grid/Grid.js';
|
|
266
266
|
import GridPanel from './Panel/GridPanel.js';
|
|
267
|
+
import Hidden from './Form/Field/Hidden.js';
|
|
267
268
|
import IconButton from './Buttons/IconButton.js';
|
|
268
269
|
import Input from './Form/Field/Input.js';
|
|
269
270
|
import IntervalsCombo from './Form/Field/Combo/IntervalsCombo.js';
|
|
@@ -558,6 +559,7 @@ const components = {
|
|
|
558
559
|
Form,
|
|
559
560
|
Grid,
|
|
560
561
|
GridPanel,
|
|
562
|
+
Hidden,
|
|
561
563
|
IconButton,
|
|
562
564
|
Input,
|
|
563
565
|
IntervalsCombo,
|
|
@@ -3,4 +3,9 @@ export const PROGRESS__IN_PROCESS = 'IN_PROCESS';
|
|
|
3
3
|
export const PROGRESS__COMPLETED = 'COMPLETED';
|
|
4
4
|
export const PROGRESS__FAILED = 'FAILED';
|
|
5
5
|
export const PROGRESS__STUCK = 'STUCK';
|
|
6
|
-
export const PROGRESS__UNSTUCK = 'UNSTUCK';
|
|
6
|
+
export const PROGRESS__UNSTUCK = 'UNSTUCK';
|
|
7
|
+
|
|
8
|
+
export const ASYNC_OPERATION_MODES__INIT = 'INIT';
|
|
9
|
+
export const ASYNC_OPERATION_MODES__START = 'START';
|
|
10
|
+
export const ASYNC_OPERATION_MODES__PROCESSING = 'PROCESSING';
|
|
11
|
+
export const ASYNC_OPERATION_MODES__RESULTS = 'RESULTS';
|
|
@@ -474,9 +474,9 @@ function AttachmentsElement(props) {
|
|
|
474
474
|
},
|
|
475
475
|
isPdf = currentFile.attachments__mimetype === 'application/pdf';
|
|
476
476
|
|
|
477
|
-
let url = currentFile.attachments__uri;
|
|
477
|
+
let url = encodeURI(currentFile.attachments__uri);
|
|
478
478
|
try {
|
|
479
|
-
const response = await fetch(
|
|
479
|
+
const response = await fetch(url, {
|
|
480
480
|
headers: Attachments.headers // Use your repository's headers
|
|
481
481
|
});
|
|
482
482
|
|
|
@@ -1019,7 +1019,7 @@ function AttachmentsElement(props) {
|
|
|
1019
1019
|
},
|
|
1020
1020
|
{
|
|
1021
1021
|
"id": "attachments__size_formatted",
|
|
1022
|
-
"header": "Size
|
|
1022
|
+
"header": "Size",
|
|
1023
1023
|
"fieldName": "attachments__size_formatted",
|
|
1024
1024
|
"isSortable": false,
|
|
1025
1025
|
"isEditable": false,
|