@fto-consult/expo-ui 2.8.0 → 2.9.0
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 +2 -2
- package/src/components/Chart/appexChart/index.js +1 -1
- package/src/components/Chart/index.js +11 -7
- package/src/components/Dialog/DropdownAlert/index.js +35 -24
- package/src/components/Fab/Group.js +135 -1
- package/src/components/Link/index.js +8 -3
- package/src/layouts/Fab/index.js +1 -0
- package/src/screens/NetWork +0 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fto-consult/expo-ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "Bibliothèque de composants UI Expo,react-native",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"resolutions": {
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"@expo/metro-config": "^0.4.0",
|
|
68
68
|
"@expo/vector-icons": "^13.0.0",
|
|
69
69
|
"@expo/webpack-config": "^0.17.2",
|
|
70
|
-
"@fto-consult/common": "^1.14.
|
|
70
|
+
"@fto-consult/common": "^1.14.16",
|
|
71
71
|
"@gorhom/portal": "^1.0.14",
|
|
72
72
|
"@react-native-async-storage/async-storage": "~1.17.3",
|
|
73
73
|
"@react-native-community/datetimepicker": "6.5.2",
|
|
@@ -11,7 +11,7 @@ const AppexChartComponent = React.forwardRef(({chartContext,options,...props},re
|
|
|
11
11
|
if (chartContext.current && typeof chartContext.current.destroy === 'function') {
|
|
12
12
|
setTimeout(()=>{
|
|
13
13
|
try {chartContext.current.destroy();} catch{}
|
|
14
|
-
},
|
|
14
|
+
},1000);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
},[]);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from '$react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import {defaultStr} from "$utils";
|
|
3
|
+
import {defaultStr,defaultObj,uniqid} from "$utils";
|
|
4
4
|
import {extend} from "./utils"
|
|
5
5
|
import stableHash from 'stable-hash';
|
|
6
6
|
import Chart from "./appexChart";
|
|
@@ -14,30 +14,34 @@ import Chart from "./appexChart";
|
|
|
14
14
|
* options {number} - les options supplémentaires au chart
|
|
15
15
|
*
|
|
16
16
|
*/
|
|
17
|
-
const ChartComponent = React.forwardRef(({type, height, width, series, options,testID,webViewProps, ...props },ref)=>{
|
|
17
|
+
const ChartComponent = React.forwardRef(({type, height,chartId, width, series, options,testID,webViewProps, ...props },ref)=>{
|
|
18
18
|
const chartContext = React.useRef(null);
|
|
19
|
+
options = defaultObj(options);
|
|
20
|
+
const chartIdRef = React.useRef(defaultStr(chartId,options.chart?.id,uniqid("chart-id")));
|
|
19
21
|
const config = extend(options,{
|
|
20
22
|
chart: {
|
|
21
23
|
type,
|
|
22
24
|
height,
|
|
23
|
-
width
|
|
25
|
+
width,
|
|
26
|
+
id : chartIdRef.current
|
|
24
27
|
},
|
|
25
28
|
series,
|
|
26
29
|
});
|
|
30
|
+
config.chart.id = chartIdRef.current;
|
|
27
31
|
testID = defaultStr(testID,"RN_ChartComponent");
|
|
28
32
|
const prevWidth = React.usePrevious(width), prevHeight = React.usePrevious(height);
|
|
29
|
-
const prevOptions = React.usePrevious(options,
|
|
33
|
+
const prevOptions = React.usePrevious(options,JSON.stringify);
|
|
30
34
|
const prevSeries = React.usePrevious(series,JSON.stringify);
|
|
31
35
|
React.useEffect(()=>{
|
|
32
36
|
if(chartContext.current && chartContext.current.updateOptions){
|
|
33
|
-
if((
|
|
37
|
+
if((prevSeries == series) || width != prevWidth || height != prevHeight){
|
|
34
38
|
chartContext.current.updateOptions(config);
|
|
35
|
-
} else if(
|
|
39
|
+
} else if(prevOptions != options){
|
|
36
40
|
chartContext.current.updateSeries(series);
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
43
|
},[stableHash({type,options,series,width,height})])
|
|
40
|
-
return <Chart {...props} options={config} chartContext={chartContext} testID={testID} ref={ref}/>
|
|
44
|
+
return <Chart {...props} options={config} chartId={chartIdRef.current} chartContext={chartContext} testID={testID} ref={ref}/>
|
|
41
45
|
});
|
|
42
46
|
|
|
43
47
|
|
|
@@ -3,6 +3,7 @@ import {isObj,defaultStr} from "$utils";
|
|
|
3
3
|
import View from "$ecomponents/View";
|
|
4
4
|
import Portal from "$ecomponents/Portal";
|
|
5
5
|
import theme,{Colors} from "$theme";
|
|
6
|
+
import Dimensions,{isMobileMedia,isTabletMedia,isDesktopMedia} from "$dimensions";
|
|
6
7
|
import {
|
|
7
8
|
StyleSheet,
|
|
8
9
|
TouchableOpacity,
|
|
@@ -598,7 +599,7 @@ export default class DropdownAlert extends Component {
|
|
|
598
599
|
wrapperStyle,
|
|
599
600
|
tapToCloseEnabled,
|
|
600
601
|
accessibilityLabel,
|
|
601
|
-
testID,
|
|
602
|
+
testID:customTestId,
|
|
602
603
|
accessible,
|
|
603
604
|
contentContainerStyle,
|
|
604
605
|
defaultTextContainer,
|
|
@@ -608,6 +609,7 @@ export default class DropdownAlert extends Component {
|
|
|
608
609
|
showCancel,
|
|
609
610
|
imageStyle,
|
|
610
611
|
} = this.props;
|
|
612
|
+
const testID = defaultStr(customTestId,"RN_DropdownAlert");
|
|
611
613
|
const {animationValue, bottomValue, height} = this.state;
|
|
612
614
|
const {type, payload} = this.alertData;
|
|
613
615
|
let style = this.getStyleForType(type);
|
|
@@ -641,14 +643,6 @@ export default class DropdownAlert extends Component {
|
|
|
641
643
|
right: 0,
|
|
642
644
|
elevation: elevation,
|
|
643
645
|
};
|
|
644
|
-
/*wrapperAnimStyle.zIndex = 1000100;
|
|
645
|
-
if (zIndex != null) {
|
|
646
|
-
wrapperAnimStyle.zIndex = zIndex;
|
|
647
|
-
}*/
|
|
648
|
-
let ContentView = View//SafeAreaView;
|
|
649
|
-
if (IS_IOS_BELOW_11 || IS_ANDROID) {
|
|
650
|
-
ContentView = View;
|
|
651
|
-
}
|
|
652
646
|
const activeOpacity = !tapToCloseEnabled || showCancel ? 1 : 0.95;
|
|
653
647
|
const onPress = !tapToCloseEnabled
|
|
654
648
|
? null
|
|
@@ -658,28 +652,45 @@ export default class DropdownAlert extends Component {
|
|
|
658
652
|
<Animated.View
|
|
659
653
|
ref={(ref) => (this.mainView = ref)}
|
|
660
654
|
{...this.panResponder.panHandlers}
|
|
655
|
+
testID={testID+"_AnimatedView"}
|
|
661
656
|
style={[wrapperAnimStyle, wrapperStyle]}>
|
|
662
657
|
<TouchableOpacity
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
<View style={style}>
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
658
|
+
activeOpacity={activeOpacity}
|
|
659
|
+
onPress={onPress}
|
|
660
|
+
disabled={!tapToCloseEnabled}
|
|
661
|
+
onLayout={(event) => this._onLayoutEvent(event)}
|
|
662
|
+
testID={testID}
|
|
663
|
+
accessibilityLabel={accessibilityLabel}
|
|
664
|
+
accessible={accessible}>
|
|
665
|
+
<View style={style} testID={testID+"_ContentContainer"}>
|
|
666
|
+
<View testID={testID+"_ContentWrapper"} style={[contentContainerStyle,getContainerStyle().style]}
|
|
667
|
+
mediaQueryUpdateNativeProps={(args)=>{
|
|
668
|
+
return getContainerStyle(args);
|
|
669
|
+
}}
|
|
670
|
+
>
|
|
671
|
+
{this._renderImage(imageSrc, imageStyle)}
|
|
672
|
+
<View testID={testID+"_Content"} style={defaultTextContainer}>
|
|
673
|
+
{this._renderTitle()}
|
|
674
|
+
{this._renderMessage()}
|
|
675
|
+
</View>
|
|
676
|
+
{this._renderCancel(showCancel)}
|
|
676
677
|
</View>
|
|
677
|
-
{this._renderCancel(showCancel)}
|
|
678
|
-
</ContentView>
|
|
679
678
|
</View>
|
|
680
679
|
</TouchableOpacity>
|
|
681
680
|
</Animated.View>
|
|
682
681
|
</Portal>
|
|
683
682
|
);
|
|
684
683
|
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const getContainerStyle = (args)=>{
|
|
687
|
+
if(!isObj(args)){
|
|
688
|
+
args = {isMobile : isMobileMedia(),isTablet : isTabletMedia()};
|
|
689
|
+
}
|
|
690
|
+
const {width} = Dimensions.get("window");
|
|
691
|
+
return {
|
|
692
|
+
style : {
|
|
693
|
+
maxWidth : args.isMobile ? (90*width)/100 : args.isTablet? Math.max((70*width/100),350) : 500
|
|
694
|
+
}
|
|
695
|
+
}
|
|
685
696
|
}
|
|
@@ -9,8 +9,122 @@ import Group from "./GroupComponent";
|
|
|
9
9
|
import Portal from "$ecomponents/Portal";
|
|
10
10
|
import {isAllowedFromStr} from "$cauth/perms";
|
|
11
11
|
|
|
12
|
+
const activeRef = {current:null};
|
|
13
|
+
|
|
14
|
+
export const isValid = (context)=>{
|
|
15
|
+
if(!isObj(context) || !isNonNullString(context.fabId) || typeof context.show !=="function" || context.hide !="function") return false;
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
/*** retourne le fab d'ont l'id est passé en paramètre */
|
|
19
|
+
export const getFab = (fabId)=>{
|
|
20
|
+
if(!isNonNullString(fabId)) return null;
|
|
21
|
+
return isValid(allFabs[fabId])? allFabs[fabId] : null;
|
|
22
|
+
}
|
|
23
|
+
const allFabs = {};
|
|
24
|
+
///les ids des fabs
|
|
25
|
+
const fabIdRefs = {current:[]};
|
|
26
|
+
|
|
27
|
+
//ajoute l'id du fab dans la lite des fabActif
|
|
28
|
+
export const activateFabId = (fabId)=>{
|
|
29
|
+
if(!isNonNullString(fabId)) return fabIdRefs.current;
|
|
30
|
+
const nIds = desactivateFabId(fabId);
|
|
31
|
+
///le fab active devient en top des id
|
|
32
|
+
nIds.push(fabId);
|
|
33
|
+
fabIdRefs.current = nIds;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const desactivateFabId = (fabId)=>{
|
|
37
|
+
if(!isNonNullString(fabId)) return fabIdRefs.current;
|
|
38
|
+
const nIds = [];
|
|
39
|
+
for(let i in fabIdRefs.current){
|
|
40
|
+
if(fabIdRefs.current[i] != fabId){
|
|
41
|
+
nIds.push(fabIdRefs.current[i]);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
fabIdRefs.current = nIds;
|
|
45
|
+
return nIds;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const isActive = (fabId)=>{
|
|
49
|
+
return isNonNullString(fabId) && fabIdRefs.current[fabIdRefs.current.length-1] == fabId && isValid(getFab(fabId));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const MANAGER = {
|
|
53
|
+
get active (){
|
|
54
|
+
return activeRef.current;
|
|
55
|
+
},
|
|
56
|
+
///la liste des fabs
|
|
57
|
+
get all (){
|
|
58
|
+
return allFabs;
|
|
59
|
+
},
|
|
60
|
+
///la liste des fabs Id
|
|
61
|
+
get fabIds (){
|
|
62
|
+
return fabIdRefs.current;
|
|
63
|
+
},
|
|
64
|
+
set active(active){
|
|
65
|
+
active = isValid(active)? active : null;
|
|
66
|
+
if(active){
|
|
67
|
+
///on désactive l'ancien fab qui était actif
|
|
68
|
+
if(isValid(activeRef.current)){
|
|
69
|
+
activeRef.current.hide();
|
|
70
|
+
}
|
|
71
|
+
activateFabId(active.fabId);
|
|
72
|
+
activeRef.current = active;
|
|
73
|
+
} else {
|
|
74
|
+
//l'ancien fab devient active
|
|
75
|
+
let length = fabIdRefs.current.length-1;
|
|
76
|
+
let prevActive = null,prevFabId = null;
|
|
77
|
+
///ça veut dire que l'ancien fab active a été démonté
|
|
78
|
+
if(isValid(activeRef.current)){
|
|
79
|
+
prevFabId = activeRef.current.fabId
|
|
80
|
+
}
|
|
81
|
+
while(length >=0 && !isValid(prevActive)){
|
|
82
|
+
const fId = fabIdRefs.current[length];
|
|
83
|
+
if(isNonNullString(fId) && fId !== prevFabId){
|
|
84
|
+
prevActive = allFabs[fId];
|
|
85
|
+
if(isValid(prevActive)){
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
length --;
|
|
90
|
+
}
|
|
91
|
+
if(!prevActive){
|
|
92
|
+
fabIdRefs.current = [];
|
|
93
|
+
Object.map(allFabs,(f,i)=>{
|
|
94
|
+
delete allFabs[i];
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
if(isValid(prevActive)){
|
|
98
|
+
prevActive.show();
|
|
99
|
+
activateFabId(prevActive.fabId);
|
|
100
|
+
}
|
|
101
|
+
activeRef.current = prevActive;
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
get hasActive(){
|
|
105
|
+
return isValid(activeRef.current);
|
|
106
|
+
},
|
|
107
|
+
get get (){
|
|
108
|
+
return getFab;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const activate = (args)=>{
|
|
113
|
+
const {context,fabId} = args;
|
|
114
|
+
if(!isNonNullString(fabId)|| !isObj(context)) return false;
|
|
115
|
+
let hasFound = false;
|
|
116
|
+
for(let i in allFabs){
|
|
117
|
+
if(allFabs[i]?.fabId == fabId){
|
|
118
|
+
hasFound = true;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
12
123
|
const FabGroupComponent = React.forwardRef((props,innerRef)=>{
|
|
13
|
-
let {openedIcon,screenName,display:customDisplay,primary,actionMutator,secondary,onOpen,prepareActions,fabStyle,open:customOpen,onClose,onStateChange:customOnStateChange,closedIcon,color,actions:customActions,children,...customRest} = props;
|
|
124
|
+
let {openedIcon,screenName,fabId,display:customDisplay,onMount,onUnmount,primary,actionMutator,secondary,onOpen,prepareActions,fabStyle,open:customOpen,onClose,onStateChange:customOnStateChange,closedIcon,color,actions:customActions,children,...customRest} = props;
|
|
125
|
+
const fabIdRef = React.useRef(defaultStr(fabId,uniqid("fab-id-ref")));
|
|
126
|
+
fabId = fabIdRef.current;
|
|
127
|
+
const isMountedRef = React.useRef(false);
|
|
14
128
|
const [state, setState] = React.useStateIfMounted({
|
|
15
129
|
open: typeof customOpen =='boolean'? customOpen : false,
|
|
16
130
|
display : typeof customDisplay ==='boolean'? customDisplay : true,
|
|
@@ -27,6 +141,8 @@ const FabGroupComponent = React.forwardRef((props,innerRef)=>{
|
|
|
27
141
|
const context = {
|
|
28
142
|
open:x=>setState({...state,open:true}),
|
|
29
143
|
close : x=> {setState({...state,open:false})},
|
|
144
|
+
fabId,
|
|
145
|
+
id : fabId,
|
|
30
146
|
hide : x=> {
|
|
31
147
|
setState({...state,display:false})
|
|
32
148
|
},
|
|
@@ -35,9 +151,11 @@ const FabGroupComponent = React.forwardRef((props,innerRef)=>{
|
|
|
35
151
|
},
|
|
36
152
|
isHidden : x => !state.display,
|
|
37
153
|
isShown : x => state.display,
|
|
154
|
+
isVisible : x=> state.display,
|
|
38
155
|
isClosed : x => !state.open,
|
|
39
156
|
isOpened : x => state.open,
|
|
40
157
|
}
|
|
158
|
+
allFabs[fabId] = context;
|
|
41
159
|
const {open,display} = state;
|
|
42
160
|
openedIcon = defaultStr(openedIcon,"close");
|
|
43
161
|
closedIcon = defaultStr(closedIcon,MENU_ICON);
|
|
@@ -77,11 +195,24 @@ const FabGroupComponent = React.forwardRef((props,innerRef)=>{
|
|
|
77
195
|
},[customActions,prepareActions,open])();
|
|
78
196
|
|
|
79
197
|
React.useEffect(()=>{
|
|
198
|
+
onMount && onMount(context);
|
|
80
199
|
React.setRef(innerRef,context);
|
|
200
|
+
isMountedRef.current = true;
|
|
81
201
|
return ()=>{
|
|
202
|
+
onUnmount && onUnmount({fabId});
|
|
203
|
+
delete allFabs[fabId];
|
|
204
|
+
desactivateFabId(fabId);
|
|
205
|
+
isMountedRef.current = false;
|
|
82
206
|
React.setRef(innerRef,null);
|
|
83
207
|
}
|
|
84
208
|
},[])
|
|
209
|
+
React.useEffect(()=>{
|
|
210
|
+
if(display){
|
|
211
|
+
MANAGER.active = context;
|
|
212
|
+
} else {
|
|
213
|
+
MANAGER.active = null;
|
|
214
|
+
}
|
|
215
|
+
},[display]);
|
|
85
216
|
return <Portal>
|
|
86
217
|
<Group
|
|
87
218
|
{...rest}
|
|
@@ -112,6 +243,9 @@ const actionType = PropTypes.shape({
|
|
|
112
243
|
});
|
|
113
244
|
FabGroupComponent.propTypes = {
|
|
114
245
|
...defaultObj(FAB.Group.propTypes),
|
|
246
|
+
fabId : PropTypes.string,//l'id du fab
|
|
247
|
+
onMount : PropTypes.func, ///lorsque le composant est monté
|
|
248
|
+
onUnmount : PropTypes.func, //lorsque le composant est démonté
|
|
115
249
|
actionMutator : PropTypes.func,
|
|
116
250
|
prepareActions : PropTypes.bool, //si un retraitement sera effectué sur les actions du FAB
|
|
117
251
|
onOpen : PropTypes.func,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Pressable } from "react-native";
|
|
2
2
|
import { navigate } from "$cnavigation";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
|
-
import {defaultStr} from "$utils";
|
|
4
|
+
import {defaultStr,defaultNumber} from "$utils";
|
|
5
5
|
import React from "$react";
|
|
6
|
+
|
|
6
7
|
const LinkComponent= React.forwardRef((props,ref)=>{
|
|
7
|
-
let {Component,navigation,children,params,stopEventPropagation,source,onPress,routeParams,routeName,routeSource,routeType, ...rest} = props;
|
|
8
|
+
let {Component,navigation,children,params,stopEventPropagation,timeout,delay,source,onPress,routeParams,routeName,routeSource,routeType, ...rest} = props;
|
|
8
9
|
const onRoutePress = (e)=>{
|
|
9
10
|
if(stopEventPropagation !== false){
|
|
10
11
|
React.stopEventPropagation(e);
|
|
@@ -12,7 +13,9 @@ const LinkComponent= React.forwardRef((props,ref)=>{
|
|
|
12
13
|
if(onPress && onPress(e) === false){
|
|
13
14
|
return;
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
+
setTimeout(()=>{
|
|
17
|
+
navigate({routeName,previousRoute:undefined,routeParams,params,type:routeType,source:defaultStr(routeSource,source)},navigation);
|
|
18
|
+
},defaultNumber(timeout,delay))
|
|
16
19
|
};
|
|
17
20
|
if(typeof children =='function'){
|
|
18
21
|
return children ({...rest,onPress:onRoutePress},ref);
|
|
@@ -26,6 +29,8 @@ const LinkComponent= React.forwardRef((props,ref)=>{
|
|
|
26
29
|
LinkComponent.propTypes = {
|
|
27
30
|
stopEventPropagation : PropTypes.bool, //si la propagation d'évènement sera effective une fois qu'on ait cliqué sur le lien
|
|
28
31
|
onPress : PropTypes.func,
|
|
32
|
+
timeout : PropTypes.number,
|
|
33
|
+
delay : PropTypes.number,//le delay d'attente lorsqu'on clique sur l'élément avant de faire la navigation
|
|
29
34
|
routeName : PropTypes.string.isRequired,
|
|
30
35
|
routeParams : PropTypes.object, //les paramètres à passer à la route
|
|
31
36
|
routeType : PropTypes.string, //le type de route : stack ou drawer
|
package/src/layouts/Fab/index.js
CHANGED
|
@@ -16,6 +16,7 @@ const FABContainer = React.forwardRef((props,ref)=>{
|
|
|
16
16
|
ref = ref || createFabRef(screenName);
|
|
17
17
|
React.useEffect(()=>{
|
|
18
18
|
const onFocusFab = ({sanitizedName})=>{
|
|
19
|
+
console.log(sanitizedName," is dddddd focused")
|
|
19
20
|
const isFocused = sanitizedName === sScreenName;
|
|
20
21
|
if(isFocused && ref.current && ref.current.show){
|
|
21
22
|
ref.current.show();
|
package/src/screens/NetWork
DELETED