@onehat/ui 0.4.27 → 0.4.30
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/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/Hoc/withFilters.js +42 -1
- package/src/Components/Icons/ClipboardCheck.js +2 -2
- 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 -61
- 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/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}
|
|
@@ -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}
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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,27 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
|
-
HStack,
|
|
3
|
-
Icon,
|
|
4
|
-
Text,
|
|
5
2
|
VStackNative,
|
|
6
3
|
} from '@project-components/Gluestack';
|
|
7
4
|
import React, { useState, useEffect, } from 'react';
|
|
5
|
+
import {
|
|
6
|
+
SCREEN_MODES__FULL,
|
|
7
|
+
SCREEN_MODES__SIDE,
|
|
8
|
+
} from '../../Constants/ScreenModes.js'
|
|
8
9
|
import withComponent from '../Hoc/withComponent.js';
|
|
9
10
|
import testProps from '../../Functions/testProps.js';
|
|
10
|
-
import
|
|
11
|
-
import IconButton from '../Buttons/IconButton';
|
|
12
|
-
import FullWidth from '../Icons/FullWidth';
|
|
13
|
-
import SideBySide from '../Icons/SideBySide';
|
|
11
|
+
import ScreenHeader from '../Layout/ScreenHeader';
|
|
14
12
|
import getSaved from '../../Functions/getSaved.js';
|
|
15
13
|
import setSaved from '../../Functions/setSaved.js';
|
|
16
14
|
import _ from 'lodash';
|
|
17
15
|
|
|
18
|
-
const
|
|
19
|
-
MODE_FULL = 'MODE_FULL',
|
|
20
|
-
MODE_SIDE = 'MODE_SIDE';
|
|
21
|
-
|
|
22
16
|
function ManagerScreen(props) {
|
|
23
17
|
const {
|
|
24
18
|
title,
|
|
19
|
+
icon,
|
|
25
20
|
sideModeComponent,
|
|
26
21
|
fullModeComponent,
|
|
27
22
|
onChangeMode,
|
|
@@ -29,15 +24,14 @@ function ManagerScreen(props) {
|
|
|
29
24
|
// withComponent
|
|
30
25
|
self,
|
|
31
26
|
} = props,
|
|
32
|
-
styles = UiGlobals.styles,
|
|
33
27
|
id = props.id || props.self?.path,
|
|
34
28
|
[isRendered, setIsRendered] = useState(false),
|
|
35
29
|
[isModeSet, setIsModeSet] = useState(false),
|
|
36
30
|
[allowSideBySide, setAllowSideBySide] = useState(false),
|
|
37
|
-
[mode, setModeRaw] = useState(
|
|
38
|
-
actualMode = (!allowSideBySide || mode ===
|
|
31
|
+
[mode, setModeRaw] = useState(SCREEN_MODES__FULL),
|
|
32
|
+
actualMode = (!allowSideBySide || mode === SCREEN_MODES__FULL) ? SCREEN_MODES__FULL : SCREEN_MODES__SIDE,
|
|
39
33
|
setMode = (newMode) => {
|
|
40
|
-
if (!allowSideBySide && newMode ===
|
|
34
|
+
if (!allowSideBySide && newMode === SCREEN_MODES__SIDE) {
|
|
41
35
|
return;
|
|
42
36
|
}
|
|
43
37
|
if (newMode === mode) {
|
|
@@ -84,58 +78,22 @@ function ManagerScreen(props) {
|
|
|
84
78
|
self.mode = actualMode;
|
|
85
79
|
}
|
|
86
80
|
|
|
87
|
-
const
|
|
88
|
-
whichComponent = actualMode === MODE_FULL ? fullModeComponent : sideModeComponent,
|
|
89
|
-
textProps = {};
|
|
90
|
-
if (styles.MANAGER_SCREEN_TITLE) {
|
|
91
|
-
textProps.style = {
|
|
92
|
-
fontFamily: styles.MANAGER_SCREEN_TITLE,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
81
|
+
const whichComponent = actualMode === SCREEN_MODES__FULL ? fullModeComponent : sideModeComponent;
|
|
95
82
|
|
|
96
83
|
return <VStackNative
|
|
97
84
|
{...testProps(self)}
|
|
98
85
|
onLayout={onLayout}
|
|
99
86
|
className="max-h-screen overflow-hidden flex-1 w-full"
|
|
100
87
|
>
|
|
101
|
-
<
|
|
102
|
-
{
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
/> : null}
|
|
111
|
-
<Text {...textProps} className="pl-4 text-[26px] font-[700]">{title}</Text>
|
|
112
|
-
{allowSideBySide &&
|
|
113
|
-
<>
|
|
114
|
-
<IconButton
|
|
115
|
-
{...testProps('fullModeBtn')}
|
|
116
|
-
icon={FullWidth}
|
|
117
|
-
_icon={{
|
|
118
|
-
size: 'xl',
|
|
119
|
-
className: 'text-black',
|
|
120
|
-
}}
|
|
121
|
-
isDisabled={actualMode === MODE_FULL}
|
|
122
|
-
onPress={() => setMode(MODE_FULL)}
|
|
123
|
-
tooltip="To full width"
|
|
124
|
-
className="ml-5"
|
|
125
|
-
/>
|
|
126
|
-
<IconButton
|
|
127
|
-
{...testProps('sideModeBtn')}
|
|
128
|
-
icon={SideBySide}
|
|
129
|
-
_icon={{
|
|
130
|
-
size: 'xl',
|
|
131
|
-
className: 'text-black',
|
|
132
|
-
}}
|
|
133
|
-
isDisabled={actualMode === MODE_SIDE}
|
|
134
|
-
onPress={() => setMode(MODE_SIDE)}
|
|
135
|
-
tooltip="To side editor"
|
|
136
|
-
/>
|
|
137
|
-
</>}
|
|
138
|
-
</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
|
+
/>
|
|
139
97
|
{isRendered && isModeSet && whichComponent}
|
|
140
98
|
</VStackNative>;
|
|
141
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} />}
|