@onehat/ui 0.4.64 → 0.4.66
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/ContainerColumn.js +21 -0
- package/src/Components/Form/Field/Combo/Combo.js +1 -0
- package/src/Components/Form/Field/Json.js +3 -3
- package/src/Components/Grid/Grid.js +17 -5
- package/src/Components/Grid/GridHeaderRow.js +11 -1
- package/src/Components/Grid/GridRow.js +11 -3
- package/src/Components/Grid/RowDragHandle.js +4 -5
- package/src/Components/Grid/RowSelectHandle.js +18 -0
- package/src/Components/Hoc/withAlert.js +4 -0
- package/src/Components/Hoc/withDnd.js +54 -57
- package/src/Components/Icons/Arcs.js +10 -0
- package/src/Components/Icons/ArrowPointer.js +11 -0
- package/src/Components/Layout/AsyncOperation.js +36 -6
- package/src/Components/Report/Report.js +54 -0
- package/src/Components/Tree/Tree.js +312 -310
- package/src/Components/Tree/TreeNode.js +89 -15
- package/src/Components/Tree/TreeNodeDragHandle.js +20 -0
- package/src/Components/index.js +6 -0
- package/src/Constants/Styles.js +2 -0
- package/src/PlatformImports/Web/Attachments.js +19 -4
package/package.json
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
VStack,
|
|
3
|
+
} from '@project-components/Gluestack';
|
|
4
|
+
import _ from 'lodash';
|
|
5
|
+
|
|
6
|
+
// This component allows us to stack multiple children in a Container slot (e.g. east)
|
|
7
|
+
// such that the ContainerColumn can be passed props like isResizable,
|
|
8
|
+
// which the Container will translate into classNames for the VStack component.
|
|
9
|
+
|
|
10
|
+
export default function ContainerColumn(props) {
|
|
11
|
+
let className = `
|
|
12
|
+
ContainerColumn
|
|
13
|
+
`;
|
|
14
|
+
if (props.className) {
|
|
15
|
+
className += ` ${props.className}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return <VStack className={className}>
|
|
19
|
+
{props.children}
|
|
20
|
+
</VStack>;
|
|
21
|
+
}
|
|
@@ -767,6 +767,7 @@ export const ComboComponent = forwardRef((props, ref) => {
|
|
|
767
767
|
newEntityDisplayProperty={newEntityDisplayProperty}
|
|
768
768
|
disablePresetButtons={!isEditor}
|
|
769
769
|
alternateRowBackgrounds={false}
|
|
770
|
+
showSelectHandle={false}
|
|
770
771
|
onChangeSelection={(selection) => {
|
|
771
772
|
|
|
772
773
|
if (Repository && selection[0]?.isPhantom) {
|
|
@@ -46,12 +46,12 @@ export function JsonElement(props) {
|
|
|
46
46
|
${testID}
|
|
47
47
|
`;
|
|
48
48
|
if (props.className) {
|
|
49
|
-
className += ' ' +
|
|
49
|
+
className += ' ' + propsToPass.className;
|
|
50
50
|
}
|
|
51
51
|
// if (UiGlobals.mode === UI_MODE_WEB) {
|
|
52
52
|
const src = value ? JSON.parse(value) : {};
|
|
53
53
|
assembledComponents =
|
|
54
|
-
<HStack style={
|
|
54
|
+
<HStack style={propsToPass.style} className={className}>
|
|
55
55
|
<JsonEditor
|
|
56
56
|
width="100%"
|
|
57
57
|
editable={!isViewOnly}
|
|
@@ -61,7 +61,7 @@ export function JsonElement(props) {
|
|
|
61
61
|
onEdit={(obj) => {
|
|
62
62
|
setValue(JSON.stringify(obj.updated_src));
|
|
63
63
|
}}
|
|
64
|
-
{...
|
|
64
|
+
{...propsToPass}
|
|
65
65
|
/>
|
|
66
66
|
</HStack>;
|
|
67
67
|
// }
|
|
@@ -132,6 +132,9 @@ function GridComponent(props) {
|
|
|
132
132
|
getExpandedRowContent,
|
|
133
133
|
showHeaders = true,
|
|
134
134
|
showHovers = true,
|
|
135
|
+
showSelectHandle = true,
|
|
136
|
+
isRowSelectable = true,
|
|
137
|
+
isRowHoverable = true,
|
|
135
138
|
canColumnsSort = true,
|
|
136
139
|
canColumnsReorder = true,
|
|
137
140
|
canColumnsResize = true,
|
|
@@ -424,10 +427,10 @@ function GridComponent(props) {
|
|
|
424
427
|
} else {
|
|
425
428
|
let canDoEdit = false,
|
|
426
429
|
canDoView = false;
|
|
427
|
-
if (onEdit && canUser && canUser(EDIT) && (!canRecordBeEdited || canRecordBeEdited(selection))) {
|
|
430
|
+
if (onEdit && canUser && canUser(EDIT) && (!canRecordBeEdited || canRecordBeEdited(selection)) && !props.disableEdit) {
|
|
428
431
|
canDoEdit = true;
|
|
429
432
|
} else
|
|
430
|
-
if (onView && canUser && canUser(VIEW)) {
|
|
433
|
+
if (onView && canUser && canUser(VIEW) && !props.disableView) {
|
|
431
434
|
canDoView = true;
|
|
432
435
|
}
|
|
433
436
|
|
|
@@ -472,7 +475,13 @@ function GridComponent(props) {
|
|
|
472
475
|
onContextMenu(item, e, newSelection);
|
|
473
476
|
}
|
|
474
477
|
}}
|
|
475
|
-
className=
|
|
478
|
+
className={`
|
|
479
|
+
Pressable
|
|
480
|
+
Row
|
|
481
|
+
flex-row
|
|
482
|
+
grow
|
|
483
|
+
`}
|
|
484
|
+
>
|
|
476
485
|
{({
|
|
477
486
|
hovered,
|
|
478
487
|
focused,
|
|
@@ -493,6 +502,7 @@ function GridComponent(props) {
|
|
|
493
502
|
isInlineEditorShown={isInlineEditorShown}
|
|
494
503
|
areRowsDragSource={areRowsDragSource}
|
|
495
504
|
showColumnsSelector={showColumnsSelector}
|
|
505
|
+
showSelectHandle={showSelectHandle}
|
|
496
506
|
/>;
|
|
497
507
|
if (showRowExpander) {
|
|
498
508
|
// align the header row to content rows by adding a spacer that matches the width of the Grid-rowExpander-expandBtn
|
|
@@ -503,12 +513,11 @@ function GridComponent(props) {
|
|
|
503
513
|
}
|
|
504
514
|
return headerRow;
|
|
505
515
|
}
|
|
506
|
-
|
|
507
516
|
const
|
|
508
517
|
rowReorderProps = {},
|
|
509
518
|
rowDragProps = {};
|
|
510
519
|
let WhichRow = GridRow;
|
|
511
|
-
if (CURRENT_MODE === UI_MODE_WEB) { // DND is
|
|
520
|
+
if (CURRENT_MODE === UI_MODE_WEB) { // DND is currently web-only TODO: implement for RN
|
|
512
521
|
// Create a method that gets an always-current copy of the selection ids
|
|
513
522
|
dragSelectionRef.current = selection;
|
|
514
523
|
const getSelection = () => dragSelectionRef.current;
|
|
@@ -567,9 +576,12 @@ function GridComponent(props) {
|
|
|
567
576
|
fields={fields}
|
|
568
577
|
rowProps={rowProps}
|
|
569
578
|
hideNavColumn={hideNavColumn}
|
|
579
|
+
isRowSelectable={isRowSelectable}
|
|
580
|
+
isRowHoverable={isRowHoverable}
|
|
570
581
|
isSelected={isSelected}
|
|
571
582
|
isHovered={hovered}
|
|
572
583
|
showHovers={showHovers}
|
|
584
|
+
showSelectHandle={showSelectHandle}
|
|
573
585
|
index={index}
|
|
574
586
|
alternatingInterval={alternatingInterval}
|
|
575
587
|
alternateRowBackgrounds={alternateRowBackgrounds}
|
|
@@ -20,6 +20,7 @@ import UiGlobals from '../../UiGlobals.js';
|
|
|
20
20
|
import useBlocking from '../../Hooks/useBlocking.js';
|
|
21
21
|
import testProps from '../../Functions/testProps.js';
|
|
22
22
|
import AngleRight from '../Icons/AngleRight.js';
|
|
23
|
+
import Arcs from '../Icons/Arcs.js';
|
|
23
24
|
import HeaderReorderHandle from './HeaderReorderHandle.js';
|
|
24
25
|
import HeaderResizeHandle from './HeaderResizeHandle.js';
|
|
25
26
|
import HeaderColumnSelectorHandle from './HeaderColumnSelectorHandle.js';
|
|
@@ -46,6 +47,7 @@ export default function GridHeaderRow(props) {
|
|
|
46
47
|
isInlineEditorShown,
|
|
47
48
|
areRowsDragSource,
|
|
48
49
|
showColumnsSelector,
|
|
50
|
+
showSelectHandle,
|
|
49
51
|
} = props,
|
|
50
52
|
styles = UiGlobals.styles,
|
|
51
53
|
sortFn = Repository && Repository.getSortFn(),
|
|
@@ -462,10 +464,18 @@ export default function GridHeaderRow(props) {
|
|
|
462
464
|
/>}
|
|
463
465
|
</Pressable>;
|
|
464
466
|
});
|
|
467
|
+
if (showSelectHandle) {
|
|
468
|
+
headerColumns.unshift(<Box
|
|
469
|
+
key="RowSelectHandle"
|
|
470
|
+
className="Spacer-RowSelectHandle px-2 items-center justify-center flex-none w-[40px]"
|
|
471
|
+
>
|
|
472
|
+
<Icon as={Arcs} className={`Arcs w-[20px] h-[20px] text-[#aaa]`} />
|
|
473
|
+
</Box>);
|
|
474
|
+
}
|
|
465
475
|
if (areRowsDragSource) {
|
|
466
476
|
headerColumns.unshift(<Box
|
|
467
477
|
key="spacer"
|
|
468
|
-
className="Spacer w-[
|
|
478
|
+
className="Spacer w-[7px]"
|
|
469
479
|
/>);
|
|
470
480
|
}
|
|
471
481
|
if (!hideNavColumn) {
|
|
@@ -16,6 +16,7 @@ import { withDragSource, withDropTarget } from '../Hoc/withDnd.js';
|
|
|
16
16
|
import testProps from '../../Functions/testProps.js';
|
|
17
17
|
import AngleRight from '../Icons/AngleRight.js';
|
|
18
18
|
import RowDragHandle from './RowDragHandle.js';
|
|
19
|
+
import RowSelectHandle from './RowSelectHandle.js';
|
|
19
20
|
import _ from 'lodash';
|
|
20
21
|
|
|
21
22
|
// This was broken out from Grid simply so we can memoize it
|
|
@@ -27,6 +28,9 @@ function GridRow(props) {
|
|
|
27
28
|
fields,
|
|
28
29
|
rowProps,
|
|
29
30
|
hideNavColumn,
|
|
31
|
+
showSelectHandle,
|
|
32
|
+
isRowSelectable,
|
|
33
|
+
isRowHoverable,
|
|
30
34
|
isSelected,
|
|
31
35
|
isHovered,
|
|
32
36
|
bg,
|
|
@@ -55,13 +59,13 @@ function GridRow(props) {
|
|
|
55
59
|
|
|
56
60
|
let bg = rowProps.bg || props.bg || styles.GRID_ROW_BG,
|
|
57
61
|
mixWith;
|
|
58
|
-
if (isSelected) {
|
|
62
|
+
if (isRowSelectable && isSelected) {
|
|
59
63
|
if (showHovers && isHovered) {
|
|
60
64
|
mixWith = styles.GRID_ROW_SELECTED_BG_HOVER;
|
|
61
65
|
} else {
|
|
62
66
|
mixWith = styles.GRID_ROW_SELECTED_BG;
|
|
63
67
|
}
|
|
64
|
-
} else if (showHovers && isHovered) {
|
|
68
|
+
} else if (isRowHoverable && showHovers && isHovered) {
|
|
65
69
|
mixWith = styles.GRID_ROW_BG_HOVER;
|
|
66
70
|
} else if (alternateRowBackgrounds && index % alternatingInterval === 0) { // i.e. every second line, or every third line
|
|
67
71
|
mixWith = styles.GRID_ROW_ALTERNATE_BG;
|
|
@@ -85,7 +89,8 @@ function GridRow(props) {
|
|
|
85
89
|
}
|
|
86
90
|
const
|
|
87
91
|
propsToPass = columnProps[key] || {},
|
|
88
|
-
colStyle = {}
|
|
92
|
+
colStyle = {},
|
|
93
|
+
whichCursor = showSelectHandle ? 'cursor-text' : 'cursor-pointer'; // when using rowSelectHandle, indicate that the row text is selectable, otherwise indicate that the row itself is selectable
|
|
89
94
|
let colClassName = `
|
|
90
95
|
GridRow-column
|
|
91
96
|
p-1
|
|
@@ -93,6 +98,7 @@ function GridRow(props) {
|
|
|
93
98
|
border-r-black-100
|
|
94
99
|
block
|
|
95
100
|
overflow-auto
|
|
101
|
+
${whichCursor}
|
|
96
102
|
${styles.GRID_ROW_MAX_HEIGHT_EXTRA}
|
|
97
103
|
`;
|
|
98
104
|
if (isOnlyOneVisibleColumn) {
|
|
@@ -265,6 +271,8 @@ function GridRow(props) {
|
|
|
265
271
|
|
|
266
272
|
let rowContents = <>
|
|
267
273
|
{(isDragSource || isDraggable) && <RowDragHandle />}
|
|
274
|
+
{showSelectHandle && <RowSelectHandle />}
|
|
275
|
+
|
|
268
276
|
{isPhantom &&
|
|
269
277
|
<Box
|
|
270
278
|
className={`
|
|
@@ -5,11 +5,10 @@ import {
|
|
|
5
5
|
import styles from '../../Styles/StyleSheets.js';
|
|
6
6
|
import GripVertical from '../Icons/GripVertical.js';
|
|
7
7
|
|
|
8
|
-
function RowDragHandle(props) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
>
|
|
8
|
+
function RowDragHandle(props) { return <VStack
|
|
9
|
+
style={styles.ewResize}
|
|
10
|
+
className="RowDragHandle bg-grey-100 w-[7px] items-center justify-center select-none"
|
|
11
|
+
>
|
|
13
12
|
<Icon
|
|
14
13
|
as={GripVertical}
|
|
15
14
|
size="xs"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Icon,
|
|
3
|
+
VStack,
|
|
4
|
+
} from '@project-components/Gluestack';
|
|
5
|
+
import Arcs from '../Icons/Arcs.js';
|
|
6
|
+
|
|
7
|
+
function RowSelectHandle(props) {
|
|
8
|
+
return <VStack
|
|
9
|
+
className="RowSelectHandle w-[40px] px-2 items-center justify-center select-none cursor-pointer"
|
|
10
|
+
>
|
|
11
|
+
<Icon
|
|
12
|
+
as={Arcs}
|
|
13
|
+
size="xs"
|
|
14
|
+
className="w-[20px] h-[20px] text-[#ddd]" />
|
|
15
|
+
</VStack>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default RowSelectHandle;
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { forwardRef, useEffect, useRef, } from 'react';
|
|
2
2
|
import { useDrag, useDrop, useDragLayer } from 'react-dnd'; // https://react-dnd.github.io/react-dnd/about don't forget the wrapping <DndProvider /> as shown here: https://react-dnd.github.io/react-dnd/docs/api/dnd-provider
|
|
3
|
-
|
|
3
|
+
import {
|
|
4
|
+
Box,
|
|
5
|
+
} from '@project-components/Gluestack';
|
|
6
|
+
import {
|
|
7
|
+
UI_MODE_WEB,
|
|
8
|
+
UI_MODE_NATIVE,
|
|
9
|
+
CURRENT_MODE,
|
|
10
|
+
} from '../../Constants/UiModes.js';
|
|
4
11
|
|
|
5
12
|
// This HOC allows components to be dragged and dropped onto another component.
|
|
6
13
|
// It can't contrain the movement of the preview item, because react-dnd uses
|
|
@@ -32,6 +39,7 @@ export function withDragSource(WrappedComponent) {
|
|
|
32
39
|
onDragEnd = null,
|
|
33
40
|
canDrag = null,
|
|
34
41
|
isDragging = null,
|
|
42
|
+
getDragProxy,
|
|
35
43
|
dragCollect = (monitor, props2) => { // Optional. The collecting function. It should return a plain object of the props to return for injection into your component. It receives two parameters, monitor and props. Read the overview for an introduction to the monitors and the collecting function. See the collecting function described in detail in the next section.
|
|
36
44
|
// monitor fn determines which props from dnd state get passed
|
|
37
45
|
return {
|
|
@@ -54,7 +62,10 @@ export function withDragSource(WrappedComponent) {
|
|
|
54
62
|
|
|
55
63
|
return {
|
|
56
64
|
type: dragSourceType, // Required. This must be either a string or a symbol. Only the drop targets registered for the same type will react to this item.
|
|
57
|
-
item:
|
|
65
|
+
item: {
|
|
66
|
+
...dragSourceItem,
|
|
67
|
+
getDragProxy,
|
|
68
|
+
}, // Required (object or function).
|
|
58
69
|
// When an object, it is a plain JavaScript object describing the data being dragged. This is the only information available to the drop targets about the drag source so it's important to pick the minimal data they need to know. You may be tempted to put a complex reference here, but you should try very hard to avoid doing this because it couples the drag sources and drop targets. It's a good idea to use something like { id }.
|
|
59
70
|
// When a function, it is fired at the beginning of the drag operation and returns an object representing the drag operation (see first bullet). If null is returned, the drag operation is cancelled.
|
|
60
71
|
previewOptions: dragPreviewOptions, // Optional. A plain JavaScript object describing drag preview options.
|
|
@@ -139,6 +150,7 @@ export function withDropTarget(WrappedComponent) {
|
|
|
139
150
|
return {
|
|
140
151
|
canDrop: !!monitor.canDrop(),
|
|
141
152
|
isOver: !!monitor.isOver(),
|
|
153
|
+
draggedItem: monitor.getItem(), // Pass the dragged item so TreeNode can evaluate custom logic
|
|
142
154
|
};
|
|
143
155
|
},
|
|
144
156
|
} = props,
|
|
@@ -171,6 +183,7 @@ export function withDropTarget(WrappedComponent) {
|
|
|
171
183
|
{
|
|
172
184
|
canDrop: stateCanDrop,
|
|
173
185
|
isOver,
|
|
186
|
+
draggedItem,
|
|
174
187
|
// didDrop,
|
|
175
188
|
// clientOffset,
|
|
176
189
|
// differenceFromInitialOffset,
|
|
@@ -187,64 +200,48 @@ export function withDropTarget(WrappedComponent) {
|
|
|
187
200
|
ref={ref}
|
|
188
201
|
isOver={isOver}
|
|
189
202
|
dropTargetRef={localTargetRef}
|
|
203
|
+
draggedItem={draggedItem} // Pass the dragged item
|
|
190
204
|
{...props}
|
|
191
205
|
/>;
|
|
192
206
|
});
|
|
193
207
|
}
|
|
194
208
|
|
|
195
209
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
//
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
// return (
|
|
236
|
-
// <div id="dragLayer" style={{
|
|
237
|
-
// background: '#f00',
|
|
238
|
-
// position: 'fixed',
|
|
239
|
-
// pointerEvents: 'none',
|
|
240
|
-
// zIndex: 10000,
|
|
241
|
-
// width: '200px',
|
|
242
|
-
// height: '10px',
|
|
243
|
-
// left: 0,
|
|
244
|
-
// top: 0,
|
|
245
|
-
// transform,
|
|
246
|
-
// }}>
|
|
247
|
-
// {children}
|
|
248
|
-
// </div>
|
|
249
|
-
// );
|
|
250
|
-
// }
|
|
210
|
+
export function GlobalDragProxy() {
|
|
211
|
+
const {
|
|
212
|
+
isDragging,
|
|
213
|
+
item,
|
|
214
|
+
currentOffset,
|
|
215
|
+
} = useDragLayer((monitor) => ({
|
|
216
|
+
isDragging: monitor.isDragging(),
|
|
217
|
+
item: monitor.getItem(),
|
|
218
|
+
currentOffset: monitor.getSourceClientOffset(),
|
|
219
|
+
}));
|
|
220
|
+
|
|
221
|
+
if (!isDragging || !currentOffset || CURRENT_MODE !== UI_MODE_WEB) { // Native uses a native drag layer, so we don't need to render a custom proxy
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const getDragProxy = item?.getDragProxy;
|
|
226
|
+
if (!getDragProxy) {
|
|
227
|
+
// Only render a custom proxy if one is provided - let React DnD handle default case
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let proxyContent = null;
|
|
232
|
+
try {
|
|
233
|
+
proxyContent = getDragProxy(item);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.warn('Failed to render custom drag proxy:', error);
|
|
236
|
+
return null; // use default React DnD proxy
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return <Box
|
|
240
|
+
className="fixed pointer-events-none z-[10000] left-0 top-0"
|
|
241
|
+
style={{
|
|
242
|
+
transform: `translate(${currentOffset.x}px, ${currentOffset.y}px)`,
|
|
243
|
+
}}
|
|
244
|
+
>
|
|
245
|
+
{proxyContent}
|
|
246
|
+
</Box>;
|
|
247
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createIcon } from "../Gluestack/icon";
|
|
2
|
+
import { Path, Svg } from 'react-native-svg';
|
|
3
|
+
|
|
4
|
+
const SvgComponent = createIcon({
|
|
5
|
+
Root: Svg,
|
|
6
|
+
viewBox: '0 0 53.98 53.98',
|
|
7
|
+
path: <Path d="M14.59 0c1.1 1.66 1.74 3.65 1.74 5.79 0 5.82-4.72 10.54-10.54 10.54-2.14 0-4.13-.64-5.79-1.74 1.89 2.86 5.12 4.75 8.8 4.75 5.82 0 10.54-4.72 10.54-10.54 0-3.68-1.89-6.92-4.75-8.8zM49.23 0c1.1 1.66 1.74 3.65 1.74 5.79 0 5.82-4.72 10.54-10.54 10.54-2.14 0-4.13-.64-5.79-1.74 1.89 2.86 5.12 4.75 8.8 4.75 5.82 0 10.54-4.72 10.54-10.54 0-3.68-1.89-6.92-4.75-8.8zM14.59 34.64c1.1 1.66 1.74 3.65 1.74 5.79 0 5.82-4.72 10.54-10.54 10.54-2.14 0-4.13-.64-5.79-1.74 1.89 2.86 5.12 4.75 8.8 4.75 5.82 0 10.54-4.72 10.54-10.54 0-3.68-1.89-6.92-4.75-8.8zM49.23 34.64c1.1 1.66 1.74 3.65 1.74 5.79 0 5.82-4.72 10.54-10.54 10.54-2.14 0-4.13-.64-5.79-1.74 1.89 2.86 5.12 4.75 8.8 4.75 5.82 0 10.54-4.72 10.54-10.54 0-3.68-1.89-6.92-4.75-8.8z" />,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export default SvgComponent
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createIcon } from "../Gluestack/icon";
|
|
2
|
+
// Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc.
|
|
3
|
+
import Svg, { Path } from "react-native-svg"
|
|
4
|
+
|
|
5
|
+
const SvgComponent = createIcon({
|
|
6
|
+
Root: Svg,
|
|
7
|
+
viewBox: '0 0 320 512',
|
|
8
|
+
path: <Path d="M0 55.2V426c0 12.2 9.9 22 22 22 6.3 0 12.4-2.7 16.6-7.5l82.6-94.5 58.1 116.3c7.9 15.8 27.1 22.2 42.9 14.3s22.2-27.1 14.3-42.9L179.8 320h118.1c12.2 0 22.1-9.9 22.1-22.1 0-6.3-2.7-12.3-7.4-16.5L38.6 37.9c-4.3-3.8-9.7-5.9-15.4-5.9C10.4 32 0 42.4 0 55.2z" />,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export default SvgComponent
|
|
@@ -23,6 +23,11 @@ import Panel from '../Panel/Panel.js';
|
|
|
23
23
|
import Toolbar from '../Toolbar/Toolbar.js';
|
|
24
24
|
import _ from 'lodash';
|
|
25
25
|
|
|
26
|
+
const
|
|
27
|
+
INITIATE = 'INITIATE',
|
|
28
|
+
PROCESSING = 'PROCESSING',
|
|
29
|
+
RESULTS = 'RESULTS';
|
|
30
|
+
|
|
26
31
|
// NOTE: This component assumes you have an AppSlice, that has
|
|
27
32
|
// an 'operationsInProgress' state var and a 'setOperationsInProgress' action.
|
|
28
33
|
|
|
@@ -37,6 +42,7 @@ function AsyncOperation(props) {
|
|
|
37
42
|
Repository,
|
|
38
43
|
formItems = [],
|
|
39
44
|
formStartingValues = {},
|
|
45
|
+
_form = {},
|
|
40
46
|
getProgressUpdates = false,
|
|
41
47
|
parseProgress, // optional fn, accepts 'response' as arg and returns progress string
|
|
42
48
|
progressStuckThreshold = null, // e.g. 3, if left blank, doesn't check for stuck state
|
|
@@ -49,10 +55,25 @@ function AsyncOperation(props) {
|
|
|
49
55
|
alert,
|
|
50
56
|
} = props,
|
|
51
57
|
dispatch = useDispatch(),
|
|
58
|
+
isValid = useRef(true),
|
|
59
|
+
setIsValid = (valid) => {
|
|
60
|
+
isValid.current = valid;
|
|
61
|
+
},
|
|
62
|
+
getIsValid = () => {
|
|
63
|
+
return isValid.current;
|
|
64
|
+
},
|
|
65
|
+
mode = useRef(INITIATE),
|
|
66
|
+
setMode = (newMode) => {
|
|
67
|
+
mode.current = newMode;
|
|
68
|
+
},
|
|
69
|
+
getMode = () => {
|
|
70
|
+
return mode.current;
|
|
71
|
+
},
|
|
52
72
|
initiate = async () => {
|
|
53
73
|
|
|
54
74
|
clearProgress();
|
|
55
|
-
|
|
75
|
+
setMode(PROCESSING);
|
|
76
|
+
setFooter(getFooter());
|
|
56
77
|
setIsInProgress(true);
|
|
57
78
|
|
|
58
79
|
const
|
|
@@ -85,17 +106,18 @@ function AsyncOperation(props) {
|
|
|
85
106
|
}
|
|
86
107
|
showResults(results);
|
|
87
108
|
},
|
|
88
|
-
getFooter = (which =
|
|
109
|
+
getFooter = (which = getMode()) => {
|
|
89
110
|
switch(which) {
|
|
90
|
-
case
|
|
111
|
+
case INITIATE:
|
|
91
112
|
return <Toolbar>
|
|
92
113
|
<Button
|
|
93
114
|
text="Start"
|
|
94
115
|
rightIcon={ChevronRight}
|
|
95
116
|
onPress={() => initiate()}
|
|
117
|
+
isDisabled={!getIsValid()}
|
|
96
118
|
/>
|
|
97
119
|
</Toolbar>;
|
|
98
|
-
case
|
|
120
|
+
case PROCESSING:
|
|
99
121
|
return <Toolbar>
|
|
100
122
|
<Button
|
|
101
123
|
text="Please wait"
|
|
@@ -103,7 +125,7 @@ function AsyncOperation(props) {
|
|
|
103
125
|
variant="link"
|
|
104
126
|
/>
|
|
105
127
|
</Toolbar>;
|
|
106
|
-
case
|
|
128
|
+
case RESULTS:
|
|
107
129
|
return <Toolbar>
|
|
108
130
|
<Button
|
|
109
131
|
text="Reset"
|
|
@@ -152,7 +174,8 @@ function AsyncOperation(props) {
|
|
|
152
174
|
},
|
|
153
175
|
showResults = (results) => {
|
|
154
176
|
setCurrentTab(1);
|
|
155
|
-
|
|
177
|
+
setMode(RESULTS);
|
|
178
|
+
setFooter(getFooter());
|
|
156
179
|
setResults(results);
|
|
157
180
|
getProgress();
|
|
158
181
|
},
|
|
@@ -206,6 +229,7 @@ function AsyncOperation(props) {
|
|
|
206
229
|
},
|
|
207
230
|
resetToInitialState = () => {
|
|
208
231
|
setCurrentTab(0);
|
|
232
|
+
setMode(INITIATE);
|
|
209
233
|
setFooter(getFooter());
|
|
210
234
|
clearProgress();
|
|
211
235
|
},
|
|
@@ -227,6 +251,10 @@ function AsyncOperation(props) {
|
|
|
227
251
|
},
|
|
228
252
|
});
|
|
229
253
|
},
|
|
254
|
+
onValidityChange = (isValid) => {
|
|
255
|
+
setIsValid(isValid);
|
|
256
|
+
setFooter(getFooter());
|
|
257
|
+
},
|
|
230
258
|
unchangedProgressCount = getUnchangedProgressCount();
|
|
231
259
|
|
|
232
260
|
useEffect(() => {
|
|
@@ -255,6 +283,8 @@ function AsyncOperation(props) {
|
|
|
255
283
|
disableFooter={true}
|
|
256
284
|
items={formItems}
|
|
257
285
|
startingValues={formStartingValues}
|
|
286
|
+
onValidityChange={onValidityChange}
|
|
287
|
+
{..._form}
|
|
258
288
|
/>,
|
|
259
289
|
},
|
|
260
290
|
{
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Box,
|
|
4
4
|
HStack,
|
|
5
5
|
Icon,
|
|
6
|
+
Pressable,
|
|
6
7
|
Text,
|
|
7
8
|
VStack,
|
|
8
9
|
VStackNative,
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
REPORT_TYPES__PDF,
|
|
18
19
|
} from '../../Constants/ReportTypes.js';
|
|
19
20
|
import Form from '../Form/Form.js';
|
|
21
|
+
import IconButton from '../Buttons/IconButton.js';
|
|
20
22
|
import withComponent from '../Hoc/withComponent.js';
|
|
21
23
|
import withAlert from '../Hoc/withAlert.js';
|
|
22
24
|
import testProps from '../../Functions/testProps.js';
|
|
@@ -37,9 +39,19 @@ function Report(props) {
|
|
|
37
39
|
disablePdf = false,
|
|
38
40
|
disableExcel = false,
|
|
39
41
|
showReportHeaders = true,
|
|
42
|
+
isQuickReport = false,
|
|
43
|
+
quickReportData = {},
|
|
40
44
|
alert,
|
|
41
45
|
} = props,
|
|
42
46
|
buttons = [],
|
|
47
|
+
onPressQuickReport = () => {
|
|
48
|
+
downloadReport({
|
|
49
|
+
reportId,
|
|
50
|
+
reportType: REPORT_TYPES__EXCEL,
|
|
51
|
+
showReportHeaders,
|
|
52
|
+
data: quickReportData,
|
|
53
|
+
});
|
|
54
|
+
},
|
|
43
55
|
downloadReport = (args) => {
|
|
44
56
|
getReport(args);
|
|
45
57
|
alert('Download started');
|
|
@@ -59,6 +71,48 @@ function Report(props) {
|
|
|
59
71
|
icon = <Icon as={icon} {...propsIcon} />;
|
|
60
72
|
}
|
|
61
73
|
|
|
74
|
+
if (isQuickReport) {
|
|
75
|
+
let className = `
|
|
76
|
+
Report
|
|
77
|
+
max-w-[100px]
|
|
78
|
+
m-2
|
|
79
|
+
`;
|
|
80
|
+
if (props.className) {
|
|
81
|
+
className += ' ' + props.className;
|
|
82
|
+
}
|
|
83
|
+
return <VStackNative
|
|
84
|
+
{...testProps('QuickReport-' + reportId)}
|
|
85
|
+
className={className}
|
|
86
|
+
>
|
|
87
|
+
<Pressable
|
|
88
|
+
onPress={onPressQuickReport}
|
|
89
|
+
className={`
|
|
90
|
+
flex-1
|
|
91
|
+
items-center
|
|
92
|
+
justify-center
|
|
93
|
+
flex-col
|
|
94
|
+
bg-white
|
|
95
|
+
p-3
|
|
96
|
+
rounded-lg
|
|
97
|
+
border
|
|
98
|
+
border-primary-300
|
|
99
|
+
hover:bg-primary-300
|
|
100
|
+
`}
|
|
101
|
+
>
|
|
102
|
+
{icon}
|
|
103
|
+
<Text
|
|
104
|
+
className={`
|
|
105
|
+
text-black
|
|
106
|
+
text-center
|
|
107
|
+
text-[17px]
|
|
108
|
+
leading-tight
|
|
109
|
+
mt-2
|
|
110
|
+
`}
|
|
111
|
+
>{title}</Text>
|
|
112
|
+
</Pressable>
|
|
113
|
+
</VStackNative>;
|
|
114
|
+
}
|
|
115
|
+
|
|
62
116
|
if (!disableExcel) {
|
|
63
117
|
buttons.push({
|
|
64
118
|
...testProps('excelBtn'),
|