@onehat/ui 0.4.26 → 0.4.29
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/Accordion/Accordion.js +4 -1
- package/src/Components/Buttons/SquareButton.js +20 -16
- package/src/Components/Form/Field/Color.js +1 -0
- package/src/Components/Form/Field/Combo/Combo.js +4 -4
- package/src/Components/Form/Field/Date.js +1 -0
- package/src/Components/Form/Field/Input.js +1 -1
- package/src/Components/Form/Field/Number.js +1 -0
- package/src/Components/Form/Field/Slider.js +1 -0
- package/src/Components/Form/Form.js +4 -1
- package/src/Components/Grid/Grid.js +1 -1
- package/src/Components/Hoc/Secondary/withSecondaryEditor.js +39 -10
- package/src/Components/Hoc/Secondary/withSecondarySideEditor.js +23 -7
- package/src/Components/Hoc/Secondary/withSecondaryWindowedEditor.js +23 -17
- package/src/Components/Hoc/withFilters.js +42 -1
- package/src/Components/Hoc/withModal.js +2 -1
- package/src/Components/Hoc/withPdfButtons.js +7 -1
- package/src/Components/Icons/ClipboardCheck.js +2 -2
- package/src/Components/Icons/LowPriority.js +1 -1
- package/src/Components/Layout/CenterBox.js +3 -1
- package/src/Components/Layout/ScreenHeader.js +73 -0
- package/src/Components/Layout/TextWithTooltip.js +22 -0
- package/src/Components/Panel/Header.js +2 -0
- package/src/Components/Panel/Panel.js +8 -3
- package/src/Components/Screens/Manager.js +19 -51
- package/src/Components/Screens/ReportsManager.js +62 -0
- package/src/Components/Toolbar/Pagination.js +1 -0
- package/src/Components/Tree/Tree.js +7 -1
- package/src/Components/Tree/TreeNode.js +3 -1
- package/src/Constants/ScreenModes.js +2 -0
- package/src/Functions/Cypress/dom_functions.js +43 -10
- package/src/Styles/Global.css +2 -2
package/package.json
CHANGED
|
@@ -126,7 +126,10 @@ export default function Accordion(props) {
|
|
|
126
126
|
return <ScrollView
|
|
127
127
|
ref={scrollViewRef}
|
|
128
128
|
keyboardShouldPersistTaps="always"
|
|
129
|
-
className="flex-1 w-full"
|
|
129
|
+
className="Accordion-ScrollView flex-1 w-full"
|
|
130
|
+
contentContainerStyle={{
|
|
131
|
+
height: '100%',
|
|
132
|
+
}}
|
|
130
133
|
>
|
|
131
134
|
<VStackNative
|
|
132
135
|
{...propsToPass}
|
|
@@ -7,14 +7,13 @@ export default function SquareButton(props) {
|
|
|
7
7
|
const {
|
|
8
8
|
text,
|
|
9
9
|
isActive = false,
|
|
10
|
-
|
|
10
|
+
activeClassName,
|
|
11
11
|
invertColorWhenActive = false,
|
|
12
12
|
showText = true,
|
|
13
13
|
disableInteractions = false,
|
|
14
14
|
fontSize = '20px',
|
|
15
15
|
...propsToPass
|
|
16
16
|
} = props,
|
|
17
|
-
bg = isActive ? activeColor || '#56a6f8' : '#fff',
|
|
18
17
|
color = invertColorWhenActive && isActive ? '#fff' : '#000';
|
|
19
18
|
|
|
20
19
|
if (!props.icon) {
|
|
@@ -24,21 +23,26 @@ export default function SquareButton(props) {
|
|
|
24
23
|
throw Error('text missing. If you want to hide the text, use showText={false}');
|
|
25
24
|
}
|
|
26
25
|
|
|
26
|
+
let className = `
|
|
27
|
+
SquareButton
|
|
28
|
+
rounded-md
|
|
29
|
+
p-2
|
|
30
|
+
h-[100px]
|
|
31
|
+
w-[100px]
|
|
32
|
+
flex
|
|
33
|
+
flex-col
|
|
34
|
+
justify-center
|
|
35
|
+
items-center
|
|
36
|
+
bg-grey-200
|
|
37
|
+
hover:bg-grey-400
|
|
38
|
+
disabled:bg-grey-100
|
|
39
|
+
`;
|
|
40
|
+
if (isActive && activeClassName) {
|
|
41
|
+
className += ' ' + activeClassName;
|
|
42
|
+
}
|
|
43
|
+
|
|
27
44
|
return <IconButton
|
|
28
|
-
className={
|
|
29
|
-
SquareButton
|
|
30
|
-
rounded-md
|
|
31
|
-
p-2
|
|
32
|
-
bg-[${bg}]
|
|
33
|
-
hover:bg-[${bg}]
|
|
34
|
-
disabled:bg-[${bg}]
|
|
35
|
-
h-[100px]
|
|
36
|
-
w-[100px]
|
|
37
|
-
flex
|
|
38
|
-
flex-col
|
|
39
|
-
justify-center
|
|
40
|
-
items-center
|
|
41
|
-
`}
|
|
45
|
+
className={className}
|
|
42
46
|
style={{
|
|
43
47
|
// backgroundColor: bg,
|
|
44
48
|
}}
|
|
@@ -182,6 +182,7 @@ export function ColorElement(props) {
|
|
|
182
182
|
border-bottom-right-radius-6
|
|
183
183
|
${styles.FORM_COLOR_INPUT_CLASSNAME}
|
|
184
184
|
`}
|
|
185
|
+
textAlignIsCenter={true}
|
|
185
186
|
onLayout={(e) => {
|
|
186
187
|
// On web, this is not needed, but on RN it might be, so leave it in for now
|
|
187
188
|
const {
|
|
@@ -577,6 +577,7 @@ export function ComboComponent(props) {
|
|
|
577
577
|
input = disableDirectEntry ?
|
|
578
578
|
<Pressable
|
|
579
579
|
{...testProps('toggleMenuBtn')}
|
|
580
|
+
ref={inputRef}
|
|
580
581
|
onPress={toggleMenu}
|
|
581
582
|
className={`
|
|
582
583
|
Combo-toggleMenuBtn
|
|
@@ -587,7 +588,8 @@ export function ComboComponent(props) {
|
|
|
587
588
|
justify-center
|
|
588
589
|
items-center
|
|
589
590
|
m-0
|
|
590
|
-
p-
|
|
591
|
+
p-2
|
|
592
|
+
bg-white
|
|
591
593
|
border
|
|
592
594
|
border-grey-400
|
|
593
595
|
rounded-r-none
|
|
@@ -596,13 +598,10 @@ export function ComboComponent(props) {
|
|
|
596
598
|
>
|
|
597
599
|
{inputIconElement}
|
|
598
600
|
<TextNative
|
|
599
|
-
ref={inputRef}
|
|
600
601
|
numberOfLines={1}
|
|
601
602
|
ellipsizeMode="head"
|
|
602
603
|
className={`
|
|
603
604
|
Combo-TextNative
|
|
604
|
-
h-auto
|
|
605
|
-
self-stretch
|
|
606
605
|
flex-1
|
|
607
606
|
${_.isEmpty(textInputValue) ? "text-grey-400" : "text-black"}
|
|
608
607
|
${styles.FORM_COMBO_INPUT_CLASSNAME}
|
|
@@ -878,6 +877,7 @@ export function ComboComponent(props) {
|
|
|
878
877
|
dropdownMenu-Box
|
|
879
878
|
flex-1
|
|
880
879
|
overflow-auto
|
|
880
|
+
bg-white
|
|
881
881
|
p-0
|
|
882
882
|
rounded-none
|
|
883
883
|
border
|
|
@@ -1350,12 +1350,15 @@ function Form(props) {
|
|
|
1350
1350
|
{editorType !== EDITOR_TYPE__INLINE &&
|
|
1351
1351
|
<ScrollView
|
|
1352
1352
|
className={`
|
|
1353
|
-
ScrollView
|
|
1353
|
+
Form-ScrollView
|
|
1354
1354
|
w-full
|
|
1355
1355
|
flex-1
|
|
1356
1356
|
pb-1
|
|
1357
1357
|
web:min-h-[${minHeight}px]
|
|
1358
1358
|
`}
|
|
1359
|
+
contentContainerStyle={{
|
|
1360
|
+
height: '100%',
|
|
1361
|
+
}}
|
|
1359
1362
|
>
|
|
1360
1363
|
{modeHeader}
|
|
1361
1364
|
{formHeader}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useState, useRef, } from 'react';
|
|
1
|
+
import { forwardRef, useEffect, useState, useRef, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ADD,
|
|
4
4
|
EDIT,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
EDITOR_MODE__ADD,
|
|
12
12
|
EDITOR_MODE__EDIT,
|
|
13
13
|
EDITOR_TYPE__SIDE,
|
|
14
|
+
EDITOR_TYPE__INLINE,
|
|
14
15
|
} from '../../../Constants/Editor.js';
|
|
15
16
|
import Button from '../../Buttons/Button.js';
|
|
16
17
|
import UiGlobals from '../../../UiGlobals.js';
|
|
@@ -20,10 +21,10 @@ import _ from 'lodash';
|
|
|
20
21
|
// This HOC will eventually get out of sync with that one, and may need to be updated.
|
|
21
22
|
|
|
22
23
|
export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
23
|
-
return (props) => {
|
|
24
|
+
return forwardRef((props, ref) => {
|
|
24
25
|
|
|
25
26
|
if (props.secondaryDisableWithEditor) {
|
|
26
|
-
return <WrappedComponent {...props} isTree={isTree} />;
|
|
27
|
+
return <WrappedComponent {...props} ref={ref} isTree={isTree} />;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
let [secondaryEditorMode, secondarySetEditorMode] = useState(EDITOR_MODE__VIEW); // Can change below, so use 'let'
|
|
@@ -52,6 +53,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
52
53
|
secondaryNewEntityDisplayValue,
|
|
53
54
|
secondaryNewEntityDisplayProperty, // in case the field to set for newEntityDisplayValue is different from model
|
|
54
55
|
secondaryDefaultValues,
|
|
56
|
+
secondaryStayInEditModeOnSelectionChange = false,
|
|
55
57
|
|
|
56
58
|
// withComponent
|
|
57
59
|
self,
|
|
@@ -132,7 +134,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
132
134
|
// NOTE: This is a hack to prevent adding a new record while the repository is still loading.
|
|
133
135
|
// This can happen when the repository is still loading, and the user clicks the 'Add' button.
|
|
134
136
|
setTimeout(() => {
|
|
135
|
-
|
|
137
|
+
secondaryDoAdd(e, values);
|
|
136
138
|
}, 500);
|
|
137
139
|
return;
|
|
138
140
|
}
|
|
@@ -169,7 +171,9 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
169
171
|
if (!secondarySelection[0]) {
|
|
170
172
|
throw Error('Must select a parent node.');
|
|
171
173
|
}
|
|
172
|
-
|
|
174
|
+
const parent = secondarySelection[0];
|
|
175
|
+
addValues.parentId = parent.id;
|
|
176
|
+
addValues.depth = parent.depth +1;
|
|
173
177
|
} else {
|
|
174
178
|
// Set repository to sort by id DESC and switch to page 1, so this new entity is guaranteed to show up on the current page, even after saving
|
|
175
179
|
const currentSorter = SecondaryRepository.sorters[0];
|
|
@@ -200,10 +204,13 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
200
204
|
setIsSaving(false);
|
|
201
205
|
setSecondaryIsIgnoreNextSelectionChange(true);
|
|
202
206
|
secondarySetSelection([entity]);
|
|
207
|
+
if (getListeners().onAfterAdd) {
|
|
208
|
+
await getListeners().onAfterAdd(entity);
|
|
209
|
+
}
|
|
203
210
|
if (SecondaryRepository.isAutoSave) {
|
|
204
211
|
// for isAutoSave Repositories, submit the handers right away
|
|
205
|
-
if (getListeners().
|
|
206
|
-
await getListeners().
|
|
212
|
+
if (getListeners().onAfterAddSave) {
|
|
213
|
+
await getListeners().onAfterAddSave(entity);
|
|
207
214
|
}
|
|
208
215
|
if (secondaryOnAdd) {
|
|
209
216
|
await secondaryOnAdd(entity);
|
|
@@ -331,6 +338,13 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
331
338
|
showPermissionsError(VIEW, secondaryModel);
|
|
332
339
|
return;
|
|
333
340
|
}
|
|
341
|
+
if (secondaryEditorType === EDITOR_TYPE__INLINE) {
|
|
342
|
+
alert('Cannot view in inline editor.');
|
|
343
|
+
return; // inline editor doesn't have a view mode
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// check permissions for view
|
|
347
|
+
|
|
334
348
|
if (secondarySelection.length !== 1) {
|
|
335
349
|
return;
|
|
336
350
|
}
|
|
@@ -350,6 +364,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
350
364
|
showPermissionsError(DUPLICATE, secondaryModel);
|
|
351
365
|
return;
|
|
352
366
|
}
|
|
367
|
+
|
|
368
|
+
// check permissions for duplicate
|
|
369
|
+
|
|
370
|
+
|
|
353
371
|
if (secondarySelection.length !== 1) {
|
|
354
372
|
return;
|
|
355
373
|
}
|
|
@@ -436,8 +454,8 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
436
454
|
if (secondaryOnAdd) {
|
|
437
455
|
await secondaryOnAdd(secondarySelection);
|
|
438
456
|
}
|
|
439
|
-
if (getListeners().
|
|
440
|
-
await getListeners().
|
|
457
|
+
if (getListeners().onAfterAddSave) {
|
|
458
|
+
await getListeners().onAfterAddSave(secondarySelection);
|
|
441
459
|
}
|
|
442
460
|
setIsAdding(false);
|
|
443
461
|
if (!canUser || canUser(EDIT, secondaryModel)) {
|
|
@@ -500,6 +518,16 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
500
518
|
});
|
|
501
519
|
},
|
|
502
520
|
calculateEditorMode = (secondaryIsIgnoreNextSelectionChange = false) => {
|
|
521
|
+
|
|
522
|
+
let doStayInEditModeOnSelectionChange = secondaryStayInEditModeOnSelectionChange;
|
|
523
|
+
if (!_.isNil(UiGlobals.stayInEditModeOnSelectionChange)) {
|
|
524
|
+
// allow global override to for this property
|
|
525
|
+
doStayInEditModeOnSelectionChange = UiGlobals.stayInEditModeOnSelectionChange;
|
|
526
|
+
}
|
|
527
|
+
if (doStayInEditModeOnSelectionChange) {
|
|
528
|
+
secondaryIsIgnoreNextSelectionChange = true;
|
|
529
|
+
}
|
|
530
|
+
|
|
503
531
|
// calculateEditorMode gets called only on selection changes
|
|
504
532
|
let mode;
|
|
505
533
|
if (secondaryEditorType === EDITOR_TYPE__SIDE && !_.isNil(UiGlobals.isSideEditorAlwaysEditMode) && UiGlobals.isSideEditorAlwaysEditMode) {
|
|
@@ -579,6 +607,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
579
607
|
|
|
580
608
|
return <WrappedComponent
|
|
581
609
|
{...props}
|
|
610
|
+
ref={ref}
|
|
582
611
|
secondaryDisableWithEditor={false}
|
|
583
612
|
secondaryCurrentRecord={secondaryCurrentRecord}
|
|
584
613
|
secondarySetCurrentRecord={secondarySetCurrentRecord}
|
|
@@ -612,5 +641,5 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
|
|
|
612
641
|
secondarySetSelection={secondarySetSelectionDecorated}
|
|
613
642
|
isTree={isTree}
|
|
614
643
|
/>;
|
|
615
|
-
};
|
|
644
|
+
});
|
|
616
645
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { forwardRef } from 'react';
|
|
1
2
|
import {
|
|
2
3
|
EDITOR_TYPE__SIDE,
|
|
3
4
|
} from '../../../Constants/Editor.js';
|
|
@@ -10,20 +11,21 @@ import _ from 'lodash';
|
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
function withAdditionalProps(WrappedComponent) {
|
|
13
|
-
return (props) => {
|
|
14
|
+
return forwardRef((props, ref) => {
|
|
14
15
|
// provide the editorType to withEditor
|
|
15
16
|
return <WrappedComponent
|
|
16
17
|
editorType={EDITOR_TYPE__SIDE}
|
|
17
18
|
{...props}
|
|
19
|
+
ref={ref}
|
|
18
20
|
/>;
|
|
19
|
-
};
|
|
21
|
+
});
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
// NOTE: Effectivtly, the HOC composition is:
|
|
23
25
|
// withAdditionalProps(withSecondaryEditor(withSecondarySideEditor))
|
|
24
26
|
|
|
25
27
|
export default function withSecondarySideEditor(WrappedComponent, isTree = false) {
|
|
26
|
-
|
|
28
|
+
const SideEditor = forwardRef((props, ref) => {
|
|
27
29
|
const {
|
|
28
30
|
SecondaryEditor,
|
|
29
31
|
secondaryEditorProps = {},
|
|
@@ -36,6 +38,7 @@ export default function withSecondarySideEditor(WrappedComponent, isTree = false
|
|
|
36
38
|
secondarySelectorId,
|
|
37
39
|
secondarySelectorSelected,
|
|
38
40
|
secondarySelectorSelectedField,
|
|
41
|
+
style,
|
|
39
42
|
|
|
40
43
|
...propsToPass
|
|
41
44
|
} = props;
|
|
@@ -44,8 +47,23 @@ export default function withSecondarySideEditor(WrappedComponent, isTree = false
|
|
|
44
47
|
throw Error('SecondaryEditor is not defined');
|
|
45
48
|
}
|
|
46
49
|
|
|
50
|
+
if (isResizable) {
|
|
51
|
+
secondaryEditorProps.w = 500;
|
|
52
|
+
secondaryEditorProps.isResizable = true;
|
|
53
|
+
} else {
|
|
54
|
+
secondaryEditorProps.flex = secondarySideFlex;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!secondaryEditorProps.className) {
|
|
58
|
+
secondaryEditorProps.className = '';
|
|
59
|
+
}
|
|
60
|
+
secondaryEditorProps.className += ' border-l-1 border-l-grey-300';
|
|
61
|
+
|
|
47
62
|
return <Container
|
|
63
|
+
parent={self}
|
|
64
|
+
reference="SideEditor"
|
|
48
65
|
center={<WrappedComponent
|
|
66
|
+
ref={ref}
|
|
49
67
|
isTree={isTree}
|
|
50
68
|
isSideEditor={true}
|
|
51
69
|
{...props}
|
|
@@ -53,13 +71,11 @@ export default function withSecondarySideEditor(WrappedComponent, isTree = false
|
|
|
53
71
|
east={<Editor
|
|
54
72
|
{...propsToPass}
|
|
55
73
|
editorType={EDITOR_TYPE__SIDE}
|
|
56
|
-
flex={secondarySideFlex}
|
|
57
|
-
borderLeftWidth={1}
|
|
58
|
-
borderLeftColor="#ccc"
|
|
59
74
|
{...secondaryEditorProps}
|
|
60
75
|
parent={self}
|
|
61
76
|
reference="secondaryEditor"
|
|
62
77
|
/>}
|
|
63
78
|
/>;
|
|
64
|
-
})
|
|
79
|
+
});
|
|
80
|
+
return withAdditionalProps(withSecondaryEditor(SideEditor, isTree));
|
|
65
81
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { forwardRef } from 'react';
|
|
1
2
|
import {
|
|
2
3
|
Modal, ModalBackdrop, ModalHeader, ModalContent, ModalCloseButton, ModalBody, ModalFooter,
|
|
3
4
|
} from '@project-components/Gluestack';
|
|
@@ -13,20 +14,21 @@ import _ from 'lodash';
|
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
function withAdditionalProps(WrappedComponent) {
|
|
16
|
-
return (props) => {
|
|
17
|
+
return forwardRef((props, ref) => {
|
|
17
18
|
// provide the editorType to withEditor
|
|
18
19
|
return <WrappedComponent
|
|
19
20
|
editorType={EDITOR_TYPE__WINDOWED}
|
|
20
21
|
{...props}
|
|
22
|
+
ref={ref}
|
|
21
23
|
/>;
|
|
22
|
-
};
|
|
24
|
+
});
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
// NOTE: Effectivtly, the HOC composition is:
|
|
26
28
|
// withAdditionalProps(withSecondaryEditor(withSecondaryWindowedEditor))
|
|
27
29
|
|
|
28
30
|
export default function withSecondaryWindowedEditor(WrappedComponent, isTree = false) {
|
|
29
|
-
|
|
31
|
+
const WindowedEditor = forwardRef((props, ref) => {
|
|
30
32
|
const {
|
|
31
33
|
secondaryIsEditorShown = false,
|
|
32
34
|
secondarySetIsEditorShown,
|
|
@@ -41,6 +43,7 @@ export default function withSecondaryWindowedEditor(WrappedComponent, isTree = f
|
|
|
41
43
|
secondarySelectorSelected,
|
|
42
44
|
secondarySelectorSelectedField,
|
|
43
45
|
h,
|
|
46
|
+
style,
|
|
44
47
|
|
|
45
48
|
...propsToPass
|
|
46
49
|
} = props;
|
|
@@ -65,25 +68,28 @@ export default function withSecondaryWindowedEditor(WrappedComponent, isTree = f
|
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
return <>
|
|
68
|
-
<WrappedComponent {...props} />
|
|
71
|
+
<WrappedComponent {...props} ref={ref} />
|
|
69
72
|
{secondaryIsEditorShown &&
|
|
70
73
|
<Modal
|
|
71
74
|
isOpen={true}
|
|
72
75
|
onClose={() => secondarySetIsEditorShown(false)}
|
|
76
|
+
className="withSecondaryEditor-Modal"
|
|
73
77
|
>
|
|
74
|
-
<ModalBackdrop />
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
<ModalBackdrop className="withSecondaryEditor-ModalBackdrop" />
|
|
79
|
+
<SecondaryEditor
|
|
80
|
+
editorType={EDITOR_TYPE__WINDOWED}
|
|
81
|
+
{...propsToPass}
|
|
82
|
+
{...secondaryEditorProps}
|
|
83
|
+
parent={self}
|
|
84
|
+
reference="secondaryEditor"
|
|
85
|
+
className={`
|
|
86
|
+
bg-white
|
|
87
|
+
shadow-lg
|
|
88
|
+
rounded-lg
|
|
89
|
+
`}
|
|
90
|
+
/>
|
|
86
91
|
</Modal>}
|
|
87
92
|
</>;
|
|
88
|
-
}
|
|
93
|
+
});
|
|
94
|
+
return withAdditionalProps(withSecondaryEditor(WindowedEditor, isTree));
|
|
89
95
|
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from '@project-components/Gluestack';
|
|
7
7
|
import {
|
|
8
8
|
ScrollView,
|
|
9
|
+
Platform,
|
|
9
10
|
} from 'react-native'
|
|
10
11
|
import {
|
|
11
12
|
EDITOR_TYPE__PLAIN,
|
|
@@ -35,6 +36,8 @@ import _ from 'lodash';
|
|
|
35
36
|
//
|
|
36
37
|
// Model defaultFilters should adjust to this new arrangement
|
|
37
38
|
|
|
39
|
+
const isWindows = Platform.OS === 'windows';
|
|
40
|
+
|
|
38
41
|
export default function withFilters(WrappedComponent) {
|
|
39
42
|
return forwardRef((props, ref) => {
|
|
40
43
|
|
|
@@ -140,9 +143,11 @@ export default function withFilters(WrappedComponent) {
|
|
|
140
143
|
|
|
141
144
|
const
|
|
142
145
|
filterCallbackRef = useRef(),
|
|
146
|
+
scrollViewRef = useRef(),
|
|
143
147
|
[filters, setFiltersRaw] = useState(formattedStartingFilters), // array of formatted filters
|
|
144
148
|
[slots, setSlots] = useState(startingSlots), // array of field names user is currently filtering on; blank slots have a null entry in array
|
|
145
149
|
[previousFilterNames, setPreviousFilterNames] = useState([]), // names of filters the repository used last query
|
|
150
|
+
[isHorizontalScrollbarShown, setIsHorizontalScrollbarShown] = useState(false),
|
|
146
151
|
setFilters = (filters, doSetSlots = true, save = true) => {
|
|
147
152
|
setFiltersRaw(filters);
|
|
148
153
|
|
|
@@ -349,6 +354,16 @@ export default function withFilters(WrappedComponent) {
|
|
|
349
354
|
w: 500,
|
|
350
355
|
});
|
|
351
356
|
},
|
|
357
|
+
onContentSizeChange = (contentWidth, contentHeight) => {
|
|
358
|
+
if (!isWindows) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
if (scrollViewRef.current) {
|
|
362
|
+
scrollViewRef.current.measure((x, y, width, height, pageX, pageY) => {
|
|
363
|
+
setIsHorizontalScrollbarShown(contentWidth > width);
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
},
|
|
352
367
|
buildModalBody = (modalFilters, modalSlots) => {
|
|
353
368
|
|
|
354
369
|
const
|
|
@@ -603,6 +618,25 @@ export default function withFilters(WrappedComponent) {
|
|
|
603
618
|
})();
|
|
604
619
|
}, [filters]);
|
|
605
620
|
|
|
621
|
+
useEffect(() => {
|
|
622
|
+
if (!isWindows) {
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// NOTE: On Windows machines, I was getting horizontal scrollbars when the ScrollView
|
|
627
|
+
// was not wide enough to contain all the filters. This workaround adds pb-5 to the ScrollView
|
|
628
|
+
// when the scrollbar is shown.
|
|
629
|
+
|
|
630
|
+
if (scrollViewRef.current) {
|
|
631
|
+
scrollViewRef.current.addEventListener('contentSizeChange', onContentSizeChange);
|
|
632
|
+
}
|
|
633
|
+
return () => {
|
|
634
|
+
if (scrollViewRef.current) {
|
|
635
|
+
scrollViewRef.current.removeEventListener('contentSizeChange', onContentSizeChange);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
}, []);
|
|
639
|
+
|
|
606
640
|
if (!isReady) {
|
|
607
641
|
return null;
|
|
608
642
|
}
|
|
@@ -615,9 +649,16 @@ export default function withFilters(WrappedComponent) {
|
|
|
615
649
|
const
|
|
616
650
|
renderedFilters = renderFilters(),
|
|
617
651
|
hasFilters = !!renderedFilters.length,
|
|
652
|
+
scrollViewClass = isWindows && isHorizontalScrollbarShown ? 'pb-5' : '',
|
|
618
653
|
toolbar = <Toolbar>
|
|
619
654
|
<HStack className="withFilters-scrollViewContainer flex-1 items-center">
|
|
620
|
-
<ScrollView
|
|
655
|
+
<ScrollView
|
|
656
|
+
ref={scrollViewRef}
|
|
657
|
+
className={`withFilters-ScrollView ${scrollViewClass}`}
|
|
658
|
+
horizontal={true}
|
|
659
|
+
contentContainerStyle={{ alignItems: 'center' }}
|
|
660
|
+
onContentSizeChange={onContentSizeChange}
|
|
661
|
+
>
|
|
621
662
|
<Text
|
|
622
663
|
className={`
|
|
623
664
|
withFilters-filtersLabel
|
|
@@ -163,13 +163,14 @@ export default function withModal(WrappedComponent) {
|
|
|
163
163
|
<Panel
|
|
164
164
|
title={title}
|
|
165
165
|
isCollapsible={false}
|
|
166
|
-
className="bg-white
|
|
166
|
+
className="withModal-Panel bg-white"
|
|
167
167
|
h={h > windowHeight ? windowHeight : h}
|
|
168
168
|
w={w > windowWidth ? windowWidth : w}
|
|
169
169
|
isWindow={true}
|
|
170
170
|
disableAutoFlex={true}
|
|
171
171
|
onClose={canClose ? hideModal : null}
|
|
172
172
|
footer={footer}
|
|
173
|
+
isScrollable={true}
|
|
173
174
|
>{modalBody}</Panel>
|
|
174
175
|
}
|
|
175
176
|
}
|
|
@@ -83,7 +83,13 @@ export default function withPdfButtons(WrappedComponent) {
|
|
|
83
83
|
|
|
84
84
|
if (!_.isEmpty(ancillaryItems)) {
|
|
85
85
|
const
|
|
86
|
-
ancillaryItemsClone = _.
|
|
86
|
+
ancillaryItemsClone = _.cloneDeepWith(ancillaryItems, (value) => {
|
|
87
|
+
// Exclude the 'parent' property from being cloned, as it would introduce an infinitely recursive loop
|
|
88
|
+
if (value && value.parent) {
|
|
89
|
+
const { parent, ...rest } = value;
|
|
90
|
+
return rest;
|
|
91
|
+
}
|
|
92
|
+
}),
|
|
87
93
|
items = [];
|
|
88
94
|
_.each(ancillaryItemsClone, (ancillaryItem) => { // clone, as we don't want to alter the item by reference
|
|
89
95
|
let name;
|
|
@@ -4,8 +4,8 @@ import { Path, Svg } from 'react-native-svg';
|
|
|
4
4
|
|
|
5
5
|
const SvgComponent = createIcon({
|
|
6
6
|
Root: Svg,
|
|
7
|
-
viewBox: '0 0
|
|
8
|
-
path: <Path d="
|
|
7
|
+
viewBox: '0 0 384 512',
|
|
8
|
+
path: <Path d="M192 0c-41.8 0-77.4 26.7-90.5 64L64 64C28.7 64 0 92.7 0 128L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-320c0-35.3-28.7-64-64-64l-37.5 0C269.4 26.7 233.8 0 192 0zm0 64a32 32 0 1 1 0 64 32 32 0 1 1 0-64zM305 273L177 401c-9.4 9.4-24.6 9.4-33.9 0L79 337c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L271 239c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z" />
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
export default SvgComponent
|
|
@@ -3,7 +3,7 @@ import { Path, Svg } from 'react-native-svg';
|
|
|
3
3
|
|
|
4
4
|
const SvgComponent = createIcon({
|
|
5
5
|
Root: Svg,
|
|
6
|
-
viewBox: '0 0
|
|
6
|
+
viewBox: '0 0 101.44 83.8',
|
|
7
7
|
path: <Path d="M58.92 4.5L99.9 68.86c4.12 6.47-.53 14.94-8.2 14.94H9.74c-7.67 0-12.32-8.47-8.2-14.94L42.52 4.5c3.82-6 12.58-6 16.4 0zm-8.2 68.21c3.24 0 5.34-2.34 5.34-5.46-.06-3.18-2.16-5.46-5.34-5.46s-5.4 2.28-5.4 5.46 2.16 5.46 5.4 5.46zm3.42-13.92l1.32-27.18h-9.54l1.38 27.18h6.84z" strokeWidth={0} />
|
|
8
8
|
});
|
|
9
9
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HStack,
|
|
3
|
+
Icon,
|
|
4
|
+
Text,
|
|
5
|
+
VStack,
|
|
6
|
+
} from '@project-components/Gluestack';
|
|
7
|
+
import {
|
|
8
|
+
SCREEN_MODES__FULL,
|
|
9
|
+
SCREEN_MODES__SIDE,
|
|
10
|
+
} from '../../Constants/ScreenModes.js'
|
|
11
|
+
import FullWidth from '../Icons/FullWidth';
|
|
12
|
+
import SideBySide from '../Icons/SideBySide';
|
|
13
|
+
import UiGlobals from '../../UiGlobals.js';
|
|
14
|
+
import IconButton from '../Buttons/IconButton';
|
|
15
|
+
import testProps from '../../Functions/testProps.js';
|
|
16
|
+
import _ from 'lodash';
|
|
17
|
+
|
|
18
|
+
export default function ScreenHeader(props) {
|
|
19
|
+
const {
|
|
20
|
+
title,
|
|
21
|
+
icon,
|
|
22
|
+
useModeIcons = false,
|
|
23
|
+
allowSideBySide = false,
|
|
24
|
+
actualMode,
|
|
25
|
+
onFullWidth,
|
|
26
|
+
onSideBySide,
|
|
27
|
+
} = props,
|
|
28
|
+
textProps = {},
|
|
29
|
+
styles = UiGlobals.styles;
|
|
30
|
+
if (styles.MANAGER_SCREEN_TITLE) {
|
|
31
|
+
textProps.style = {
|
|
32
|
+
fontFamily: styles.MANAGER_SCREEN_TITLE,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return <HStack className="ScreenHeader-HStack h-[80px] items-center border-b-[2px] border-b-[#ccc]">
|
|
36
|
+
{icon &&
|
|
37
|
+
<Icon
|
|
38
|
+
as={icon}
|
|
39
|
+
className={`
|
|
40
|
+
ml-5
|
|
41
|
+
text-black
|
|
42
|
+
`}
|
|
43
|
+
size="xl"
|
|
44
|
+
/>}
|
|
45
|
+
<Text {...textProps} className="ScreenHeader-Text pl-4 text-[26px] font-[700]">{title}</Text>
|
|
46
|
+
{useModeIcons && allowSideBySide &&
|
|
47
|
+
<>
|
|
48
|
+
<IconButton
|
|
49
|
+
{...testProps('fullModeBtn')}
|
|
50
|
+
icon={FullWidth}
|
|
51
|
+
_icon={{
|
|
52
|
+
size: 'xl',
|
|
53
|
+
className: 'text-black',
|
|
54
|
+
}}
|
|
55
|
+
isDisabled={actualMode === SCREEN_MODES__FULL}
|
|
56
|
+
onPress={onFullWidth}
|
|
57
|
+
tooltip="To full width"
|
|
58
|
+
className="ml-5"
|
|
59
|
+
/>
|
|
60
|
+
<IconButton
|
|
61
|
+
{...testProps('sideModeBtn')}
|
|
62
|
+
icon={SideBySide}
|
|
63
|
+
_icon={{
|
|
64
|
+
size: 'xl',
|
|
65
|
+
className: 'text-black',
|
|
66
|
+
}}
|
|
67
|
+
isDisabled={actualMode === SCREEN_MODES__SIDE}
|
|
68
|
+
onPress={onSideBySide}
|
|
69
|
+
tooltip="To side editor"
|
|
70
|
+
/>
|
|
71
|
+
</>}
|
|
72
|
+
</HStack>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TextNative,
|
|
3
|
+
Tooltip, TooltipContent, TooltipText,
|
|
4
|
+
} from '@project-components/Gluestack';
|
|
5
|
+
|
|
6
|
+
export default function TextWithTooltip(props) {
|
|
7
|
+
const {
|
|
8
|
+
tooltip,
|
|
9
|
+
children,
|
|
10
|
+
...propsToPass
|
|
11
|
+
} = props;
|
|
12
|
+
return <Tooltip
|
|
13
|
+
placement="bottom"
|
|
14
|
+
trigger={(triggerProps) => {
|
|
15
|
+
return <TextNative {...triggerProps} {...propsToPass}>{children}</TextNative>
|
|
16
|
+
}}
|
|
17
|
+
>
|
|
18
|
+
<TooltipContent>
|
|
19
|
+
<TooltipText>{tooltip}</TooltipText>
|
|
20
|
+
</TooltipContent>
|
|
21
|
+
</Tooltip>;
|
|
22
|
+
}
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
UI_MODE_NATIVE,
|
|
16
16
|
} from '../../Constants/UiModes.js';
|
|
17
17
|
import UiGlobals from '../../UiGlobals.js';
|
|
18
|
+
import testProps from '../../Functions/testProps.js';
|
|
18
19
|
import Minus from '../Icons/Minus.js';
|
|
19
20
|
import Plus from '../Icons/Plus.js';
|
|
20
21
|
import Xmark from '../Icons/Xmark.js';
|
|
@@ -55,6 +56,7 @@ export default function Header(props) {
|
|
|
55
56
|
closeClassName += ' mb-1';
|
|
56
57
|
}
|
|
57
58
|
closeBtn = <IconButton
|
|
59
|
+
{...testProps('closeBtn')}
|
|
58
60
|
onPress={onClose}
|
|
59
61
|
icon={Xmark}
|
|
60
62
|
_icon={{
|
|
@@ -120,7 +120,7 @@ function Panel(props) {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
// frame
|
|
123
|
-
className += ' border-
|
|
123
|
+
className += ' border-grey-300' + (isWindow ? ' rounded-lg shadow-lg ' : '') + (frame ? ' border-2' : ' border-none');
|
|
124
124
|
|
|
125
125
|
if (props.className) {
|
|
126
126
|
className += ' ' + props.className;
|
|
@@ -143,8 +143,13 @@ function Panel(props) {
|
|
|
143
143
|
overflow-hidden
|
|
144
144
|
`}
|
|
145
145
|
>
|
|
146
|
-
{isScrollable ?
|
|
147
|
-
<ScrollView
|
|
146
|
+
{isScrollable ?
|
|
147
|
+
<ScrollView
|
|
148
|
+
className="Panel-ScrollView"
|
|
149
|
+
contentContainerStyle={{
|
|
150
|
+
height: '100%',
|
|
151
|
+
}}
|
|
152
|
+
>
|
|
148
153
|
{children}
|
|
149
154
|
</ScrollView> :
|
|
150
155
|
children}
|
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
|
-
HStack,
|
|
3
|
-
Text,
|
|
4
2
|
VStackNative,
|
|
5
3
|
} from '@project-components/Gluestack';
|
|
6
4
|
import React, { useState, useEffect, } from 'react';
|
|
5
|
+
import {
|
|
6
|
+
SCREEN_MODES__FULL,
|
|
7
|
+
SCREEN_MODES__SIDE,
|
|
8
|
+
} from '../../Constants/ScreenModes.js'
|
|
7
9
|
import withComponent from '../Hoc/withComponent.js';
|
|
8
10
|
import testProps from '../../Functions/testProps.js';
|
|
9
|
-
import
|
|
10
|
-
import IconButton from '../Buttons/IconButton';
|
|
11
|
-
import FullWidth from '../Icons/FullWidth';
|
|
12
|
-
import SideBySide from '../Icons/SideBySide';
|
|
11
|
+
import ScreenHeader from '../Layout/ScreenHeader';
|
|
13
12
|
import getSaved from '../../Functions/getSaved.js';
|
|
14
13
|
import setSaved from '../../Functions/setSaved.js';
|
|
15
14
|
import _ from 'lodash';
|
|
16
15
|
|
|
17
|
-
const
|
|
18
|
-
MODE_FULL = 'MODE_FULL',
|
|
19
|
-
MODE_SIDE = 'MODE_SIDE';
|
|
20
|
-
|
|
21
16
|
function ManagerScreen(props) {
|
|
22
17
|
const {
|
|
23
18
|
title,
|
|
19
|
+
icon,
|
|
24
20
|
sideModeComponent,
|
|
25
21
|
fullModeComponent,
|
|
26
22
|
onChangeMode,
|
|
@@ -28,15 +24,14 @@ function ManagerScreen(props) {
|
|
|
28
24
|
// withComponent
|
|
29
25
|
self,
|
|
30
26
|
} = props,
|
|
31
|
-
styles = UiGlobals.styles,
|
|
32
27
|
id = props.id || props.self?.path,
|
|
33
28
|
[isRendered, setIsRendered] = useState(false),
|
|
34
29
|
[isModeSet, setIsModeSet] = useState(false),
|
|
35
30
|
[allowSideBySide, setAllowSideBySide] = useState(false),
|
|
36
|
-
[mode, setModeRaw] = useState(
|
|
37
|
-
actualMode = (!allowSideBySide || mode ===
|
|
31
|
+
[mode, setModeRaw] = useState(SCREEN_MODES__FULL),
|
|
32
|
+
actualMode = (!allowSideBySide || mode === SCREEN_MODES__FULL) ? SCREEN_MODES__FULL : SCREEN_MODES__SIDE,
|
|
38
33
|
setMode = (newMode) => {
|
|
39
|
-
if (!allowSideBySide && newMode ===
|
|
34
|
+
if (!allowSideBySide && newMode === SCREEN_MODES__SIDE) {
|
|
40
35
|
return;
|
|
41
36
|
}
|
|
42
37
|
if (newMode === mode) {
|
|
@@ -83,49 +78,22 @@ function ManagerScreen(props) {
|
|
|
83
78
|
self.mode = actualMode;
|
|
84
79
|
}
|
|
85
80
|
|
|
86
|
-
const
|
|
87
|
-
whichComponent = actualMode === MODE_FULL ? fullModeComponent : sideModeComponent,
|
|
88
|
-
textProps = {};
|
|
89
|
-
if (styles.MANAGER_SCREEN_TITLE) {
|
|
90
|
-
textProps.style = {
|
|
91
|
-
fontFamily: styles.MANAGER_SCREEN_TITLE,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
81
|
+
const whichComponent = actualMode === SCREEN_MODES__FULL ? fullModeComponent : sideModeComponent;
|
|
94
82
|
|
|
95
83
|
return <VStackNative
|
|
96
84
|
{...testProps(self)}
|
|
97
85
|
onLayout={onLayout}
|
|
98
86
|
className="max-h-screen overflow-hidden flex-1 w-full"
|
|
99
87
|
>
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
{
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
className: 'text-black',
|
|
110
|
-
}}
|
|
111
|
-
isDisabled={actualMode === MODE_FULL}
|
|
112
|
-
onPress={() => setMode(MODE_FULL)}
|
|
113
|
-
tooltip="To full width"
|
|
114
|
-
className="ml-5"
|
|
115
|
-
/>
|
|
116
|
-
<IconButton
|
|
117
|
-
{...testProps('sideModeBtn')}
|
|
118
|
-
icon={SideBySide}
|
|
119
|
-
_icon={{
|
|
120
|
-
size: 'xl',
|
|
121
|
-
className: 'text-black',
|
|
122
|
-
}}
|
|
123
|
-
isDisabled={actualMode === MODE_SIDE}
|
|
124
|
-
onPress={() => setMode(MODE_SIDE)}
|
|
125
|
-
tooltip="To side editor"
|
|
126
|
-
/>
|
|
127
|
-
</>}
|
|
128
|
-
</HStack>
|
|
88
|
+
<ScreenHeader
|
|
89
|
+
title={title}
|
|
90
|
+
icon={icon}
|
|
91
|
+
useModeIcons={true}
|
|
92
|
+
actualMode={actualMode}
|
|
93
|
+
allowSideBySide={allowSideBySide}
|
|
94
|
+
onFullWidth={() => setMode(SCREEN_MODES__FULL)}
|
|
95
|
+
onSideBySide={() => setMode(SCREEN_MODES__SIDE)}
|
|
96
|
+
/>
|
|
129
97
|
{isRendered && isModeSet && whichComponent}
|
|
130
98
|
</VStackNative>;
|
|
131
99
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useState, } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
HStack,
|
|
4
|
+
ScrollView,
|
|
5
|
+
VStack,
|
|
6
|
+
VStackNative,
|
|
7
|
+
} from '@project-components/Gluestack';
|
|
8
|
+
import ChartPie from '../Icons/ChartPie.js';
|
|
9
|
+
import ScreenHeader from '../Layout/ScreenHeader.js';
|
|
10
|
+
|
|
11
|
+
const CONTAINER_THRESHOLD = 1100;
|
|
12
|
+
|
|
13
|
+
export default function ReportsManager(props) {
|
|
14
|
+
const {
|
|
15
|
+
reports = [],
|
|
16
|
+
isActive = false,
|
|
17
|
+
} = props,
|
|
18
|
+
[containerWidth, setContainerWidth] = useState(),
|
|
19
|
+
onLayout = (e) => {
|
|
20
|
+
setContainerWidth(e.nativeEvent.layout.width);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if (!isActive) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let reportElements = [];
|
|
28
|
+
if (containerWidth) {
|
|
29
|
+
if (containerWidth >= CONTAINER_THRESHOLD) {
|
|
30
|
+
// two column layout
|
|
31
|
+
const
|
|
32
|
+
reportsPerColumn = Math.ceil(reports.length / 2),
|
|
33
|
+
col1Reports = reports.slice(0, reportsPerColumn),
|
|
34
|
+
col2Reports = reports.slice(reportsPerColumn);
|
|
35
|
+
reportElements = <HStack className="gap-3">
|
|
36
|
+
<VStack className="flex-1">
|
|
37
|
+
{col1Reports}
|
|
38
|
+
</VStack>
|
|
39
|
+
<VStack className="flex-1">
|
|
40
|
+
{col2Reports}
|
|
41
|
+
</VStack>
|
|
42
|
+
</HStack>;
|
|
43
|
+
} else {
|
|
44
|
+
// one column layout
|
|
45
|
+
reportElements = reports;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return <VStack
|
|
50
|
+
className="overflow-hidden flex-1 w-full"
|
|
51
|
+
>
|
|
52
|
+
<ScreenHeader
|
|
53
|
+
title="Reports"
|
|
54
|
+
icon={ChartPie}
|
|
55
|
+
/>
|
|
56
|
+
<ScrollView className="flex-1 w-full">
|
|
57
|
+
<VStackNative className="w-full p-4" onLayout={onLayout}>
|
|
58
|
+
{containerWidth && reportElements}
|
|
59
|
+
</VStackNative>
|
|
60
|
+
</ScrollView>
|
|
61
|
+
</VStack>;
|
|
62
|
+
}
|
|
@@ -1396,7 +1396,13 @@ function TreeComponent(props) {
|
|
|
1396
1396
|
}}
|
|
1397
1397
|
className="Tree-deselector w-full flex-1 p-1 bg-white"
|
|
1398
1398
|
>
|
|
1399
|
-
<ScrollView
|
|
1399
|
+
<ScrollView
|
|
1400
|
+
{...testProps('ScrollView')}
|
|
1401
|
+
className="Tree-ScrollView flex-1 w-full"
|
|
1402
|
+
contentContainerStyle={{
|
|
1403
|
+
height: '100%',
|
|
1404
|
+
}}
|
|
1405
|
+
>
|
|
1400
1406
|
{!treeNodes?.length ?
|
|
1401
1407
|
<CenterBox>
|
|
1402
1408
|
{Repository.isLoading ? <Loading /> : <NoRecordsFound text={noneFoundText} onRefresh={reloadTree} />}
|
|
@@ -24,9 +24,11 @@ export function getDomNodes(selectors, options = {}) {
|
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Builds selector string for data-testid attributes.
|
|
27
|
-
*
|
|
28
|
-
*
|
|
27
|
+
* It leaves classname, id, and attribute selectors unchanged.
|
|
28
|
+
*
|
|
29
|
+
* If selectors is an array, these will be considered nested selectors.
|
|
29
30
|
* e.g. ['parent', 'child'] will be converted to '[data-testid="parent"] [data-testid="child"]'
|
|
31
|
+
* @argument {string|string[]} selectors - data-testid attribute values
|
|
30
32
|
* @return {string}
|
|
31
33
|
*/
|
|
32
34
|
export function getTestIdSelectors(selectors, isGetFirst = false) {
|
|
@@ -34,16 +36,47 @@ export function getTestIdSelectors(selectors, isGetFirst = false) {
|
|
|
34
36
|
selectors = [selectors];
|
|
35
37
|
}
|
|
36
38
|
const selectorParts = _.map(selectors, (selector) => {
|
|
37
|
-
if (selector.match(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
if (!selector.match(/^\./) // className, like .my-class
|
|
40
|
+
&& !selector.match(/^#/) // id, like @my-id
|
|
41
|
+
&& !selector.match(/=/) // attribute, like [role="switch"]
|
|
42
|
+
){
|
|
43
|
+
selector = '[data-testid="' + selector + '"]';
|
|
42
44
|
}
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
+
if (isGetFirst) {
|
|
46
|
+
selector += ':first';
|
|
45
47
|
}
|
|
46
|
-
return
|
|
48
|
+
return selector;
|
|
47
49
|
});
|
|
48
50
|
return selectorParts.join(' ');
|
|
49
51
|
}
|
|
52
|
+
|
|
53
|
+
export function drag(draggableSelectors, droppableSelectors, options = {}) {
|
|
54
|
+
if (typeof options.force === 'undefined') {
|
|
55
|
+
options.force = true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// getDomNode(getTestIdSelectors(droppableSelectors)).then((node) => {
|
|
59
|
+
// const selectors = getTestIdSelectors(droppableSelectors);
|
|
60
|
+
// debugger;
|
|
61
|
+
// });
|
|
62
|
+
|
|
63
|
+
options = {
|
|
64
|
+
source: { // applies to the element being dragged
|
|
65
|
+
x: 10,
|
|
66
|
+
// y: 100,
|
|
67
|
+
position: 'left',
|
|
68
|
+
},
|
|
69
|
+
target: { // applies to the drop target
|
|
70
|
+
position: 'left',
|
|
71
|
+
x: 20,
|
|
72
|
+
},
|
|
73
|
+
force: true, // applied to both the source and target element
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
return getDomNode(draggableSelectors)
|
|
78
|
+
.drag(getTestIdSelectors(droppableSelectors), options)
|
|
79
|
+
.then((success) => {
|
|
80
|
+
debugger;
|
|
81
|
+
});
|
|
82
|
+
}
|
package/src/Styles/Global.css
CHANGED