@fto-consult/expo-ui 5.8.10 → 5.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.
@@ -16,6 +16,29 @@ module.exports = (opts)=>{
16
16
  const cpath = fs.existsSync(euCommon)? path.resolve(euCommon,"babel.config.alias") : "@fto-consult/common/babel.config.alias";
17
17
  const r = require(`${cpath}`)(opts);
18
18
  const expo = path.resolve(expoUI,"src");
19
+
20
+ /**** package json */
21
+ const packagePath = path.resolve(base,"package.json");
22
+ const configPath = path.resolve(expo,"app.config.json");
23
+ if(fs.existsSync(packagePath)){
24
+ try {
25
+ const packageObj = require(`${packagePath}`);
26
+ if(typeof packageObj.name =="string"){
27
+ packageObj.name = packageObj.name.toUpperCase();
28
+ }
29
+ if(packageObj){
30
+ ["scripts","private","main","repository","keywords","bugs","dependencies","devDependencies"].map(v=>{
31
+ delete packageObj[v];
32
+ })
33
+ fs.writeFileSync(configPath,JSON.stringify(packageObj,null,"\t"));
34
+ }
35
+ } catch (e){
36
+ console.log(e," writing file sync on package JSON, file : $common/babel.config.alias")
37
+ }
38
+ }
39
+ if(fs.existsSync(configPath)){
40
+ r["$package.json"] = r["$packageJSON"] = configPath;
41
+ }
19
42
  r["$eauth"] = path.resolve(expo,"auth");
20
43
  r["$ecomponents"] = r["$expo-components"] = path.resolve(expo,"components");
21
44
  r["$etableLink"] = r["$eTableLink"] = path.resolve(r["$ecomponents"],"TableLink");
package/babel.config.js CHANGED
@@ -14,7 +14,6 @@ module.exports = function(api,opts) {
14
14
  inlineDovOptions.path ='./.env';
15
15
  }
16
16
  /*** par défaut, les variables d'environnements sont stockés dans le fichier .env situé à la racine du projet, référencée par la prop base */
17
-
18
17
  const alias = require("./babel.config.alias")(options);
19
18
  return {
20
19
  presets: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fto-consult/expo-ui",
3
- "version": "5.8.10",
3
+ "version": "5.9.0",
4
4
  "description": "Bibliothèque de composants UI Expo,react-native",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -61,7 +61,7 @@
61
61
  "@emotion/native": "^11.10.6",
62
62
  "@expo/html-elements": "^0.2.0",
63
63
  "@expo/vector-icons": "^13.0.0",
64
- "@fto-consult/common": "^3.12.0",
64
+ "@fto-consult/common": "^3.13.16",
65
65
  "@gorhom/portal": "^1.0.14",
66
66
  "@react-native-async-storage/async-storage": "^1.17.11",
67
67
  "@react-native-community/datetimepicker": "^6.7.3",
package/src/App.js CHANGED
@@ -2,18 +2,11 @@ import '$session';
2
2
  import React from 'react';
3
3
  import {SWRConfig} from "$swr";
4
4
  import {defaultObj} from "$cutils";
5
- import {updateTheme,defaultTheme} from "$theme";
6
- import {Provider as PaperProvider } from 'react-native-paper';
7
5
  import Index from './index';
8
6
  import notify from "$notify";
9
7
  import { SafeAreaProvider } from 'react-native-safe-area-context';
10
- import { PreferencesContext } from './Preferences';
11
- import {AuthProvider} from '$cauth';
12
- import ErrorBoundary from "$ecomponents/ErrorBoundary";
13
8
  import {GestureHandlerRootView} from "react-native-gesture-handler";
14
- import StatusBar from "$ecomponents/StatusBar";
15
9
  import APP from "$app";
16
- import FontIcon from "$ecomponents/Icon/Font"
17
10
  import {isMobileNative} from "$cplatform";
18
11
  import {setDeviceIdRef} from "$capp";
19
12
  import appConfig from "$capp/config";
@@ -30,7 +23,7 @@ Object.map(Utils,(v,i)=>{
30
23
  window[i] = v;
31
24
  }
32
25
  });
33
- export default function getIndex({onMount,onUnmount,swrConfig,render,onRender,preferences:appPreferences,...rest}){
26
+ export default function getIndex({onMount,onUnmount,swrConfig,onRender,...rest}){
34
27
  const isScreenFocusedRef = React.useRef(true);
35
28
  ///garde pour chaque écran sa date de dernière activité
36
29
  const screensRef = React.useRef({});//la liste des écrans actifs
@@ -97,19 +90,7 @@ export default function getIndex({onMount,onUnmount,swrConfig,render,onRender,pr
97
90
  }
98
91
  }
99
92
  },[])
100
- const [theme,setTheme] = React.useState(updateTheme(defaultTheme));
101
- const updatePreferenceTheme = (customTheme,persist)=>{
102
- setTheme(updateTheme(customTheme));
103
- };
104
- const forceRender = React.useForceRender();
105
- const pref = typeof appPreferences =='function'? appPreferences({setTheme,forceRender,updateTheme:updatePreferenceTheme}) : appPreferences;
106
- const preferences = React.useMemo(()=>({
107
- updateTheme:updatePreferenceTheme,
108
- theme,
109
- ...defaultObj(pref),
110
- }),[theme,pref]);
111
- const child = <Index {...rest} theme={theme}/>;
112
- const content = typeof render == 'function'? render({children:child,appConfig,config:appConfig}) : child;
93
+
113
94
  return (
114
95
  <SWRConfig
115
96
  value={{
@@ -160,25 +141,9 @@ export default function getIndex({onMount,onUnmount,swrConfig,render,onRender,pr
160
141
  }}
161
142
  >
162
143
  <GestureHandlerRootView style={{ flex: 1 }}>
163
- <PaperProvider
164
- theme={theme}
165
- settings={{
166
- icon: (props) => {
167
- return <FontIcon {...props}/>
168
- },
169
- }}
170
- >
171
- <SafeAreaProvider>
172
- <AuthProvider>
173
- <ErrorBoundary>
174
- <StatusBar/>
175
- <PreferencesContext.Provider value={preferences}>
176
- {React.isValidElement(content) && content || child}
177
- </PreferencesContext.Provider>
178
- </ErrorBoundary>
179
- </AuthProvider>
180
- </SafeAreaProvider>
181
- </PaperProvider>
144
+ <SafeAreaProvider>
145
+ <Index {...rest}/>
146
+ </SafeAreaProvider>
182
147
  </GestureHandlerRootView>
183
148
  </SWRConfig>
184
149
  );
@@ -934,19 +934,22 @@ export default class CommonDatagridComponent extends AppComponent {
934
934
  })
935
935
  if(size === 1 && canMakePhoneCall === true && canMakeCall()){
936
936
  const rowKey = Object.keys(this.selectedRows)[0], rowData = defaultObj(this.selectedRows[rowKey]);
937
- let callProps = typeof makePhoneCallProps == 'function'? makePhoneCallProps(rowData,rowKey) : makePhoneCallProps;
938
- callProps = defaultObj(callProps);
939
- r.push({
940
- text : defaultStr(callProps.text,callProps.label,'Appeler'),
941
- icon : defaultStr(callProps.icon,'phone'),
942
- flat : true,
943
- onPress : ()=>{
944
- return makePhoneCall(
945
- rowData,
946
- callProps
947
- );
948
- }
949
- })
937
+ const table = defaultStr(this.props.table,this.props.tableName).trim();
938
+ let callProps = typeof makePhoneCallProps == 'function'? makePhoneCallProps({rowData,rowKey,table,tableName:table,data:rowData,key:rowKey,context:this,props:this.props}) : makePhoneCallProps;
939
+ if(callProps !== false){
940
+ callProps = defaultObj(callProps);
941
+ r.push({
942
+ text : defaultStr(callProps.text,callProps.label,'Appeler'),
943
+ icon : defaultStr(callProps.icon,'phone'),
944
+ flat : true,
945
+ onPress : ()=>{
946
+ return makePhoneCall(
947
+ rowData,
948
+ callProps
949
+ );
950
+ }
951
+ })
952
+ }
950
953
  }
951
954
  if(isObj(this.props.columns) && size ===1){
952
955
  r.push({
@@ -65,7 +65,7 @@ export const getSWROptions = ()=>{
65
65
  }
66
66
  }
67
67
 
68
-
68
+ const isValidMakePhoneCallProps = p=> isObj(p) && Object.size(p,true) || typeof p ==='function';
69
69
  /****la fonction fetcher doit toujours retourner :
70
70
  * 1. la liste des éléments fetchés dans la props data
71
71
  * 2. le nombre total d'éléments de la liste obtenue en escluant les clause limit et offset correspondant à la même requête
@@ -118,7 +118,7 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
118
118
  delete sort.column;
119
119
  }
120
120
  canMakePhoneCall = defaultBool(canMakePhoneCall,table.canMakePhoneCall);
121
- makePhoneCallProps = defaultObj(makePhoneCallProps,rest.makePhoneCallProps,table.makePhoneCallProps);
121
+ makePhoneCallProps = isValidMakePhoneCallProps(makePhoneCallProps) && makePhoneCallProps || isValidMakePhoneCallProps(rest.makePhoneCallProps) && rest.makePhoneCallProps || isValidMakePhoneCallProps(table.makePhoneCallProps) && table.makePhoneCallProps || {};
122
122
  const isExportable = !!Auth.isTableDataAllowed({table:tableName,action:'export'});
123
123
  rest.exportable = isExportable;
124
124
  rowKey = defaultStr(rowKey,table.rowKey,table.primaryKeyColumnName);
@@ -3,9 +3,10 @@ import KeyboardEventHandler from "../KeyboardEventHandler";
3
3
  const {getActions,getFormFields,Forms} = require("../utils")
4
4
  import TextField,{parseDecimal} from "$ecomponents/TextField";
5
5
  import Icon from "$ecomponents/Icon";
6
- import {extendObj,isBool,isUndefined,uniqid,isValidDataFileName,defaultObj,isObj,defaultFunc,isFunction,isNumber,arrayValueExists,defaultVal,defaultStr,isNonNullString,defaultBool,defaultDecimal} from "$cutils";
6
+ import {extendObj,isBool,isUndefined,uniqid,isValidDataFileName,isValidEmail,defaultObj,isObj,defaultFunc,isFunction,isNumber,arrayValueExists,defaultVal,defaultStr,isNonNullString,defaultBool,defaultDecimal} from "$cutils";
7
7
  import {Component as AppComponent} from "$react";
8
8
  import {observable,addObserver} from "$observable";
9
+ import {isValidPhoneNumber} from "$ecomponents/PhoneInput";
9
10
  import {Validator} from "$validator";
10
11
  import theme,{grid} from "$theme";
11
12
  import React from "$react";
@@ -285,11 +286,23 @@ export default class Field extends AppComponent {
285
286
  });
286
287
  }
287
288
  onValidatorValid(args){
288
- if(!this.isFilter() && ((this.props.allowWhiteSpaces === false) || ((this.type ==='id' || this.type =='piece') && this.props.allowWhiteSpaces !== true))){
289
- const value = isNonNullString(args.value) && args.value.replaceAll("/","").replaceAll("\\",'') || undefined;
290
-
291
- if(value && this.type !=='email' && !defaultStr(this.getValidRule()).toLowerCase().contains('email') && (value.contains(" ") || !isValidDataFileName(value.replaceAll("@","")))){
292
- return "Veuillez renseigner une valeur ne contenant pas d'espace ou de caractère accentués";
289
+ if(!this.isFilter()){
290
+ const vRule =defaultStr(this.getValidRule()).toLowerCase();
291
+ const value = typeof args.value == "undefined" || args.value == null ? "" : String(args.value).replaceAll("/","").replaceAll("\\",'').trim();
292
+ if(value){
293
+ if(this.type ==='email' || vRule.contains('email')){
294
+ if(!isValidEmail(value)){
295
+ return "Veuillez saisir une addresse email valide";
296
+ }
297
+ } else if(this.type ==="tel" || this.type =="phone"){
298
+ if(!isValidPhoneNumber(value)){
299
+ return "Merci d'entrer un numéro de téléphone valide";
300
+ }
301
+ } else if(((this.props.allowWhiteSpaces === false) || ((this.type ==='id' || this.type =='piece') && this.props.allowWhiteSpaces !== true))){
302
+ if((value.contains(" ") || !isValidDataFileName(value.replaceAll("@","").replaceAll(".","")))){
303
+ return "Veuillez renseigner une valeur ne contenant pas d'espace ou de caractère accentués";
304
+ }
305
+ }
293
306
  }
294
307
  }
295
308
  if(isFunction(this.props.onValidatorValid)){
@@ -33,7 +33,8 @@ const TableDataSelectField = React.forwardRef(({foreignKeyColumn,bindUpsert2Remo
33
33
  }
34
34
  convertFiltersToSQL = defaultVal(convertFiltersToSQL,willConvertFiltersToSQL());
35
35
  getForeignKeyTable = getForeignKeyTable || appConfig.getTableData;
36
- let fKeyTable = typeof getForeignKeyTable =='function' ? getForeignKeyTable(foreignKeyTable,props) : undefined;
36
+ const foreignKeyTableStr = defaultStr(foreignKeyTable,props.table,props.tableName);
37
+ let fKeyTable = typeof getForeignKeyTable =='function' ? getForeignKeyTable(foreignKeyTableStr,props) : undefined;
37
38
  fetchItemsPath = defaultStr(fetchItemsPath).trim();
38
39
 
39
40
  if(!fetchItemsPath && (!isObj(fKeyTable) || !(defaultStr(fKeyTable.tableName,fKeyTable.table)))){
@@ -67,8 +68,8 @@ const TableDataSelectField = React.forwardRef(({foreignKeyColumn,bindUpsert2Remo
67
68
  const defaultFields = Array.isArray(foreignKeyColumn)? foreignKeyColumn : [foreignKeyColumn];
68
69
  if(Array.isArray(foreignKeyLabel)){
69
70
  foreignKeyLabel.map(f=>{
70
- if(isNonNullString(f)){
71
- defaultFields.push(f);
71
+ if(isNonNullString(f) && !defaultFields.includes(f.trim())){
72
+ defaultFields.push(f.trim());
72
73
  }
73
74
  })
74
75
  }
@@ -94,6 +94,9 @@ export default class FormDataComponent extends AppComponent{
94
94
  savedArgs = {...savedArgs,data}
95
95
  }
96
96
  }
97
+ if(typeof saveDataMutator =='function'){
98
+ saveDataMutator(savedArgs);
99
+ }
97
100
  return handleBeforeSaveCallback(this.beforeSave.bind(this),()=>{
98
101
  return handleBeforeSaveCallback(beforeSave,()=>{
99
102
  return handleBeforeSaveCallback(this.onSave.bind(this),()=>{
@@ -5,7 +5,7 @@ import {defaultObj,defaultVal} from "$cutils";
5
5
  import {isMobileMedia} from "$cplatform/dimensions";
6
6
  import TextFieldComponent from "$ecomponents/TextField";
7
7
  import { StyleSheet } from "react-native";
8
-
8
+ import {METRICS_UNITS,WEIGHTS_UNITS} from "./utils";
9
9
 
10
10
  export {Select};
11
11
 
@@ -45,7 +45,7 @@ export const MetricUnit = React.forwardRef((props,ref)=>{
45
45
  inputProps.style = [styles.metricUnit,inputProps.style];
46
46
  rest = defaultObj(rest);
47
47
  return <Select
48
- items = {PRODUCTS_CONSTANTS.METRICS_UNITS}
48
+ items = {METRICS_UNITS}
49
49
  withCheckedIcon = {isMob}
50
50
  {...rest}
51
51
  inputProps = {inputProps}
@@ -67,7 +67,7 @@ export const WeightUnit = React.forwardRef((props,ref)=>{
67
67
  inputProps.style = [styles.weightUnit,inputProps.style];
68
68
  rest = defaultObj(rest);
69
69
  return <Select
70
- items = {PRODUCTS_CONSTANTS.WEIGHTS_UNITS}
70
+ items = {WEIGHTS_UNITS}
71
71
  renderItem = {({item,index})=>index}
72
72
  withCheckedIcon = {isMob}
73
73
  {...rest}
@@ -102,7 +102,7 @@ export const TextField = TextInput;
102
102
  WeightUnit.displayName = "WeightUnitInlineIndicatorComponent";
103
103
 
104
104
  export const Text = React.forwardRef((props,ref)=>{
105
- return <LabelComponent.withRef ref={ref} {...props} {...state} style={[styles.text,props.style]}/>
105
+ return <LabelComponent.withRef ref={ref} {...props} style={[styles.text,props.style]}/>
106
106
  });
107
107
 
108
108
  export const Label = Text;
@@ -0,0 +1,15 @@
1
+ export const WEIGHTS_UNITS = {
2
+ t : 'Tonne',
3
+ kg : 'Kilogramme',
4
+ g : 'Gramme',
5
+ mg : 'Milligramme',
6
+ cg : 'Centigramme',
7
+ dg : 'Decigramme',
8
+ };
9
+
10
+ export const METRICS_UNITS = {
11
+ m : 'm',
12
+ dm : 'dm',
13
+ cm : 'cm',
14
+ mm : 'mm'
15
+ };
@@ -26,6 +26,7 @@ export const isValidNumber = (number,iso2)=>{
26
26
  }
27
27
  return false;
28
28
  }
29
+ export const isValidPhoneNumber = isValidNumber;
29
30
 
30
31
  class PhoneNumber {
31
32
  // eslint-disable-next-line class-methods-use-this
@@ -322,7 +322,7 @@ const SimpleSelect = React.forwardRef((props,ref)=>{
322
322
  paddingHorizontal : 10,
323
323
  paddingVertical:0,
324
324
  height : !isMob?contentContainerHeight:'90%',
325
- width : !isMob ? layout.width : undefined,
325
+ width : !isMob ? Math.max(layout.width,150) : undefined,
326
326
  },
327
327
  isMob && {flex:1},
328
328
  !isMob && {paddingRight : 0},
@@ -1,7 +1,7 @@
1
1
  /* @flow */
2
2
  /*** fork of https://www.npmjs.com/package/react-native-animated-splash-screen */
3
3
  import PropTypes from "prop-types"
4
- import * as React from "react"
4
+ import React from "$react"
5
5
  import {Animated, StyleSheet } from "react-native";
6
6
  import View from "$ecomponents/View";
7
7
  import {isNativeMobile} from "$cplatform";
@@ -19,167 +19,136 @@ import styles, {
19
19
  const isNative = isNativeMobile();
20
20
  const Component = isNative? Animated.View : View;
21
21
 
22
- class AnimatedSplash extends React.Component {
23
- static defaultProps = {
24
- isLoaded: false,
25
- }
26
-
27
- state = {
22
+ const SplashScreenComponent = ({isLoaded,children , duration, delay,logoWidth,logoHeight,backgroundColor,imageBackgroundSource,imageBackgroundResizeMode,
23
+ testID,
24
+ disableAppScale,
25
+ disableImageBackgroundAnimation,preload})=>{
26
+ const [state,setState] = React.useState({
28
27
  animationDone: false,
29
28
  loadingProgress: new Animated.Value(0)
30
- }
31
-
32
- componentDidUpdate(prevProps) {
33
- const { isLoaded , duration, delay } = this.props
34
- const { loadingProgress } = this.state
35
-
36
- if (isLoaded && !prevProps.isLoaded) {
37
- if(!isNativeMobile()){
38
- this.setState({animationDone:true});
39
- } else {
40
- Animated.timing(loadingProgress, {
41
- toValue: 100,
42
- duration: duration || 1000,
43
- delay: delay || 0,
44
- useNativeDriver: true,
45
- }).start(() => {
46
- this.setState({
47
- animationDone: true,
48
- })
49
- })
50
- }
51
- }
52
- }
53
-
54
- renderChildren() {
55
- const { children, preload, isLoaded } = this.props
56
- if (preload || preload == null) {
57
- return children
29
+ });
30
+ const { loadingProgress, animationDone } = state;
31
+ const prevIsLoaded = React.usePrevious(isLoaded);
32
+ React.useEffect(()=>{
33
+ if(prevIsLoaded == isLoaded || !isLoaded) return;
34
+ if(!isNativeMobile()){
35
+ setState({...state,animationDone:true});
58
36
  } else {
59
- if (isLoaded) {
60
- return children
61
- }
37
+ Animated.timing(loadingProgress, {
38
+ toValue: 100,
39
+ duration: duration || 1000,
40
+ delay: delay || 0,
41
+ useNativeDriver: true,
42
+ }).start(() => {
43
+ setState({
44
+ ...state,
45
+ animationDone: true,
46
+ })
47
+ })
62
48
  }
63
-
64
- return null
49
+ },[isLoaded]);
50
+ testID = defaultStr(testID,"RN_SplashscreenComponent")
51
+ logoWidth = defaultDecimal(logoWidth,150);
52
+ logoHeight = defaultDecimal(logoHeight,250);
53
+ const opacityClearToVisible = {
54
+ opacity: loadingProgress.interpolate({
55
+ inputRange: [0, 15, 30],
56
+ outputRange: [0, 0, 1],
57
+ extrapolate: "clamp",
58
+ }),
59
+ }
60
+ const imageScale = {
61
+ transform: [
62
+ {
63
+ scale: loadingProgress.interpolate({
64
+ inputRange: [0, 10, 100],
65
+ outputRange: [1, 1, 65],
66
+ }),
67
+ },
68
+ ],
65
69
  }
66
70
 
67
- render() {
68
- const { loadingProgress, animationDone } = this.state
69
- let {
70
- logoImage,
71
- logoWidth,
72
- logoHeight,
73
- backgroundColor,
74
- imageBackgroundSource,
75
- imageBackgroundResizeMode,
76
- testID,
77
- disableAppScale,
78
- disableImageBackgroundAnimation,
79
- } = this.props
80
- testID = defaultStr(testID,"RN_SplashscreenComponent")
81
- logoWidth = defaultDecimal(logoWidth,150);
82
- logoHeight = defaultDecimal(logoHeight,250);
83
- const opacityClearToVisible = {
84
- opacity: loadingProgress.interpolate({
85
- inputRange: [0, 15, 30],
86
- outputRange: [0, 0, 1],
87
- extrapolate: "clamp",
88
- }),
89
- }
90
-
91
- const imageScale = {
92
- transform: [
93
- {
94
- scale: loadingProgress.interpolate({
95
- inputRange: [0, 10, 100],
96
- outputRange: [1, 1, 65],
97
- }),
98
- },
99
- ],
100
- }
101
-
102
- const logoScale = {
103
- transform: [
104
- {
105
- scale: loadingProgress.interpolate({
106
- inputRange: [0, 10, 100],
107
- outputRange: [1, 0.8, 10],
108
- }),
109
- },
110
- ],
111
- }
71
+ const logoScale = {
72
+ transform: [
73
+ {
74
+ scale: loadingProgress.interpolate({
75
+ inputRange: [0, 10, 100],
76
+ outputRange: [1, 0.8, 10],
77
+ }),
78
+ },
79
+ ],
80
+ }
112
81
 
113
- const logoOpacity = {
114
- opacity: loadingProgress.interpolate({
115
- inputRange: [0, 20, 100],
116
- outputRange: [1, 0, 0],
117
- extrapolate: "clamp",
118
- }),
119
- }
82
+ const logoOpacity = {
83
+ opacity: loadingProgress.interpolate({
84
+ inputRange: [0, 20, 100],
85
+ outputRange: [1, 0, 0],
86
+ extrapolate: "clamp",
87
+ }),
88
+ }
120
89
 
121
- const appScale = {
122
- transform: [
123
- {
124
- scale: loadingProgress.interpolate({
125
- inputRange: [0, 7, 100],
126
- outputRange: [1.1, 1.05, 1],
127
- }),
128
- },
129
- ],
130
- }
131
-
132
- return (
133
- <View style={[styles.container]} testID={testID} nativeID={testID}>
134
- {!animationDone && <View style={StyleSheet.absoluteFill} testID={testID+"_Animation"}/>}
135
- <View style={styles.containerGlue}>
136
- {!animationDone && (
137
- <Animated.View
138
- style={_staticBackground(logoOpacity, backgroundColor)}
139
- testID={testID+"_AnimationDone"}
140
- />
141
- )}
142
- {(animationDone || isNative) && <Component style={[!disableAppScale && appScale, opacityClearToVisible, styles.flex]}>
143
- {this.renderChildren()}
144
- </Component>}
145
- {!animationDone && (
146
- <Animated.Image
147
- testID={testID+"AnimateImage"}
148
- resizeMode={imageBackgroundResizeMode || "cover"}
149
- source={imageBackgroundSource}
150
- style={[disableImageBackgroundAnimation && _staticBackground(
151
- logoOpacity,
152
- backgroundColor
153
- ), disableImageBackgroundAnimation && _dynamicImageBackground(
154
- imageScale,
155
- logoOpacity,
156
- backgroundColor
157
- )]}
158
- />
159
- )}
160
- {!animationDone && (
161
- <View testID={testID+"_LogoContainer"} style={[StyleSheet.absoluteFill, styles.logoStyle]}>
162
- {(
163
- <Animated.View
164
- testID={testID+"_Logo"}
165
- style={_dynamicCustomComponentStyle(
166
- logoScale,
167
- logoOpacity,
168
- logoWidth,
169
- logoHeight
170
- )}>
171
- {<LogoProgress/>}
172
- </Animated.View>
173
- )}
174
- </View>
175
- )}
176
- </View>
177
- </View>
178
- )
90
+ const appScale = {
91
+ transform: [
92
+ {
93
+ scale: loadingProgress.interpolate({
94
+ inputRange: [0, 7, 100],
95
+ outputRange: [1.1, 1.05, 1],
96
+ }),
97
+ },
98
+ ],
179
99
  }
100
+ const child = (animationDone && isLoaded)? React.isValidElement(children) && children : null;
101
+ return (
102
+ <View style={[styles.container]} testID={testID} nativeID={testID}>
103
+ {!animationDone && <View style={StyleSheet.absoluteFill} testID={testID+"_Animation"}/>}
104
+ <View style={styles.containerGlue}>
105
+ {!animationDone && (
106
+ <Animated.View
107
+ style={_staticBackground(logoOpacity, backgroundColor)}
108
+ testID={testID+"_AnimationDone"}
109
+ />
110
+ )}
111
+ {(animationDone || isNative) && <Component style={[!disableAppScale && appScale, opacityClearToVisible, styles.flex]}>
112
+ {child}
113
+ </Component>}
114
+ {!animationDone && (
115
+ <Animated.Image
116
+ testID={testID+"AnimateImage"}
117
+ resizeMode={imageBackgroundResizeMode || "cover"}
118
+ source={imageBackgroundSource}
119
+ style={[disableImageBackgroundAnimation && _staticBackground(
120
+ logoOpacity,
121
+ backgroundColor
122
+ ), disableImageBackgroundAnimation && _dynamicImageBackground(
123
+ imageScale,
124
+ logoOpacity,
125
+ backgroundColor
126
+ )]}
127
+ />
128
+ )}
129
+ {!animationDone && (
130
+ <View testID={testID+"_LogoContainer"} style={[StyleSheet.absoluteFill, styles.logoStyle]}>
131
+ {(
132
+ <Animated.View
133
+ testID={testID+"_Logo"}
134
+ style={_dynamicCustomComponentStyle(
135
+ logoScale,
136
+ logoOpacity,
137
+ logoWidth,
138
+ logoHeight
139
+ )}>
140
+ {<LogoProgress/>}
141
+ </Animated.View>
142
+ )}
143
+ </View>
144
+ )}
145
+ </View>
146
+ </View>
147
+ )
180
148
  }
181
149
 
182
- AnimatedSplash.propTypes = {
150
+
151
+ SplashScreenComponent.propTypes = {
183
152
  preload: PropTypes.bool,
184
153
  logoWidth: PropTypes.number,
185
154
  children: PropTypes.element,
@@ -197,4 +166,5 @@ AnimatedSplash.propTypes = {
197
166
  delay: PropTypes.number,
198
167
  }
199
168
 
200
- export default AnimatedSplash
169
+ SplashScreenComponent.displayName = "SplashScreenComponent";
170
+ export default SplashScreenComponent;
package/src/index.js CHANGED
@@ -31,6 +31,13 @@ import {PortalProvider,PortalHost } from '$ecomponents/Portal';
31
31
  import ErrorBoundaryProvider from "$ecomponents/ErrorBoundary/Provider";
32
32
  import notify, {notificationRef} from "$notify";
33
33
  import DropdownAlert from '$ecomponents/Dialog/DropdownAlert';
34
+ import {AuthProvider} from '$cauth';
35
+ import { PreferencesContext } from './Preferences';
36
+ import ErrorBoundary from "$ecomponents/ErrorBoundary";
37
+ import {updateTheme,defaultTheme} from "$theme";
38
+ import StatusBar from "$ecomponents/StatusBar";
39
+ import {Provider as PaperProvider } from 'react-native-paper';
40
+ import FontIcon from "$ecomponents/Icon/Font";
34
41
 
35
42
  let MAX_BACK_COUNT = 1;
36
43
  let countBack = 0;
@@ -48,13 +55,13 @@ const NAVIGATION_PERSISTENCE_KEY = 'NAVIGATION_STATE';
48
55
  * initialRouteName : la route initiale par défaut
49
56
  * getStartedRouteName : la route par défaut de getStarted lorsque l'application est en mode getStarted, c'est à dire lorsque la fonction init renvoie une erreur (reject)
50
57
  */
51
- function App({init:initApp,initialRouteName:appInitialRouteName,getStartedRouteName}) {
58
+ function App({init:initApp,initialRouteName:appInitialRouteName,render,preferences:appPreferences,getStartedRouteName}) {
52
59
  AppStateService.init();
53
60
  const [initialState, setInitialState] = React.useState(undefined);
54
61
  const appReadyRef = React.useRef(true);
55
62
  const [state,setState] = React.useState({
56
63
  isLoading : true,
57
- isInitialized:true,
64
+ isInitialized:false,
58
65
  });
59
66
  React.useEffect(() => {
60
67
  const loadResources = ()=>{
@@ -220,35 +227,67 @@ function App({init:initApp,initialRouteName:appInitialRouteName,getStartedRouteN
220
227
  }
221
228
  },[isInitialized]);
222
229
  const hasGetStarted = state.hasGetStarted !== false? true : false;
230
+
231
+ const [theme,setTheme] = React.useState(updateTheme(defaultTheme));
232
+ const updatePreferenceTheme = (customTheme,persist)=>{
233
+ setTheme(updateTheme(customTheme));
234
+ };
235
+ const forceRender = React.useForceRender();
236
+ const pref = typeof appPreferences =='function'? appPreferences({setTheme,forceRender,updateTheme:updatePreferenceTheme}) : appPreferences;
237
+ const preferences = React.useMemo(()=>({
238
+ updateTheme:updatePreferenceTheme,
239
+ theme,
240
+ ...defaultObj(pref),
241
+ }),[theme,pref]);
242
+ const isLoaded = !isLoading;
243
+ const child = isLoaded ? <NavigationContainer
244
+ ref={navigationRef}
245
+ initialState={initialState}
246
+ onStateChange={(state) =>{
247
+ setSession(NAVIGATION_PERSISTENCE_KEY,decycle(state),false);
248
+ }
249
+ }
250
+ >
251
+ <PortalProvider>
252
+ <Portal.Host>
253
+ <PreloaderProvider/>
254
+ <DialogProvider responsive/>
255
+ <AlertProvider SimpleSelect={SimpleSelect}/>
256
+ <FormDataDialogProvider/>
257
+ <BottomSheetProvider/>
258
+ <DropdownAlert ref={notificationRef}/>
259
+ <ErrorBoundaryProvider/>
260
+ <Navigation
261
+ initialRouteName = {defaultStr(hasGetStarted ? appInitialRouteName : getStartedRouteName,"Home")}
262
+ state = {state}
263
+ hasGetStarted = {hasGetStarted}
264
+ isInitialized = {!isLoading}
265
+ onGetStart = {(e)=>{
266
+ setState({...state,hasGetStarted:true})
267
+ }}
268
+ />
269
+ </Portal.Host>
270
+ </PortalProvider>
271
+ </NavigationContainer> : null;
272
+ const content = isLoaded ? typeof render == 'function'? render({children:child,appConfig,config:appConfig}) : child : null;
223
273
  return (<SplashScreen isLoaded={!isLoading}>
224
- <NavigationContainer
225
- ref={navigationRef}
226
- initialState={initialState}
227
- onStateChange={(state) =>{
228
- setSession(NAVIGATION_PERSISTENCE_KEY,decycle(state),false);
229
- }
230
- }
231
- >
232
- <PortalProvider>
233
- <Portal.Host>
234
- <PreloaderProvider/>
235
- <DialogProvider responsive/>
236
- <AlertProvider SimpleSelect={SimpleSelect}/>
237
- <FormDataDialogProvider/>
238
- <BottomSheetProvider/>
239
- <DropdownAlert ref={notificationRef}/>
240
- <ErrorBoundaryProvider/>
241
- <Navigation
242
- initialRouteName = {defaultStr(hasGetStarted ? appInitialRouteName : getStartedRouteName,"Home")}
243
- state = {state}
244
- hasGetStarted = {hasGetStarted}
245
- onGetStart = {(e)=>{
246
- setState({...state,hasGetStarted:true})
247
- }}
248
- />
249
- </Portal.Host>
250
- </PortalProvider>
251
- </NavigationContainer>
274
+ <AuthProvider>
275
+ <PaperProvider
276
+ theme={theme}
277
+ settings={{
278
+ icon: (props) => {
279
+ return <FontIcon {...props}/>
280
+ },
281
+ }}
282
+ >
283
+ <ErrorBoundary>
284
+ <StatusBar/>
285
+ <PreferencesContext.Provider value={preferences}>
286
+ {React.isValidElement(content) && content || child}
287
+ </PreferencesContext.Provider>
288
+ </ErrorBoundary>
289
+ </PaperProvider>
290
+ </AuthProvider>
252
291
  </SplashScreen>);
253
292
  }
254
293
 
@@ -54,7 +54,7 @@ export default function DatabaseStatisticScreen ({withScreen,fetchDataProps,tabl
54
54
  if(!content.length) {
55
55
  return null;
56
56
  }
57
- content = <Component {...containerProps} style={[containerProps.style,theme.styles.mr1,theme.styles.ml1]}>
57
+ content = <Component {...containerProps} style={[containerProps.style,theme.styles.mr1,theme.styles.pv1,theme.styles.ml1]}>
58
58
  {content}
59
59
  </Component>;
60
60
  return withScreen !== false ? <Screen containerProps={{style:[{flexGrow:0,flex:0}]}} withScrollView title={defaultVal(customTitle,title)} {...props}>{content}</Screen> : content;
@@ -33,30 +33,16 @@ export default class FormDataListScreen extends FormData{
33
33
  if(typeof show =='function'){
34
34
  show({...this.props,index:undefined,data:{}});
35
35
  }
36
- }
37
- doSave(args){
38
-
39
36
  }
40
37
  doSave2New(args){
41
- if(!this.__canSaveListData) return;
42
38
  const {show} = this.props;
43
39
  if(typeof show =='function'){
44
40
  show({...args,index:undefined,data:{}});
45
41
  }
46
42
  }
47
43
  doSave2Close(args){
48
- if(!this.__canSaveListData) return;
49
44
  return this.close();
50
45
  }
51
- onSave(args){
52
- let {formDataProps} = this.props;
53
- this.__canSaveListData = true;
54
- formDataProps = defaultObj(formDataProps);
55
- if(typeof formDataProps.onSave =='function' && formDataProps.onSave(args) === false){
56
- this.__canSaveListData = false;
57
- return;
58
- }
59
- }
60
46
  _render(content){
61
47
  let {isAllowed} = this.props;
62
48
  if(typeof isAllowed ==='function'){
@@ -755,11 +755,14 @@ export default class TableDataScreenComponent extends FormDataScreen{
755
755
  getMakePhoneCallProps (){
756
756
  const table = this.table;
757
757
  const makePhoneCallProps = defaultVal(this.makePhoneCallProps,table.makePhoneCallProps);
758
- return defaultObj(typeof makePhoneCallProps === 'function' ? makePhoneCallProps(this.getCurrentData()) : makePhoneCallProps);
758
+ const rowData = this.getCurrentData();
759
+ const mP = typeof makePhoneCallProps === 'function' ? makePhoneCallProps({rowData,data:rowData,isTableData:true,props:this.props,context:this,table,tableName:this.table}) : makePhoneCallProps;
760
+ return mP !== false ? defaultObj(mP) : null;
759
761
  }
760
762
  makePhoneCall(data){
761
- if(!this.canMakePhoneCall() || !canMakePhoneCall()) return false;
762
- makePCall(defaultObj(data || this.getCurrentData()),this.getMakePhoneCallProps());
763
+ const mP = this.getMakePhoneCallProps();
764
+ if(!this.canMakePhoneCall() || !canMakePhoneCall() || !isObj(mP)) return false;
765
+ makePCall(defaultObj(data || this.getCurrentData()),mP);
763
766
  return false;
764
767
  }
765
768
 
@@ -103,8 +103,11 @@ export function renderActions({context,isUpdate,newElementLabel,makePhoneCallPr
103
103
  rest = defaultObj(rest);
104
104
  newElementLabel = defaultStr(newElementLabel,"Nouveau");
105
105
  let permsObj = checkPermsActions.call(self,{...defaultObj(perms),isUpdate})
106
- makePhoneCallProps = defaultObj(makePhoneCallProps);
106
+ makePhoneCallProps = typeof makePhoneCallProps ==='function'? makePhoneCallProps({data,rowData:data,context:{},isTableDataActions:true,table,tableName:table}): makePhoneCallProps;
107
107
  self.permsObj = permsObj;
108
+ if(makePhoneCallProps !== false){
109
+ makePhoneCallProps = defaultObj(makePhoneCallProps);
110
+ }
108
111
  let save = (!readOnly && !permsObj.canSave || (saveAction === false))? null: {
109
112
  text :hasManyData? 'Modifier': textSave,
110
113
  title :hasManyData? 'Modifier': textSave,
@@ -197,7 +200,7 @@ export function renderActions({context,isUpdate,newElementLabel,makePhoneCallPr
197
200
  flat : true,
198
201
  onPress : createCallback({context:self,action:'new',callback:onPressToCreateNew})
199
202
  } : null,
200
- makePhoneCall : (canMakePhoneCall && isUpdate)?{
203
+ makePhoneCall : (canMakePhoneCall && isUpdate && isObj(makePhoneCallProps))?{
201
204
  text : defaultStr(makePhoneCallProps.text,makePhoneCallProps.label,'Appeler'),
202
205
  isAction : true,
203
206
  icon : defaultStr(makePhoneCallProps.icon,'phone'),
@@ -1,7 +1,7 @@
1
1
  import showConfirm from "$components/Dialog/confirm";
2
- import notify from "$components/Dialog/notify";
2
+ import notify from "$notify";
3
3
  import {defaultArray,arrayValueExists,defaultStr,uniqid} from "$cutils";
4
- import userDbName from "$database/data/tables/users/dbName";
4
+ import userDbName from "$database/tables/users/dbName";
5
5
  import Auth from "$auth";
6
6
  import getData from "$database/getData";
7
7
  import getDB from "$database/getDB";