@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.
Files changed (47) hide show
  1. package/App.js +6 -3
  2. package/app.json +1 -0
  3. package/bin/create-app.js +13 -4
  4. package/copy-env-file.js +1 -1
  5. package/docs/navigation/drawerItems/index.js +16 -0
  6. package/docs/navigation/drawerSections.js +14 -0
  7. package/docs/screens/Datagrid/{Datagrid.js → index.js} +1 -1
  8. package/docs/screens/index.js +1 -0
  9. package/package.json +141 -139
  10. package/src/App.js +1 -1
  11. package/src/AppEntry/index.js +1 -0
  12. package/src/components/AppBar/BackAction.js +46 -0
  13. package/src/components/AppBar/Content.js +3 -3
  14. package/src/components/AppBar/index.js +8 -3
  15. package/src/components/Datagrid/Accordion/index.js +1 -0
  16. package/src/components/Datagrid/Common/Common.js +1 -1
  17. package/src/components/Datagrid/Test/index.js +1 -1
  18. package/src/components/Drawer/DrawerItems/index.js +1 -1
  19. package/src/components/Dropdown/index.js +30 -8
  20. package/src/components/ErrorBoundary/ErrorMessage.js +5 -5
  21. package/src/components/Form/Fields/SelectTableData/Component.js +2 -2
  22. package/src/components/Image/index.js +13 -15
  23. package/src/components/Link/index.js +2 -2
  24. package/src/components/SplashScreen/index.js +9 -1
  25. package/src/components/TableLink/index.js +2 -1
  26. package/src/components/TouchableRipple/index.js +38 -0
  27. package/src/components/TouchableRipple/index.web.js +22 -0
  28. package/src/context/Provider.js +0 -1
  29. package/src/layouts/AppBar/index.js +1 -1
  30. package/src/layouts/DatabaseStatistics/DatabaseStatistic.js +6 -11
  31. package/src/layouts/DatabaseStatistics/index.js +10 -4
  32. package/src/layouts/Screen/TableData.js +15 -5
  33. package/src/media/camera.js +13 -1
  34. package/src/media/camera.native.js +135 -2
  35. package/src/media/file-system/utils/FileSaver.native.js +62 -25
  36. package/src/media/file-system/utils/native/index.js +11 -1
  37. package/src/media/file-system/utils/web/index.js +21 -1
  38. package/src/media/index.js +27 -35
  39. package/src/media/utils.js +27 -0
  40. package/src/navigation/Drawer/items/index.js +1 -1
  41. package/src/navigation/animationTypes.js +48 -0
  42. package/src/navigation/index.js +11 -7
  43. package/src/screens/Auth/Profile.js +12 -0
  44. package/src/screens/Help/openLibraries.js +9 -9
  45. package/webpack.config.js +5 -0
  46. package/docs/drawerItems/index.js +0 -17
  47. 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) && item.hasOwnProperty(itemLabel)) return defaultStr(item[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 = <Pressable
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
- </Pressable>
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
- const node = self.state.nodes[key];
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={TouchableRipple}
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.backgroundColor;
26
- const color = theme.colors.onSurface;
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,{backgroundColor}]}>
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: '300',
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(!isNonNullString(foreignKeyColumn)){
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
- (async ()=>{
226
- if(false && await canTakePhoto()){
227
- menuItems.push({
228
- label : 'Eng photo',
229
- icon : 'camera',
230
- onPress : (a)=>{
231
- takePhoto().then(handlePickedImage);
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 { Pressable } from "react-native";
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 : Pressable;
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 : Pressable;
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
+ }
@@ -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 <Appbar.BackAction {...bProps}/>;
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 dbStatistics = defaultObj(table.databaseStatistics,table.databaseStatisticsProps);
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 = typeof dbStatistics.fetchData =='function'? dbStatistics.fetchData : undefined;
31
+ fetchData = undefined;
36
32
  }
37
33
  withDashboard = withDashboard && typeof fetchData == 'function'? true : false;
38
- columns = Object.size(columns,true)? columns : Object.size(dFields,true)? dFields : table.fields;
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
- {...dbStatistics}
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
- if(table.databaseStatistic === false || table.databaseStatistics === false) return null;
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){
@@ -1 +1,13 @@
1
- export default {};
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 default Camera;
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
+ }