@fto-consult/expo-ui 7.24.3 → 8.0.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/App.js +6 -3
- package/app.json +1 -0
- package/bin/create-app.js +13 -4
- package/copy-env-file.js +1 -1
- package/docs/navigation/drawerItems/index.js +16 -0
- package/docs/navigation/drawerSections.js +14 -0
- package/docs/screens/Datagrid/{Datagrid.js → index.js} +1 -1
- package/docs/screens/index.js +1 -0
- package/package.json +141 -139
- package/src/App.js +1 -1
- package/src/AppEntry/index.js +1 -0
- package/src/components/AppBar/BackAction.js +46 -0
- package/src/components/AppBar/Content.js +3 -3
- package/src/components/AppBar/index.js +8 -3
- package/src/components/Datagrid/Accordion/index.js +1 -0
- package/src/components/Datagrid/Common/Common.js +1 -1
- package/src/components/Datagrid/Test/index.js +1 -1
- package/src/components/Drawer/DrawerItems/index.js +1 -1
- package/src/components/Dropdown/index.js +30 -8
- package/src/components/ErrorBoundary/ErrorMessage.js +5 -5
- package/src/components/Form/Fields/SelectTableData/Component.js +2 -2
- package/src/components/Image/index.js +13 -15
- package/src/components/Link/index.js +2 -2
- package/src/components/SplashScreen/index.js +9 -1
- package/src/components/TableLink/index.js +2 -1
- package/src/components/TouchableRipple/index.js +38 -0
- package/src/components/TouchableRipple/index.web.js +22 -0
- package/src/context/Provider.js +0 -1
- package/src/layouts/AppBar/index.js +1 -1
- package/src/layouts/DatabaseStatistics/DatabaseStatistic.js +6 -11
- package/src/layouts/DatabaseStatistics/index.js +10 -4
- package/src/layouts/Screen/TableData.js +15 -5
- package/src/media/camera.js +13 -1
- package/src/media/camera.native.js +135 -2
- package/src/media/file-system/utils/FileSaver.native.js +62 -25
- package/src/media/file-system/utils/native/index.js +11 -1
- package/src/media/file-system/utils/web/index.js +21 -1
- package/src/media/index.js +27 -35
- package/src/media/utils.js +27 -0
- package/src/navigation/Drawer/items/index.js +1 -1
- package/src/navigation/animationTypes.js +48 -0
- package/src/navigation/index.js +11 -7
- package/src/screens/Auth/Profile.js +12 -0
- package/src/screens/Help/openLibraries.js +9 -9
- package/webpack.config.js +5 -0
- package/docs/drawerItems/index.js +0 -17
- package/docs/drawerItems/introduction.js +0 -8
@@ -5,7 +5,7 @@
|
|
5
5
|
import PropTypes from "prop-types";
|
6
6
|
import View from "$ecomponents/View";
|
7
7
|
import {Dimensions,Pressable,StyleSheet,Animated,} from "react-native";
|
8
|
-
import {TouchableRipple} from "react-native-paper";
|
8
|
+
import {TouchableRipple as RNTouchableRiple} from "react-native-paper";
|
9
9
|
import Divider from "$ecomponents/Divider";
|
10
10
|
import React, {Fragment,Component as AppComponent} from "$react";
|
11
11
|
import theme,{Colors} from "$theme";
|
@@ -26,6 +26,7 @@ import Chip from "$ecomponents/Chip";
|
|
26
26
|
import {Content as BottomSheet,Menu as BottomSheetMenu,getContentHeight} from "$ecomponents/BottomSheet";
|
27
27
|
import {isWeb} from "$cplatform";
|
28
28
|
import Tooltip from "$ecomponents/Tooltip";
|
29
|
+
import TouchableRipple from "$ecomponents/TouchableRipple";
|
29
30
|
|
30
31
|
const _isIos = isIos();
|
31
32
|
|
@@ -64,7 +65,22 @@ class DropdownComponent extends AppComponent {
|
|
64
65
|
if(React.isValidElement(item,true) || isDecimal(item) || typeof item =='boolean') return item;
|
65
66
|
if(isObj(item) ) {
|
66
67
|
const itemLabel = this.props.itemLabel;
|
67
|
-
if(isNonNullString(itemLabel)
|
68
|
+
if(isNonNullString(itemLabel)){
|
69
|
+
let iLabel = "";
|
70
|
+
itemLabel.trim().split(",").map((itL)=>{
|
71
|
+
let itV = item.hasOwnProperty(itL) ? item[itL] : undefined;
|
72
|
+
if(typeof formatItemLabel ==="function"){
|
73
|
+
const itt = formatItemLabel({value:itV,code:itV,columnField:itL,label:itL,item});
|
74
|
+
if(isNonNullString(itt) || typeof itt =="number"){
|
75
|
+
itV = String(itt);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
if(itV !== undefined){
|
79
|
+
iLabel += " "+String(itV);
|
80
|
+
}
|
81
|
+
});
|
82
|
+
if(iLabel) return iLabel;
|
83
|
+
}
|
68
84
|
if(isNonNullString(item.label)) return item.label;
|
69
85
|
return defaultStr(item.text,item[index]);
|
70
86
|
}
|
@@ -896,10 +912,9 @@ class DropdownComponent extends AppComponent {
|
|
896
912
|
const testID = defaultStr(dropdownProps.testID,"RN_DropdownComponent");
|
897
913
|
const defRight = defaultVal(textInputProps.right,inputProps.right);
|
898
914
|
const enableCopy = defaultBool(inputProps.enableCopy,textInputProps.enableCopy,(iconDisabled || (!multiple && !showAdd)) && !loadingElement ?true : false);
|
899
|
-
const anchor = <
|
900
|
-
activeOpacity = {0.3}
|
915
|
+
const anchor = <TouchableRipple
|
901
916
|
onPress={this.open.bind(this)}
|
902
|
-
disabled = {disabled}
|
917
|
+
disabled = {disabled || isWeb()}
|
903
918
|
onLayout={bindResizeEvents === false ? undefined : this.onLayout.bind(this)}
|
904
919
|
style = {{pointerEvents}}
|
905
920
|
aria-label={defaultStr(dropdownProps["aria-label"],label,text)}
|
@@ -958,7 +973,7 @@ class DropdownComponent extends AppComponent {
|
|
958
973
|
{!canHandle && isFlatMode && <ProgressBar color={theme.colors.secondary} {...defaultObj(progressBarProps)} indeterminate />}
|
959
974
|
{helperText}
|
960
975
|
</View>
|
961
|
-
</
|
976
|
+
</TouchableRipple>
|
962
977
|
|
963
978
|
let restProps = {};
|
964
979
|
if(!isMob){
|
@@ -1097,7 +1112,7 @@ class DropdownComponent extends AppComponent {
|
|
1097
1112
|
if(renderTag && (self.state.nodes[key].valueKey in self.state.selectedValuesKeys)){
|
1098
1113
|
return null;
|
1099
1114
|
}
|
1100
|
-
|
1115
|
+
let node = self.state.nodes[key];
|
1101
1116
|
const {index,value,valueKey} = node;
|
1102
1117
|
const _isSelected = self.isSelected(value,valueKey);
|
1103
1118
|
if(dynamicContent){
|
@@ -1125,7 +1140,7 @@ class DropdownComponent extends AppComponent {
|
|
1125
1140
|
//style = {[[theme.styles.h100]]}
|
1126
1141
|
tooltipProps = {{style:[theme.styles.h100,theme.styles.w100],testID:testID+"_DropdownTooltipPopoverContainer"}}
|
1127
1142
|
onPress={onItemPress}
|
1128
|
-
Component={
|
1143
|
+
Component={RNTouchableRiple}
|
1129
1144
|
//testID={testID+"Container"}
|
1130
1145
|
style={[
|
1131
1146
|
styles.itemContainer,{minHeight:!isBigList?MIN_HEIGHT:undefined},
|
@@ -1297,6 +1312,13 @@ DropdownComponent.propTypes = {
|
|
1297
1312
|
"aria-label" : PropTypes.string,
|
1298
1313
|
compare : PropTypes.func,
|
1299
1314
|
temProps : PropTypes.object,
|
1315
|
+
/*
|
1316
|
+
le champ itemLabel peut être une chaine de caractère de plusieurs champs constituant l'item et séparé par des virgule
|
1317
|
+
si si cette valeur est définine, alors ladite fonction sera appelée pour chacun des champ constitué par itemLabel et preneant en paramètre
|
1318
|
+
@return {string|number}
|
1319
|
+
@param {<Object { item:{Object},label:{string : le label en cours}, value{any, item[label]}}>}
|
1320
|
+
*/
|
1321
|
+
formatItemLabel : PropTypes.func,
|
1300
1322
|
itemLabel : PropTypes.string,//le nom du champ à utiliser pour le rendu du libelé la méthode appelée pour retourne le libelé de l'item
|
1301
1323
|
itemValue : PropTypes.oneOfType([PropTypes.func]),//le nom du champ de la valeur à récupérer
|
1302
1324
|
renderItem : PropTypes.oneOfType([PropTypes.func]),
|
@@ -22,15 +22,15 @@ const ErrorMessage = React.forwardRef(function(props,ref){
|
|
22
22
|
}
|
23
23
|
if(!error || !info || !error.toString) return null;
|
24
24
|
const pointerEvents = 'auto';
|
25
|
-
const backgroundColor = theme.colors.
|
26
|
-
const color = theme.colors.
|
25
|
+
const backgroundColor = theme.colors.background;
|
26
|
+
const color = theme.colors.text;
|
27
27
|
return <Portal>
|
28
28
|
<Screen {...props} modal={false}>
|
29
|
-
<View ref={ref} testID={`${testID}_ErrorMessageContainer`} style={[{pointerEvents},styles.container
|
29
|
+
<View ref={ref} testID={`${testID}_ErrorMessageContainer`} style={[{pointerEvents},{backgroundColor},styles.container]}>
|
30
30
|
<View style={[styles.content,{pointerEvents}]} testID={`${testID}_ErrorMessageContentContainer`}>
|
31
31
|
<Label style={[styles.title,{color}]}>Oops!</Label>
|
32
32
|
<Label style={[styles.subtitle,{color}]}>{'Une erreur est survenue'}</Label>
|
33
|
-
<Label style={styles.error}>{error.toString()}</Label>
|
33
|
+
<Label style={[styles.subtitle,{color:theme.colors.error}]}>{error.toString()}</Label>
|
34
34
|
<Button mode="contained" iconProps={{marginVertical:0,pointerEvents,paddingVertical:0}} icon='home-variant' style={{backgroundColor:theme.colors.primary,marginHorizontal:10}} labelStyle={{color:theme.colors.primaryLabel}} onPress={goToHome}>
|
35
35
|
Retour à l'accueil
|
36
36
|
</Button>
|
@@ -80,7 +80,7 @@ const styles = StyleSheet.create({
|
|
80
80
|
},
|
81
81
|
title: {
|
82
82
|
fontSize: 48,
|
83
|
-
fontWeight: '
|
83
|
+
fontWeight: '500',
|
84
84
|
paddingBottom: 16,
|
85
85
|
},
|
86
86
|
subtitle: {
|
@@ -34,7 +34,7 @@ const TableDataSelectField = React.forwardRef(({foreignKeyColumn,swrOptions,fore
|
|
34
34
|
}
|
35
35
|
if(isNonNullString(foreignKeyLabel)){
|
36
36
|
foreignKeyLabel = foreignKeyLabel.trim().ltrim("[").rtrim("]").split(",");
|
37
|
-
if(
|
37
|
+
if(isNonNullString(foreignKeyColumn)){
|
38
38
|
foreignKeyLabel = foreignKeyLabel.filter((f)=>f?.toLowerCase()?.trim() !== foreignKeyColumn.toLowerCase().trim());
|
39
39
|
}
|
40
40
|
}
|
@@ -159,7 +159,7 @@ const TableDataSelectField = React.forwardRef(({foreignKeyColumn,swrOptions,fore
|
|
159
159
|
fetchedResultRef.current = args;
|
160
160
|
return fetchedResultRef.current;
|
161
161
|
}).catch((e)=>{
|
162
|
-
console.log(e," fetching list of data select table data ",foreignKeyColumn,foreignKeyTable)
|
162
|
+
console.log(e," fetching list of data select table data ",foreignKeyColumn,foreignKeyTable,props)
|
163
163
|
});
|
164
164
|
},
|
165
165
|
showError : false,
|
@@ -11,7 +11,7 @@ import {isMobileNative} from "$cplatform";
|
|
11
11
|
//import Signature from "$ecomponents/Signature";
|
12
12
|
import Label from "$ecomponents/Label";
|
13
13
|
//import Editor from "./Editor";
|
14
|
-
|
14
|
+
import {Component as CameraComponent} from "$emedia/camera";
|
15
15
|
|
16
16
|
import {pickImage,nonZeroMin,canTakePhoto,takePhoto} from "$emedia";
|
17
17
|
import addPhoto from "$eassets/add_photo.png";
|
@@ -57,7 +57,7 @@ export default function ImageComponent(props){
|
|
57
57
|
let {disabled,onMount,defaultSource,editable,onUnmount,label,text,labelProps,readOnly,beforeRemove,
|
58
58
|
onChange,draw,round,drawText,drawLabel,rounded,defaultSrc,
|
59
59
|
createSignatureOnly,pickImageProps,width,height,cropProps,size,resizeProps,containerProps,
|
60
|
-
menuProps,pickUri,drawProps,imageProps,length,testID,...rest} = props;
|
60
|
+
menuProps,pickUri,drawProps,imageProps,length,testID,withLabel,...rest} = props;
|
61
61
|
rest = defaultObj(rest);
|
62
62
|
pickImageProps = defaultObj(pickImageProps);
|
63
63
|
cropProps = defaultObj(cropProps);
|
@@ -222,17 +222,15 @@ export default function ImageComponent(props){
|
|
222
222
|
}
|
223
223
|
})
|
224
224
|
}
|
225
|
-
(
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
}
|
235
|
-
})();
|
225
|
+
if(isMobileNative() && !readOnly){
|
226
|
+
menuItems.push({
|
227
|
+
label : 'Enregistrer une photo',
|
228
|
+
icon : 'camera',
|
229
|
+
onPress : (a)=>{
|
230
|
+
takePhoto().then(handlePickedImage);
|
231
|
+
}
|
232
|
+
})
|
233
|
+
}
|
236
234
|
|
237
235
|
if(canUpdate && !readOnly){
|
238
236
|
menuItems.push({
|
@@ -253,7 +251,7 @@ export default function ImageComponent(props){
|
|
253
251
|
}
|
254
252
|
})
|
255
253
|
}
|
256
|
-
const _label = defaultString(label);
|
254
|
+
const _label = withLabel !== false ? defaultString(label) : "";
|
257
255
|
const isDisabled = menuItems.length > 0 ? true : false;
|
258
256
|
return <View testID={testID+"_FagmentContainer"}>
|
259
257
|
{!createSignatureOnly ? (<Menu
|
@@ -261,7 +259,7 @@ export default function ImageComponent(props){
|
|
261
259
|
disabled = {isDisabled}
|
262
260
|
anchor = {(props)=>{
|
263
261
|
return <View aria-label={_label} testID={testID+"_Container"} {...containerProps} style={[label?styles.align:null,containerProps.style,{pointerEvents:disabled|| readOnly? "none":"auto"},label?styles.container:null]}>
|
264
|
-
{<Label testID={testID+"_Label"} {...labelProps} disabled={disabled} style={[styles.label,labelProps.style]}>{label}</Label
|
262
|
+
{withLabel !== false ? <Label testID={testID+"_Label"} {...labelProps} disabled={disabled} style={[styles.label,labelProps.style]}>{label}</Label>:null}
|
265
263
|
{<Avatar
|
266
264
|
resizeMethod = {"auto"}
|
267
265
|
resizeMode = {"contain"}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import
|
1
|
+
import TouchableRipple from "$ecomponents/TouchableRipple";
|
2
2
|
import { navigate } from "$cnavigation";
|
3
3
|
import PropTypes from "prop-types";
|
4
4
|
import {defaultStr,isValidUrl,isValidEmail,defaultNumber} from "$cutils";
|
@@ -28,7 +28,7 @@ const LinkComponent= React.forwardRef(({Component,navigation,children,params,sto
|
|
28
28
|
if(typeof children =='function'){
|
29
29
|
return children ({...rest,onPress:onRoutePress},ref);
|
30
30
|
}
|
31
|
-
Component = React.isComponent(Component)? Component :
|
31
|
+
Component = React.isComponent(Component)? Component : TouchableRipple;
|
32
32
|
return <Component ref={ref} {...rest} onPress={onRoutePress}>
|
33
33
|
{children}
|
34
34
|
</Component>
|
@@ -18,7 +18,6 @@ import styles, {
|
|
18
18
|
_dynamicBackgroundOpacity,
|
19
19
|
} from "./styles"
|
20
20
|
import {useAppComponent} from "$econtext/hooks";
|
21
|
-
import theme from "$theme";
|
22
21
|
|
23
22
|
const SplashScreenComponent = ({isLoaded,children , duration, delay,logoWidth,logoHeight,backgroundColor,imageBackgroundSource,imageBackgroundResizeMode,
|
24
23
|
testID})=>{
|
@@ -28,6 +27,7 @@ const SplashScreenComponent = ({isLoaded,children , duration, delay,logoWidth,lo
|
|
28
27
|
});
|
29
28
|
const { loadingProgress, animationDone} = state;
|
30
29
|
const prevIsLoaded = React.usePrevious(isLoaded);
|
30
|
+
const timerRef = React.useRef(null);
|
31
31
|
React.useEffect(()=>{
|
32
32
|
if(isLoaded && !prevIsLoaded){
|
33
33
|
Animated.timing(loadingProgress, {
|
@@ -41,6 +41,14 @@ const SplashScreenComponent = ({isLoaded,children , duration, delay,logoWidth,lo
|
|
41
41
|
animationDone:true,
|
42
42
|
})
|
43
43
|
})
|
44
|
+
} else if(isLoaded){
|
45
|
+
clearTimeout(timerRef.current);
|
46
|
+
timerRef.current = setTimeout(()=>{
|
47
|
+
if(isLoaded && !animationDone){
|
48
|
+
setState({...state,animationDone:true});
|
49
|
+
}
|
50
|
+
clearTimeout(timerRef.current);
|
51
|
+
},delay|2000);
|
44
52
|
}
|
45
53
|
});
|
46
54
|
testID = defaultStr(testID,"RN_SplashscreenComponent")
|
@@ -15,6 +15,7 @@ import {navigateToTableData,navigateToStructData} from "$enavigation/utils";
|
|
15
15
|
import Auth from "$cauth";
|
16
16
|
import fetch from "$capi/fetch";
|
17
17
|
import useContext from "$econtext/hooks";
|
18
|
+
import TouchableRipple from "$ecomponents/TouchableRipple";
|
18
19
|
/***** la fonction fetchForeignData permet de spécifier s'il s'agit d'une données de structure où non
|
19
20
|
dans le champ isStructData
|
20
21
|
*/
|
@@ -22,7 +23,7 @@ const TableLinKComponent = React.forwardRef(({containerProps,children,labelProps
|
|
22
23
|
const {testID,onPressLink,disabled,readOnly,fetchData,navigate,isAllowed:checkIfAllowed,Component,...rest} = usePrepareProps(props);
|
23
24
|
containerProps = defaultObj(containerProps);
|
24
25
|
labelProps = defaultObj(labelProps);
|
25
|
-
const CP = disabled || readOnly ? View :
|
26
|
+
const CP = disabled || readOnly ? View : TouchableRipple;
|
26
27
|
return <CP testID={testID} onLongPres={(e)=>React.stopEventPropagation(e)} {...containerProps} onPress={disabled || readOnly? undefined : onPressLink} style={[styles.container,containerProps.style]}>
|
27
28
|
<Tooltip testID={testID+"_Tooltip"} {...rest} style={[rest.style,{pointerEvents: disabled || readOnly ? 'none' : 'auto'}]} Component={Component} onPress={disabled || readOnly?undefined:onPressLink} ref={ref} readOnly={readOnly} disabled = {disabled}>
|
28
29
|
<Label testID={testID+"_Label"} underlined primary {...labelProps} style={[_styles.lh15,labelProps.style]} disabled={disabled} readOnly={readOnly}>{children}</Label>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import {Pressable} from "react-native";
|
2
|
+
import React from "$react";
|
3
|
+
import PropTypes from "prop-types";
|
4
|
+
import { extendObj } from "$cutils";
|
5
|
+
import theme from "$theme";
|
6
|
+
|
7
|
+
/***@see : https://reactnative.dev/docs/pressable#rippleconfig */
|
8
|
+
const TouchableRipple = React.forwardRef(({android_ripple,disabled,disabledRipple,readOnly,...rest},ref)=>{
|
9
|
+
return <Pressable
|
10
|
+
ref = {ref}
|
11
|
+
android_ripple={disabledRipple || android_ripple === false || disabled || readOnly ? {} : extendObj({},{
|
12
|
+
color : theme.Colors.setAlpha(theme.colors.onSurface,0.12),
|
13
|
+
borderless : true,
|
14
|
+
radius : 0,
|
15
|
+
foreground : true,
|
16
|
+
},android_ripple)}
|
17
|
+
{...rest}
|
18
|
+
/>
|
19
|
+
});
|
20
|
+
TouchableRipple.displayName = "TouchableRippleComponent";
|
21
|
+
|
22
|
+
export default TouchableRipple;
|
23
|
+
|
24
|
+
TouchableRipple.propTypes = {
|
25
|
+
...Object.assign({},Pressable.propTypes),
|
26
|
+
disabledRipple : PropTypes.bool, //alias de android_ripple
|
27
|
+
disabled : PropTypes.bool,
|
28
|
+
readOnly : PropTypes.bool,
|
29
|
+
android_ripple : PropTypes.oneOf([
|
30
|
+
PropTypes.bool,
|
31
|
+
PropTypes.shape({
|
32
|
+
color : PropTypes.string, //Defines the color of the ripple effect.
|
33
|
+
borderless : PropTypes.bool, //Defines if ripple effect should not include border.
|
34
|
+
radius : PropTypes.number, //Defines the radius of the ripple effect.
|
35
|
+
foreground : PropTypes.bool, //Set to true to add the ripple effect to the foreground of the view, instead of the background. This is useful if one of your child views has a background of its own, or you're e.g. displaying images, and you don't want the ripple to be covered by them.
|
36
|
+
})
|
37
|
+
])
|
38
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import {TouchableRipple as RNTouchableRipple} from "react-native-paper";
|
2
|
+
import React from "$react";
|
3
|
+
import PropTypes from "prop-types";
|
4
|
+
|
5
|
+
/***@see : https://reactnative.dev/docs/pressable#rippleconfig */
|
6
|
+
const TouchableRipple = React.forwardRef(({android_ripple,rippleColor,disabledRipple,disabled,readOnly,...rest},ref)=>{
|
7
|
+
return <RNTouchableRipple
|
8
|
+
ref = {ref}
|
9
|
+
rippleColor = {disabledRipple||disabled||readOnly ? 'transparent':rippleColor}
|
10
|
+
{...rest}
|
11
|
+
/>
|
12
|
+
});
|
13
|
+
TouchableRipple.displayName = "TouchableRippleComponent";
|
14
|
+
|
15
|
+
export default TouchableRipple;
|
16
|
+
|
17
|
+
TouchableRipple.propTypes = {
|
18
|
+
...Object.assign({},RNTouchableRipple.propTypes),
|
19
|
+
disabledRipple : PropTypes.bool,//si le ripple sera désactivé
|
20
|
+
disabled : PropTypes.bool,
|
21
|
+
readOnly : PropTypes.bool
|
22
|
+
}
|
package/src/context/Provider.js
CHANGED
@@ -88,7 +88,6 @@ Object.map(Utils,(v,i)=>{
|
|
88
88
|
realm : {}, //les options de configurations de la base de données realmdb
|
89
89
|
*/
|
90
90
|
const Provider = ({children,getTableData,handleHelpScreen,navigation,swrConfig,auth:cAuth,components:cComponents,parseMangoQueries,getStructData,tablesData,structsData,...props})=>{
|
91
|
-
require('$session');///initializing session
|
92
91
|
const {extendAppTheme} = appConfig;
|
93
92
|
const { theme : pTheme } = useMaterial3Theme();
|
94
93
|
navigation = defaultObj(navigation);
|
@@ -69,7 +69,7 @@ export const getBackActionComponent = ({backAction,backActionProps,withDrawer})=
|
|
69
69
|
...backActionProps,
|
70
70
|
...props,
|
71
71
|
}
|
72
|
-
if(backAction === true) return <
|
72
|
+
if(backAction === true) return <AppBar.BackAction {...bProps}/>;
|
73
73
|
const isPermanent = typeof drawerRef?.current?.isPermanent =='function' && drawerRef?.current?.isPermanent();
|
74
74
|
const isMinimized = typeof drawerRef?.current?.isMinimized =="function" && drawerRef?.current?.isMinimized();
|
75
75
|
if(backAction === false || withDrawer === false) return null;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {defaultStr,defaultVal,isNonNullString,defaultNumber,defaultObj} from "$cutils";
|
1
|
+
import {defaultStr,defaultVal,isNonNullString,defaultNumber,extendObj,defaultObj} from "$cutils";
|
2
2
|
import React from "$react";
|
3
3
|
import CountUp from "$ecomponents/CountUp";
|
4
4
|
import Avatar from "$ecomponents/Avatar";
|
@@ -14,28 +14,24 @@ import cActions from "$cactions";
|
|
14
14
|
import {View} from "react-native";
|
15
15
|
import {Menu} from "$ecomponents/BottomSheet";
|
16
16
|
import Dashboard from "$ecomponents/Datagrid/Dashboard";
|
17
|
-
import fetch from "$capi/fetch";
|
18
|
-
import Auth from "$cauth";
|
19
17
|
import Icon from "$ecomponents/Icon";
|
20
18
|
|
21
|
-
export default function DatabaseStatisticContainer ({dashboardProps,onRefreshAll,fetchDataProps,table,fetchCount,index,testID,title,icon,onPress:customOnPress,columns,fetchData,withDashboard,...props}){
|
19
|
+
export default function DatabaseStatisticContainer ({dashboardProps,fields,onRefreshAll,fetchDataProps,table,fetchCount,index,testID,title,icon,onPress:customOnPress,columns,fetchData,withDashboard,...props}){
|
22
20
|
dashboardProps = defaultObj(dashboardProps);
|
23
21
|
const [count,setCount] = React.useState(0);
|
24
22
|
const datagridRef = React.useRef(null);
|
25
23
|
let {} = props;
|
26
24
|
table = defaultObj(table);
|
27
|
-
const
|
28
|
-
const databaseStatisticsFields = defaultObj(table.databaseStatisticsFields);
|
25
|
+
const databaseStatisticsFields = defaultObj(fields,columns,table.databaseStatisticsFields);
|
29
26
|
const hasDFields = table.databaseStatistics !== false && Object.size(databaseStatisticsFields,true);
|
30
27
|
if(!withDashboard && hasDFields){
|
31
28
|
withDashboard = true;
|
32
29
|
}
|
33
|
-
const dFields = hasDFields ? databaseStatisticsFields : defaultObj(dbStatistics.fields,dbStatistics.columns);
|
34
30
|
if(typeof fetchData !=='function'){
|
35
|
-
fetchData =
|
31
|
+
fetchData = undefined;
|
36
32
|
}
|
37
33
|
withDashboard = withDashboard && typeof fetchData == 'function'? true : false;
|
38
|
-
columns = Object.size(columns,true)? columns : Object.size(
|
34
|
+
columns = Object.size(columns,true)? columns : Object.size(databaseStatisticsFields,true)? databaseStatisticsFields : table.fields;
|
39
35
|
const tableName = defaultStr(table.tableName,table.table).toUpperCase();
|
40
36
|
fetchCount = typeof table.fetchCount =='function'? table.fetchCount : typeof fetchCount =='function'? fetchCount : undefined;
|
41
37
|
if((!fetchCount && !withDashboard) || !tableName) {
|
@@ -121,8 +117,7 @@ export default function DatabaseStatisticContainer ({dashboardProps,onRefreshAll
|
|
121
117
|
}}
|
122
118
|
sessionName = {tableName+"-database-statistics"}
|
123
119
|
{...props}
|
124
|
-
{
|
125
|
-
style = {[theme.styles.pr1,props.style,dbStatistics.style]}
|
120
|
+
style = {[theme.styles.pr1,props.style]}
|
126
121
|
columns = {columns}
|
127
122
|
ref = {datagridRef}
|
128
123
|
progressBar = {isLoading?<View/>:<View style={[theme.styles.w100,theme.styles.alignItemsCenter,theme.styles.justifyContentCenter]}>{progressBar}</View>}
|
@@ -32,14 +32,19 @@ export default function DatabaseStatisticScreen ({withScreen,fetchDataProps,item
|
|
32
32
|
if(isObj(t) && defaultStr(t.table,t.tableName)){
|
33
33
|
table = t;
|
34
34
|
}
|
35
|
-
|
35
|
+
const dbStatistics = typeof table.databaseStatistics ==="function"? table.databaseStatistics() : true;
|
36
|
+
if(dbStatistics === false || table.databaseStatistic === false || table.databaseStatistics === false) return null;
|
37
|
+
const dbStatisticsProps = extendObj({},table.databaseStatistics,table.databaseStatisticsProps);
|
38
|
+
let {containerProps,cellProps,...rDBProps} = dbStatisticsProps;
|
39
|
+
cellProps = Object.assign({},cellProps);
|
40
|
+
containerProps = Object.assign({},containerProps);
|
36
41
|
const chartAllowedPerm = defaultStr(table.chartAllowedPerm);
|
37
42
|
const testID = "RN_DatabaseStatisticsCell_"+index;
|
38
43
|
if(chartAllowedPerm){
|
39
44
|
if(!Auth.isAllowedFromStr(chartAllowedPerm)) return null;
|
40
45
|
} else if((!Auth.isTableDataAllowed({table:tableName}))) return null;
|
41
|
-
content.push(<Cell elevation = {5} withSurface mobileSize={12} desktopSize={3} tabletSize={6} {...contentProps} testID={testID} key = {index} >
|
42
|
-
<Surface testID = {testID+"_Surface"} elevation = {5} {...itemContainerProps} style={[theme.styles.w100,styles.itemContainer,itemContainerProps.style]}>
|
46
|
+
content.push(<Cell elevation = {5} withSurface mobileSize={12} desktopSize={3} tabletSize={6} {...contentProps} testID={testID} {...cellProps} style={[contentProps.style,cellProps.style]} key = {index} >
|
47
|
+
<Surface testID = {testID+"_Surface"} elevation = {5} {...itemContainerProps} {...containerProps} style={[theme.styles.w100,styles.itemContainer,itemContainerProps.style,containerProps.style]}>
|
43
48
|
<DatabaseStatistic
|
44
49
|
icon = {table.icon}
|
45
50
|
key = {index}
|
@@ -52,6 +57,7 @@ export default function DatabaseStatisticScreen ({withScreen,fetchDataProps,item
|
|
52
57
|
return fetchCount({table,tableName})
|
53
58
|
}:undefined}
|
54
59
|
{...itemProps}
|
60
|
+
{...rDBProps}
|
55
61
|
/>
|
56
62
|
</Surface>
|
57
63
|
</Cell>
|
@@ -79,7 +85,7 @@ DatabaseStatisticScreen.propTypes = {
|
|
79
85
|
getTable : PropTypes.func,//la fonction permettant de récupérer la table à partir du nom
|
80
86
|
tables : PropTypes.oneOfType([
|
81
87
|
PropTypes.arrayOf(PropTypes.object),
|
82
|
-
PropTypes.objectOf(PropTypes.object)
|
88
|
+
PropTypes.objectOf(PropTypes.object),
|
83
89
|
]).isRequired,
|
84
90
|
/*** la fonction de filtre utilisée pour filtrer les table devant figurer sur le databaseStatistics */
|
85
91
|
tableFilter : PropTypes.func,
|
@@ -385,7 +385,10 @@ export default class TableDataScreenComponent extends FormDataScreen{
|
|
385
385
|
} : null;
|
386
386
|
}
|
387
387
|
} else {
|
388
|
-
formProps.style = [theme.styles.noPadding,formProps.style]
|
388
|
+
formProps.style = [!isMobOrTab && theme.styles.noPadding,formProps.style]
|
389
|
+
}
|
390
|
+
if(isMobOrTab){
|
391
|
+
formProps.style = [theme.styles.pb4,formProps.style];
|
389
392
|
}
|
390
393
|
rActionsArg.contentProps = Object.assign({},customContentProps);
|
391
394
|
rActionsArg.containerProps = Object.assign({},customContainerProps);
|
@@ -699,17 +702,24 @@ export default class TableDataScreenComponent extends FormDataScreen{
|
|
699
702
|
onSaveTableData(){}
|
700
703
|
/*** permet de recherger le contenu du form avec la données passée en paramètre
|
701
704
|
@param {object} currentData, la nouvelle donnée en cours de modification
|
702
|
-
@param {function} callback, la fonction de rappel à appeler une fois que la données a été mise à jour
|
705
|
+
@param {function|object} callback, la fonction de rappel à appeler une fois que la données a été mise à jour
|
706
|
+
@param {object|func} les données extra à utiliser pour la mise à jour du state, s'il s'agit d'un objet
|
703
707
|
@return {this} le contexte
|
704
708
|
*/
|
705
|
-
reloadCurrentData(currentData,callback){
|
709
|
+
reloadCurrentData(currentData,callback,extraState){
|
710
|
+
if(isObj(callback)){
|
711
|
+
const c = callback;
|
712
|
+
callback = typeof extraState ==='function'? extraState :undefined;
|
713
|
+
extraState = isObj(extraState)? extraState : c;
|
714
|
+
}
|
715
|
+
extraState = defaultObj(extraState);
|
706
716
|
currentData = isObj(currentData)? currentData : {};
|
707
717
|
if(this.hasManyData() && Array.isArray(this.state.datas)){
|
708
718
|
const sData = [...this.state.datas];
|
709
719
|
sData[this.state.currentIndex] = currentData;
|
710
|
-
return this.setState({data:currentData,datas:sData},callback);
|
720
|
+
return this.setState({data:currentData,datas:sData,...extraState},callback);
|
711
721
|
} else {
|
712
|
-
return this.setState({data:currentData,datas:[],hasManyData:false},callback);
|
722
|
+
return this.setState({data:currentData,datas:[],hasManyData:false,...extraState},callback);
|
713
723
|
}
|
714
724
|
}
|
715
725
|
setCurrentData(...args){
|
package/src/media/camera.js
CHANGED
@@ -1 +1,13 @@
|
|
1
|
-
|
1
|
+
import React from "$react";
|
2
|
+
const Camera = React.forwardRef((props,ref)=>{
|
3
|
+
return null;
|
4
|
+
})
|
5
|
+
export {Camera};
|
6
|
+
|
7
|
+
export default Camera;
|
8
|
+
|
9
|
+
Camera.displayName = "CameraComponent";
|
10
|
+
|
11
|
+
export function Component (){
|
12
|
+
return null;
|
13
|
+
}
|
@@ -1,4 +1,137 @@
|
|
1
|
-
import { Camera } from 'expo-camera';
|
1
|
+
import { Camera,CameraType } from 'expo-camera';
|
2
2
|
export {Camera};
|
3
|
+
import { useState,useMemo,useEffect,useRef,usePrevious,forwardRef,mergeRefs } from '$react';
|
4
|
+
import {HStack} from "$ecomponents/Stack";
|
5
|
+
import Dialog from "$components/Dialog";
|
6
|
+
import theme from "$theme";
|
7
|
+
import Button from "$ecomponents/Button";
|
8
|
+
import Label from "$ecomponents/Label";
|
9
|
+
import View from "$ecomponents/View";
|
10
|
+
import {getTakePhotoOptions} from "./utils";
|
11
|
+
import PropTypes from "prop-types";
|
12
|
+
import { isBase64 } from '$cutils';
|
3
13
|
|
4
|
-
export
|
14
|
+
export const Component = forwardRef(({onGrantAccess,onDenyAccess,takePictureOptions,onSuccess,onCameraReady:cOnCameraReader,onCancel,onDismiss,autoTakePicture,type:defType,dialgProps,dialogRef,...cameraProps},ref)=> {
|
15
|
+
const [visible,setVisible] = useState(true);
|
16
|
+
const innerRef = useRef(null);
|
17
|
+
const cancelRef = useRef(null);
|
18
|
+
const [cameraReady,setCameraReady] = useState(false);
|
19
|
+
const takePicture = ()=>{
|
20
|
+
if(typeof innerRef?.current?.takePictureAsync =='function'){
|
21
|
+
const camOptions = getTakePhotoOptions(takePictureOptions);
|
22
|
+
innerRef.current.takePictureAsync(camOptions).then((result)=>{
|
23
|
+
result.dataURL = result.dataUrl = isBase64(result.base64) ? 'data:image/jpeg;base64,'+result.base64 : undefined;
|
24
|
+
if(typeof onSuccess =='function'){
|
25
|
+
onSuccess(result);
|
26
|
+
}
|
27
|
+
return result;
|
28
|
+
});
|
29
|
+
}
|
30
|
+
}
|
31
|
+
const onCameraReady = ()=>{
|
32
|
+
if(!cameraReady){
|
33
|
+
setCameraReady(true);
|
34
|
+
}
|
35
|
+
if(typeof cOnCameraReader =="function"){
|
36
|
+
cOnCameraReader();
|
37
|
+
}
|
38
|
+
}
|
39
|
+
takePictureOptions = Object.assign({},takePictureOptions);
|
40
|
+
|
41
|
+
const prevVisible = usePrevious(visible);
|
42
|
+
const cancel = ()=>{
|
43
|
+
cancelRef.current = true;
|
44
|
+
setVisible(false);
|
45
|
+
}
|
46
|
+
useEffect(()=>{
|
47
|
+
if(visible === prevVisible) return;
|
48
|
+
if(!visible && typeof onDismiss =="function"){
|
49
|
+
onDismiss();
|
50
|
+
}
|
51
|
+
if(prevVisible && !visible && cancelRef.current && typeof onCancel =='function'){
|
52
|
+
onCancel();
|
53
|
+
}
|
54
|
+
cancelRef.current = true;
|
55
|
+
},[visible])
|
56
|
+
const commonType = useMemo(()=>{
|
57
|
+
if(typeof defType !== "number" || !(defType in CameraType)){
|
58
|
+
return CameraType.back;
|
59
|
+
}
|
60
|
+
return defType;
|
61
|
+
},[defType]);
|
62
|
+
const [type, setType] = useState(commonType);
|
63
|
+
useEffect(()=>{
|
64
|
+
if(commonType !== type){
|
65
|
+
setType(commonType);
|
66
|
+
}
|
67
|
+
},[commonType])
|
68
|
+
const [permission, requestPermission] = Camera.useCameraPermissions();
|
69
|
+
const hasPermission = permission && permission?.granted || false;
|
70
|
+
useEffect(()=>{
|
71
|
+
if(!hasPermission){
|
72
|
+
if(typeof onDenyAccess =="function"){
|
73
|
+
onDenyAccess()
|
74
|
+
}
|
75
|
+
return;
|
76
|
+
}
|
77
|
+
if(typeof onGrantAccess =='function'){
|
78
|
+
onGrantAccess();
|
79
|
+
}
|
80
|
+
},[hasPermission]);
|
81
|
+
const isBack = type === CameraType.back;
|
82
|
+
dialgProps = Object.assign({},dialgProps);
|
83
|
+
function toggleCameraType() {
|
84
|
+
setType(current => (current === CameraType.back ? CameraType.front : CameraType.back));
|
85
|
+
}
|
86
|
+
const togglePhotoBtn = {
|
87
|
+
text : "Pivoter la camera",
|
88
|
+
icon : "camera-flip",
|
89
|
+
tooltip : `Cliquez pour basculer à la camera ${isBack ? "frontable":"arrière"}`,
|
90
|
+
onPress : toggleCameraType
|
91
|
+
};
|
92
|
+
const takePictureBtn = cameraReady ? {
|
93
|
+
text : "Photographier",
|
94
|
+
icon : "camera",
|
95
|
+
tooltip : `Cliquez pour prendre une photo`,
|
96
|
+
onPress : takePicture,
|
97
|
+
} : null;
|
98
|
+
return <Dialog ref={dialogRef} {...dialgProps} fullPage visible={visible} actions={[takePictureBtn,togglePhotoBtn]} title={"Enregistrer une photo"} onBackActionPress={(...arg)=>{
|
99
|
+
if(typeof dialgProps.onBackActionPress =="function" && dialgProps.onBackActionPress(...arg) === false) return;
|
100
|
+
cancel();
|
101
|
+
}}>
|
102
|
+
{hasPermission ? <View style={[theme.styles.flex1,theme.styles.w100]}>
|
103
|
+
<Camera {...cameraProps}
|
104
|
+
ref={mergeRefs(ref,innerRef)} style={[theme.styles.flex1,cameraProps.style]} type={type}
|
105
|
+
onCameraReady = {onCameraReady}
|
106
|
+
>
|
107
|
+
</Camera>
|
108
|
+
<HStack>
|
109
|
+
{takePictureBtn? <Button
|
110
|
+
{...takePicture}
|
111
|
+
success
|
112
|
+
/>:null}
|
113
|
+
<Button
|
114
|
+
{...togglePhotoBtn}
|
115
|
+
style = {[theme.styles.w100]}
|
116
|
+
/>
|
117
|
+
<Button
|
118
|
+
error
|
119
|
+
children = {"Annuler"}
|
120
|
+
icon = "camera-off"
|
121
|
+
title = {"Cliquez pour annuler l'opération"}
|
122
|
+
onPress = {cancel}
|
123
|
+
/>
|
124
|
+
</HStack>
|
125
|
+
</View> :
|
126
|
+
<View style={[theme.styles.flex1,theme.styles.justifyContentCenter,theme.styles.w100,theme.styles.alignItemsCenter]}>
|
127
|
+
<Label>{`Permission non autorisée. Vous devez autoriser l'accès à la camera`}</Label>
|
128
|
+
</View>}
|
129
|
+
</Dialog>
|
130
|
+
});
|
131
|
+
|
132
|
+
Component.displayName = "CameraPhotoComponent";
|
133
|
+
|
134
|
+
Component.propTypes = {
|
135
|
+
takePictureOptions : PropTypes.object,//les options pour la prise de la photo
|
136
|
+
autoTakePicture : PropTypes.bool, //si la camera devra prendre la photo lorsque celle si sera visible
|
137
|
+
}
|