@fto-consult/expo-ui 5.8.11 → 5.10.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,8 +16,34 @@ 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
- r["$eauth"] = path.resolve(expo,"auth");
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
+ }
42
+
20
43
  r["$ecomponents"] = r["$expo-components"] = path.resolve(expo,"components");
44
+ r["$econfirm"] = path.resolve(r["$expo-components"],"Dialog","confirm");
45
+ r["$confirm"] = r["$confirm"] || r["$econfirm"];
46
+ r["$eauth"] = path.resolve(expo,"auth");
21
47
  r["$etableLink"] = r["$eTableLink"] = path.resolve(r["$ecomponents"],"TableLink");
22
48
  r.$tableLink = r.$TableLink = r.$tableLink || r.$TableLink || path.resolve(r.$etableLink,"default");
23
49
  r["$components"] = r["$components"] || r["$ecomponents"];
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.11",
3
+ "version": "5.10.0",
4
4
  "description": "Bibliothèque de composants UI Expo,react-native",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -58,10 +58,10 @@
58
58
  },
59
59
  "homepage": "https://github.com/borispipo/expo-ui#readme",
60
60
  "dependencies": {
61
- "@emotion/native": "^11.10.6",
61
+ "@emotion/native": "^11.11.0",
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.14.1",
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",
@@ -70,8 +70,8 @@
70
70
  "@react-navigation/native": "^6.1.6",
71
71
  "@react-navigation/native-stack": "^6.9.12",
72
72
  "@shopify/flash-list": "^1.4.0",
73
- "apexcharts": "^3.37.2",
74
- "expo": "^48.0.15",
73
+ "apexcharts": "^3.40.0",
74
+ "expo": "^48.0.17",
75
75
  "expo-camera": "~13.2.1",
76
76
  "expo-clipboard": "~4.1.1",
77
77
  "expo-font": "~11.1.1",
@@ -91,20 +91,20 @@
91
91
  "react-content-loader": "^6.2.1",
92
92
  "react-dom": "^18.2.0",
93
93
  "react-native": "^0.71.3",
94
- "react-native-big-list": "^1.6.0",
94
+ "react-native-big-list": "^1.6.1",
95
95
  "react-native-blob-util": "^0.17.0",
96
96
  "react-native-gesture-handler": "~2.9.0",
97
97
  "react-native-iphone-x-helper": "^1.3.1",
98
98
  "react-native-mime-types": "^2.3.0",
99
- "react-native-paper": "^5.4.1",
100
- "react-native-paper-dates": "^0.15.1",
99
+ "react-native-paper": "^5.8.0",
100
+ "react-native-paper-dates": "^0.16.2",
101
101
  "react-native-reanimated": "~2.14.4",
102
- "react-native-safe-area-context": "^4.5.0",
103
- "react-native-screens": "~3.20.0",
102
+ "react-native-safe-area-context": "^4.5.3",
103
+ "react-native-screens": "^3.20.0",
104
104
  "react-native-svg": "13.4.0",
105
105
  "react-native-web": "~0.18.7",
106
106
  "react-native-webview": "^11.26.0",
107
- "react-virtuoso": "^4.0.9",
107
+ "react-virtuoso": "^4.3.7",
108
108
  "sharp-cli": "^2.1.0",
109
109
  "tippy.js": "^6.3.7",
110
110
  "websql": "^2.0.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
  );
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "SALITE",
3
+ "version": "7.0.0",
4
+ "description": "Logiciel de gestion commerciale pour PME",
5
+ "realeaseDateStr": "1er Juin 2021",
6
+ "releaseDate": "2020-05-23",
7
+ "devMail": "saliteapp@gmail.com",
8
+ "devWebsite": "http://fto-consulting.com/salite/",
9
+ "copyRight": "firsto consulting@Jan 2020",
10
+ "id": "com.ftc.apps.salite7",
11
+ "pouchdbNamePrefix": "com.ftc.apps.slite-",
12
+ "includeFieldsInDatagridFetchOptions": false,
13
+ "feeds": {
14
+ "VIDEOS": {
15
+ "link": "http://fto-consulting.com/salite/feeds",
16
+ "label": "Bibliothèque vidéo",
17
+ "provider": "",
18
+ "icon": "file-video"
19
+ }
20
+ },
21
+ "theme": {
22
+ "light": {
23
+ "primary": "#0073B1",
24
+ "secondary": "#EC008C",
25
+ "primaryOnSurface": "#0073B1",
26
+ "secondaryOnSurface": "#EC008C"
27
+ }
28
+ },
29
+ "author": "@fto-consulting",
30
+ "license": "ISC"
31
+ }
@@ -40,6 +40,7 @@ import Button from "$ecomponents/Button";
40
40
  import stableHash from "stable-hash";
41
41
  import * as XLSX from "xlsx";
42
42
  import {convertToSQL} from "$ecomponents/Filter";
43
+ import appConfig from "$capp/config";
43
44
 
44
45
  export const TIMEOUT = 100;
45
46
 
@@ -934,19 +935,22 @@ export default class CommonDatagridComponent extends AppComponent {
934
935
  })
935
936
  if(size === 1 && canMakePhoneCall === true && canMakeCall()){
936
937
  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
- })
938
+ const table = defaultStr(this.props.table,this.props.tableName).trim();
939
+ let callProps = typeof makePhoneCallProps == 'function'? makePhoneCallProps({rowData,rowKey,table,tableName:table,data:rowData,key:rowKey,context:this,props:this.props}) : makePhoneCallProps;
940
+ if(callProps !== false){
941
+ callProps = defaultObj(callProps);
942
+ r.push({
943
+ text : defaultStr(callProps.text,callProps.label,'Appeler'),
944
+ icon : defaultStr(callProps.icon,'phone'),
945
+ flat : true,
946
+ onPress : ()=>{
947
+ return makePhoneCall(
948
+ rowData,
949
+ callProps
950
+ );
951
+ }
952
+ })
953
+ }
950
954
  }
951
955
  if(isObj(this.props.columns) && size ===1){
952
956
  r.push({
@@ -3365,17 +3369,20 @@ export default class CommonDatagridComponent extends AppComponent {
3365
3369
  fetchOptions.dataSources = this.currentDataSources;
3366
3370
  fetchOptions.selector = fetchFilters;
3367
3371
  fetchOptions.sort = this.getSort();
3368
- const ff = this.getFilterableColumnsNames();
3369
- let fields = ff;
3370
- if(this.isFetchOnlyVisibleColumnsEnabled()){
3371
- fields = [];
3372
- Object.map(ff,(field)=>{
3373
- if(isNonNullString(field) && isObj(this.state.columns[field]) && this.state.columns[field].visible !== false){
3374
- fields.push(field);
3375
- }
3376
- });
3372
+ const canIncludeField = typeof this.props.includeFieldsInFetchOptions =='boolean'? this.props.includeFieldsInFetchOptions : defaultBool(appConfig.get("includeFieldsInDatagridFetchOptions"),appConfig.includeFieldsInDatagridFetchOptions) !== false;
3373
+ if(canIncludeField){
3374
+ const ff = this.getFilterableColumnsNames();
3375
+ let fields = ff;
3376
+ if(this.isFetchOnlyVisibleColumnsEnabled()){
3377
+ fields = [];
3378
+ Object.map(ff,(field)=>{
3379
+ if(isNonNullString(field) && isObj(this.state.columns[field]) && this.state.columns[field].visible !== false){
3380
+ fields.push(field);
3381
+ }
3382
+ });
3383
+ }
3384
+ fetchOptions.fields = fields;
3377
3385
  }
3378
- fetchOptions.fields = fields;
3379
3386
  let limit = this.getQueryLimit();
3380
3387
  if(limit > 0 && !this.isPivotDatagrid()){
3381
3388
  fetchOptions.limit = limit;
@@ -3809,6 +3816,7 @@ CommonDatagridComponent.propTypes = {
3809
3816
  PropTypes.node,
3810
3817
  PropTypes.element,
3811
3818
  ]),
3819
+ includeFieldsInFetchOptions : PropTypes.bool,//si les champs de colonnes seront inclus dans les fetchOptions du datagrid
3812
3820
  canMakePhoneCall : PropTypes.bool,//si l'on peut faire un appel sur la données sélectionnées
3813
3821
  makePhoneCallProps : PropTypes.oneOfType([
3814
3822
  PropTypes.object,
@@ -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);
@@ -397,7 +397,7 @@ export default class Field extends AppComponent {
397
397
  getValidValue (data){
398
398
  let v = this.state.validValue;
399
399
  if(isFunction(this.props.getValidValue)){
400
- let v1 = this.props.getValidValue.call(this,{data,context:this,props:this.props});
400
+ let v1 = this.props.getValidValue.call(this,{data,context:this,value:v,validValue:v,state:this.state,props:this.props});
401
401
  if(v1 !== undefined){
402
402
  v = v1;
403
403
  }
@@ -55,7 +55,6 @@ export default class FormIDField extends TextField {
55
55
  /*** retourne la valeur validée */
56
56
  getValidValue(data){
57
57
  const validValue = super.getValidValue(data);
58
- console.log(data, " is valid value ",data);
59
58
  if(!isNonNullString(this.name)) return validValue;
60
59
  data[this.name] = defaultStr(data[this.name],validValue,this.newFieldIdValue);
61
60
  return validValue;
@@ -3,7 +3,7 @@
3
3
  // license that can be found in the LICENSE file.
4
4
 
5
5
  import Dropdown from "$ecomponents/Dropdown";
6
- import {defaultStr,isFunction,defaultVal,isObjOrArray,defaultObj} from "$cutils";
6
+ import {defaultStr,extendObj,isFunction,defaultVal,isObjOrArray,defaultObj} from "$cutils";
7
7
  import PropTypes from "prop-types";
8
8
  import actions from "$cactions";
9
9
  import {navigateToTableData} from "$enavigation/utils";
@@ -19,7 +19,7 @@ import appConfig from "$appConfig";
19
19
  * foreignKeyTable : la tableData dans laquelle effectuer les donées de la requêtes
20
20
  * foreignKeyLabel : Le libélé dans la table étrangère
21
21
  */
22
- const TableDataSelectField = React.forwardRef(({foreignKeyColumn,bindUpsert2RemoveEvents,onAdd,showAdd:customShowAdd,canShowAdd,foreignKeyTable,fetchItemsPath,foreignKeyLabel,foreignKeyLabelIndex,dropdownActions,fields,fetchItems:customFetchItem,convertFiltersToSQL,mutateFetchedItems,getForeignKeyTable,onFetchItems,isFilter,isUpdate,isDocEditing,items,onAddProps,fetchOptions,...props},ref)=>{
22
+ const TableDataSelectField = React.forwardRef(({foreignKeyColumn,prepareFilters:cPrepareFilters,bindUpsert2RemoveEvents,onAdd,showAdd:customShowAdd,canShowAdd,foreignKeyTable,fetchItemsPath,foreignKeyLabel,foreignKeyLabelIndex,dropdownActions,fields,fetchItems:customFetchItem,convertFiltersToSQL,mutateFetchedItems,getForeignKeyTable,onFetchItems,isFilter,isUpdate,isDocEditing,items,onAddProps,fetchOptions,...props},ref)=>{
23
23
  props.data = defaultObj(props.data);
24
24
  if(!foreignKeyColumn && isNonNullString(props.field)){
25
25
  foreignKeyColumn = props.field;
@@ -138,8 +138,12 @@ const TableDataSelectField = React.forwardRef(({foreignKeyColumn,bindUpsert2Remo
138
138
  if(!isMounted()) return;
139
139
  if(typeof beforeFetchItems ==='function' && beforeFetchItems(fetchOptions) === false) return;
140
140
  let opts = Object.clone(fetchOptions);
141
- opts.selector = prepareFilters(fetchOptions.selector,{convertToSQL:convertFiltersToSQL});
142
- opts = getFetchOptions(opts);
141
+ if(cPrepareFilters !== false){
142
+ opts.selector = prepareFilters(fetchOptions.selector,{convertToSQL:convertFiltersToSQL});
143
+ opts = getFetchOptions(opts);
144
+ } else {
145
+ opts = {fetchOptions};
146
+ }
143
147
  const r = fetchItems(opts);
144
148
  if(r === false) return;
145
149
  setIsLoading(true);
@@ -296,6 +300,7 @@ const TableDataSelectField = React.forwardRef(({foreignKeyColumn,bindUpsert2Remo
296
300
 
297
301
  TableDataSelectField.propTypes = {
298
302
  ...Dropdown.propTypes,
303
+ prepareFilters : PropTypes.bool,//si les filtres seront customisé
299
304
  bindUpsert2RemoveEvents : PropTypes.bool,//si le composant écoutera l'évènement de rafraichissement des données
300
305
  onAdd : PropTypes.func, //({})=>, la fonction appelée lorsque l'on clique sur le bouton add
301
306
  canShowAdd : PropTypes.func, //({foreignKeyTable,foreignKeyColumn})=><boolean> la fonction permettant de spécifier si l'on peut afficher le bouton showAdd
@@ -43,6 +43,7 @@ const styles = StyleSheet.create({
43
43
  inputContainer : {
44
44
  paddingVertical : 0,
45
45
  marginVertical : 0,
46
+ paddingHorizontal : 10,
46
47
  backgroundColor : 'transparent',
47
48
  },
48
49
  contentContainer : {
@@ -121,7 +121,7 @@ const styles = StyleSheet.create({
121
121
  textAlign : 'right'
122
122
  },
123
123
  priceType : {
124
- width:32
124
+ width:32,
125
125
  },
126
126
  weightUnit : {
127
127
  width : 30,
@@ -312,7 +312,7 @@ const SimpleSelect = React.forwardRef((props,ref)=>{
312
312
  style = {[{marginTop}]}
313
313
  anchor = {anchor}
314
314
  contentProps = {{style:{flex:1}}}
315
- minWidth = {150}
315
+ minWidth = {180}
316
316
  contentStyle = {[{paddingVertical:0},rProps.contentStyle]}
317
317
  >
318
318
  <View
@@ -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 ? Math.max(layout.width,150) : undefined,
325
+ width : !isMob ? Math.max(layout.width,180) : 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;
@@ -53,14 +53,17 @@ export default class FormDataLayout extends FormDataActions {
53
53
  close(){
54
54
  return goBack(true);
55
55
  }
56
+ isLoading(){
57
+ return !!(this.props.isLoading)
58
+ }
56
59
  wrapRenderingContent(content,wProps){
57
60
  let {
58
61
  withHeavyScreen,
59
62
  preloader,
60
63
  preloaderProps,
61
64
  testID,
62
- isLoading,
63
65
  } = this.props;
66
+ const isLoading = this.isLoading();
64
67
  wProps = defaultObj(wProps);
65
68
  const useHeavyScreen = withHeavyScreen !== false || isLoading == true? true : false;
66
69
  const Wrapper = useHeavyScreen ? HeavyScreen : React.Fragment;
@@ -20,7 +20,7 @@ import {renderTabsContent,renderActions} from "./utils";
20
20
  import theme from "$theme";
21
21
  import cActions from "$cactions";
22
22
  import APP from "$capp/instance";
23
- import { generatedColumnsProperties } from "./utils";
23
+ import { generatedColumnsProperties,defaultArchivedPermsFilter } from "./utils";
24
24
  import {isDocEditing,checkPrimaryKey} from "$ecomponents/Form";
25
25
  import i18n from "$i18n";
26
26
  import fetch from "$capi/fetch";
@@ -55,7 +55,7 @@ export default class TableDataScreenComponent extends FormDataScreen{
55
55
  if(r === false || (!args.value && typeof args.value != 'number')) return r;
56
56
  //on applique la validation seulement en cas de non mise à jour
57
57
  if(!this.isCurrentDocEditingUpdate() && context && typeof context.onNoValidate =='function'){
58
- const cb = typeof field.fetchUniqueId =='function'? field.fetchUniqueId : typeof this.fetchUniqueId =='function'? this.fetchUniqueId : undefined;
58
+ const cb = typeof field.fetchUniqueId =='function'? field.fetchUniqueId : typeof this.props.fetchUniqueId =='function'? this.props.fetchUniqueId : undefined;
59
59
  if(cb){
60
60
  const r2 = cb(args);
61
61
  if(isPromise(r2)){
@@ -93,21 +93,11 @@ export default class TableDataScreenComponent extends FormDataScreen{
93
93
  tableName : { value : defaultStr(table.table,table.tableName)},
94
94
  fields : {value : fields},
95
95
  table : {value : table},
96
+ archivedPermsFilterFunc : {value : typeof mainProps.archivedPermsFilter =='function'? mainProps.archivedPermsFilter:defaultArchivedPermsFilter},
96
97
  isDocEditingRef : {value : {current:false}},
97
- validateDataBeforeSave : {value : mainProps.validateData},
98
- upsertDataToDB : {value : mainProps.upsertToDB},
99
- makePhoneCallProps : {value : mainProps.makePhoneCallProps},
100
- onSaveProp : {value : mainProps.onSave},
101
- titleProp : {value : mainProps.title},
102
98
  closeOnSaveProp : {value : mainProps.closeOnSave || mainProps.closeAfterSave },
103
- newActionProp : {value : mainProps.newAction},
104
- fetchUniqueId : {value : mainProps.fetchUniqueId},
105
99
  //la liste des champ de type clé primaire associés à la table
106
100
  primaryKeyFields : {value : primaryKeyFields},
107
- cloneProp : {value : typeof mainProps.clone =='function' && mainProps.clone || undefined},
108
- printProp : {value : typeof mainProps.print =='function' && mainProps.print || undefined},
109
- archiveProp : {value : typeof mainProps.archive =='function' && mainProps.archive || undefined },
110
- testIDProp : {value : defaultStr(mainProps.testID)},
111
101
  showPreloaderOnUpsert : {value : mainProps.showPreloaderOnUpsert},
112
102
  });
113
103
  this.hidePreloader = this.hidePreloader.bind(this);
@@ -145,7 +135,7 @@ export default class TableDataScreenComponent extends FormDataScreen{
145
135
  return defaultObj(this.state.data);
146
136
  }
147
137
  /**** retourne les props en cours d'édition */
148
- getCurrentEditingProps (){
138
+ getCurrentRenderingProps (){
149
139
  return defaultObj(this.currentRenderingProps);
150
140
  }
151
141
  /* Attention, cette méthode est appélée dans la méthode getComponentProps de la classe parente.
@@ -165,15 +155,44 @@ export default class TableDataScreenComponent extends FormDataScreen{
165
155
  * datas: la liste des données passées à la TableData
166
156
  * }
167
157
  */
168
- getActionsPerms({perm,action,tableName}){
169
- return (isNonNullString(perm)? Auth.isAllowed({resource:perm.split(':')[0],action}):Auth.isTableDataAllowed({table:tableName,action}));
158
+ getActionsPerms(args){
159
+ const eProps = this.getCurrentRenderingProps();
160
+ const gPA = typeof eProps.getActionsPerms =='function'? eProps.getActionsPerms : typeof this.props.getActionsPerms =='function'? this.props.getActionsPerms : undefined;
161
+ const permsR = gPA ? gPA.call(this,args) : null;
162
+ const {perm,action,tableName} = args;
163
+ const ePerms = (isNonNullString(perm)? Auth.isAllowed({resource:perm.split(':')[0],action}):Auth.isTableDataAllowed({table:tableName,action}));
164
+ return isObj(permsR) ? extendObj({},ePerms,permsR) : ePerms;
170
165
  }
171
166
  getRenderedActionPrefix (){}
172
167
  renderActions(){
173
168
  return null;
174
169
  }
170
+ isLoading(){
171
+ return !!(this.getCurrentRenderingProps().isLoading) || !!this.props.isLoading;
172
+ }
173
+ /**** permet de preparer les composant props
174
+ si la fonction retourne un élément react, alors l'élément react est rendu comme résultat du composant
175
+ */
176
+ prepareComponentProps(props){
177
+ const obj = typeof this.props.prepareComponentProps =='function'? this.props.prepareComponentProps(props) : null;
178
+ if(isObj(obj)){
179
+ return extendObj({},props,obj);
180
+ }
181
+ return props;
182
+ }
175
183
  getComponentProps(props){
176
184
  this.resetState();
185
+ const table = this.table;
186
+ const {datas,currentIndex,data} = this.state;
187
+ const tableName = this.tableName;
188
+ const isUpdated = this.isDocEditing(data);
189
+ this.isDocEditingRef.current = !!isUpdated;
190
+ const isMobOrTab = isMobileOrTabletMedia();
191
+ let archived = this.isArchived();
192
+ this.INITIAL_STATE.archived = archived;
193
+ this.INITIAL_STATE.tableName = tableName;
194
+ const fields = {};
195
+ const fieldsToPrepare = extendObj({},true,this.fields,customFields);
177
196
  const {
178
197
  actions,
179
198
  fields:customFields,
@@ -202,20 +221,10 @@ export default class TableDataScreenComponent extends FormDataScreen{
202
221
  formProps : customFormProps,
203
222
  newElementLabel,
204
223
  prepareField,
224
+ prepareComponentProps,
205
225
  ...rest
206
- } = props;
207
- const table = this.table;
208
- const {datas,currentIndex,data} = this.state;
209
- const tableName = this.tableName;
226
+ } = this.prepareComponentProps({...props,tableName,fields:fieldsToPrepare,isUpdated,isUpdate:isUpdated,data,datas,currentIndex});
210
227
  const sessionName = this.INITIAL_STATE.sessionName = defaultStr(customSessionName,"table-form-data"+tableName);
211
- const isUpdated = this.isDocEditing(data);
212
- this.isDocEditingRef.current = !!isUpdated;
213
- const isMobOrTab = isMobileOrTabletMedia();
214
- let archived = this.isArchived();
215
- this.INITIAL_STATE.archived = archived;
216
- this.INITIAL_STATE.tableName = tableName;
217
- const fields = {};
218
- const fieldsToPrepare = extendObj({},true,this.fields,customFields);
219
228
  const prepareCb = typeof prepareField =='function'? prepareField : x=> x;
220
229
  ///on effectue une mutator sur le champ en cours de modification
221
230
  Object.map(fieldsToPrepare,(field,i,counterIndex)=>{
@@ -287,11 +296,11 @@ export default class TableDataScreenComponent extends FormDataScreen{
287
296
  sessionName,
288
297
  table,
289
298
  newElementLabel : this.getNewElementLabel(),
290
- printable : this.isPrintable(),///si la table data est imprimable,
299
+ printable : (typeof rest.printable ==='boolean' ? rest.printable : true) && this.isPrintable(),///si la table data est imprimable,
291
300
  canMakePhoneCall : this.canMakePhoneCall(),
292
301
  makePhoneCallProps:this.getMakePhoneCallProps(),
293
302
  onPressToMakePhoneCall : this.makePhoneCall.bind(this),
294
- archivable : this.isArchivable(),
303
+ archivable : (typeof rest.archivable ==='boolean' ? rest.archivable : true) && this.isArchivable(),
295
304
  saveButton : isUpdated?'Modifier':'Enregistrer',
296
305
  currentData:data,
297
306
  hasManyData : this.hasManyData(),
@@ -355,10 +364,10 @@ export default class TableDataScreenComponent extends FormDataScreen{
355
364
  return rActionsArg;
356
365
  }
357
366
  _render ({header,content,context}){
358
- const restProps = this.getCurrentEditingProps();
367
+ const restProps = this.getCurrentRenderingProps();
359
368
  delete restProps.tabs;
360
369
  let {tabProps,firstTabProps,tabsProps,withScrollView} = restProps;
361
- let testID = this.testIDProp;
370
+ let testID = this.props.testID;
362
371
  tabsProps = defaultObj(tabsProps);
363
372
  tabsProps.tabContentProps = defaultObj(tabsProps.tabContentProps);
364
373
  tabsProps.tabContentProps.stopChildrenEventPropagation = typeof tabsProps.tabContentProps.stopChildrenEventPropagation =="function" ? tabsProps.tabContentProps.stopChildrenEventPropagation : false;
@@ -438,7 +447,9 @@ export default class TableDataScreenComponent extends FormDataScreen{
438
447
  return this.state.currentIndex;
439
448
  }
440
449
  isArchivable(){
441
- return false;
450
+ const editingProps = this.getCurrentRenderingProps();
451
+ if(typeof editingProps.archivable =='boolean') return editingProps.archivable;
452
+ return !!this.props.archivable;
442
453
  }
443
454
  isArchived(data){
444
455
  data = defaultObj(data,this.state.data);
@@ -491,7 +502,7 @@ export default class TableDataScreenComponent extends FormDataScreen{
491
502
  return true;
492
503
  }
493
504
  isPrintable(){
494
- return false;
505
+ return !!(this.props.printable);
495
506
  }
496
507
  /*** retourne la liste des valeurs de clé primarire associés à la table data pour la données en cours de modification
497
508
  * Elle permet d'afficher dans la barre de titre, les identifiants de la table de données en cours de modification
@@ -519,17 +530,17 @@ export default class TableDataScreenComponent extends FormDataScreen{
519
530
  return super.isDocEditing(data);
520
531
  }
521
532
  print(data){
522
- if(!this.isPrintable() && typeof this.printProp!=='function') return;
533
+ if(!this.isPrintable() && typeof this.props.print !=='function') return;
523
534
  data = this.isDocEditing(data)? data : isObj(data) && this.isDocEditing(data.data)? data.data : {};
524
- return this.printProp(data,this);
535
+ return this.props.print(data,this);
525
536
  }
526
537
  isClonable(){
527
- return true;
538
+ return !!(this.props.clonable !==false);
528
539
  }
529
540
  clone (data){
530
541
  if(!this._isMounted() || !this.isClonable())return data;
531
542
  data = {...this.getCurrentEditingData(data)};
532
- if(this.cloneProp && this.cloneProp(data,this) === false) return data;
543
+ if(typeof this.props.clone ==='function' && this.props.clone(data,this) === false) return data;
533
544
  this.showPreloader();
534
545
  delete data.approved;
535
546
  Object.map(['_rev',...generatedColumnsProperties,...Object.keys(this.primaryKeyFields),'_id','code','updateBy','updatedDate','createBy','updatedHour','createdHour','createdDate'],(idx)=>{
@@ -580,8 +591,8 @@ export default class TableDataScreenComponent extends FormDataScreen{
580
591
  return true;
581
592
  }
582
593
  upsertToDB(args){
583
- if(typeof this.upsertDataToDB ==='function'){
584
- return this.upsertDataToDB(args);
594
+ if(typeof this.props.upsertToDB ==='function'){
595
+ return this.props.upsertToDB(args);
585
596
  }
586
597
  return Promise.resolve({});
587
598
  }
@@ -596,15 +607,19 @@ export default class TableDataScreenComponent extends FormDataScreen{
596
607
  return this.doSave(args);
597
608
  }
598
609
  canCreateNew(){
599
- return this.newActionProp !== false ? true : false;
610
+ const editingProps = this.getCurrentRenderingProps();
611
+ if("newAction" in editingProps){
612
+ return !!editingProps.newAction;
613
+ }
614
+ return this.props.newAction !== false ? true : false;
600
615
  }
601
616
  createNew(){
602
617
  return this.reset();
603
618
  }
604
619
  archive(){}
605
620
  validateData(args){
606
- if(typeof this.validateDataBeforeSave =='function'){
607
- return this.validateDataBeforeSave(args);
621
+ if(typeof this.props.validateData =='function'){
622
+ return this.props.validateData(args);
608
623
  }
609
624
  return true;
610
625
  }
@@ -659,7 +674,7 @@ export default class TableDataScreenComponent extends FormDataScreen{
659
674
  let savedData = this.isDocEditing(upserted)? upserted : data;
660
675
  const newArgs = {tableName,actionName:action,action,table:this.table,data:savedData,result:upserted,context};
661
676
  APP.trigger(cActions.upsert(tableName),newArgs);
662
- if(this.onSaveTableData(newArgs) === false || (isFunction(this.onSaveProp)&& this.onSaveProp(newArgs) === false)){
677
+ if(this.onSaveTableData(newArgs) === false || (isFunction(this.props.onSave)&& this.props.onSave(newArgs) === false)){
663
678
  closePreloader();
664
679
  return;
665
680
  }
@@ -754,20 +769,23 @@ export default class TableDataScreenComponent extends FormDataScreen{
754
769
  }
755
770
  getMakePhoneCallProps (){
756
771
  const table = this.table;
757
- const makePhoneCallProps = defaultVal(this.makePhoneCallProps,table.makePhoneCallProps);
758
- return defaultObj(typeof makePhoneCallProps === 'function' ? makePhoneCallProps(this.getCurrentData()) : makePhoneCallProps);
772
+ const makePhoneCallProps = defaultVal(this.props.makePhoneCallProps,table.makePhoneCallProps);
773
+ const rowData = this.getCurrentData();
774
+ const mP = typeof makePhoneCallProps === 'function' ? makePhoneCallProps({rowData,data:rowData,isTableData:true,props:this.props,context:this,table,tableName:this.table}) : makePhoneCallProps;
775
+ return mP !== false ? defaultObj(mP) : null;
759
776
  }
760
777
  makePhoneCall(data){
761
- if(!this.canMakePhoneCall() || !canMakePhoneCall()) return false;
762
- makePCall(defaultObj(data || this.getCurrentData()),this.getMakePhoneCallProps());
778
+ const mP = this.getMakePhoneCallProps();
779
+ if(!this.canMakePhoneCall() || !canMakePhoneCall() || !isObj(mP)) return false;
780
+ makePCall(defaultObj(data || this.getCurrentData()),mP);
763
781
  return false;
764
782
  }
765
783
 
766
784
  /*** archivedPermsFilter est la fonction permettant de filtres les permissions qui par défaut ne figurent pas parmis les permissions en readOnly
767
785
  * si archivedPermFilter retourne true pour une permission données alors cette permission sera ignorée
768
786
  */
769
- archivedPermsFilter (perm,perms){
770
- return true;
787
+ archivedPermsFilter (...args){
788
+ return !!this.archivedPermsFilterFunc(...args);
771
789
  };
772
790
  copyToClipboard(){
773
791
  return copyToClipboard({
@@ -791,7 +809,8 @@ export default class TableDataScreenComponent extends FormDataScreen{
791
809
  return ret;
792
810
  }
793
811
  getAppBarTitle (){
794
- return defaultStr(this.titleProp,this.table.text,this.table.label);
812
+ const editingProps = this.getCurrentRenderingProps();
813
+ return React.isValidElement(editingProps.title,true) && editingProps.title || React.isValidElement(this.props.title,true) && this.props.title || this.table.text || this.table.label || null;
795
814
  }
796
815
  getAppBarProps(a){
797
816
  const r = super.getAppBarProps(a);
@@ -808,6 +827,7 @@ export default class TableDataScreenComponent extends FormDataScreen{
808
827
 
809
828
  TableDataScreenComponent.propTypes = {
810
829
  ...defaultObj(FormData.propTypes),
830
+ prepareComponentProps : PropTypes.func, //permet d'appreter les components props à utiliser pour le rendu des données
811
831
  prepareField : PropTypes.func,//La fonction permettant de faire des mutations sur le champ field à passer au formulaire form. si elle retourne false alors la field ne sera pas pris een compte
812
832
  table : PropTypes.shape({
813
833
  tableName : PropTypes.string,
@@ -817,7 +837,7 @@ TableDataScreenComponent.propTypes = {
817
837
  unique : PropTypes.bool,//si la validation de type unique sur le champ sera effective
818
838
  fetchUniqueId : PropTypes.func,//la fonction permettant de fetch un élément unique pour la validation de type uniqueID, liée aux champs de type piece et id
819
839
  validateData : PropTypes.func,// la fonction permettant de valider les données à enregistrer
820
- archivedPermsFilter : PropTypes.func,///le filtre des permissions archivées
840
+ archivedPermsFilter : PropTypes.func,///le filtre des permissions archivées, elle permet de laisser uniquement les permissions de faire un filtre sur les permission et ne laisser que celle qui sont considérées comme disposible en cas de document archivé
821
841
  newElementLabel : PropTypes.string,//le titre du bouton nouveau pour l'ajout d'un nouvel élément
822
842
  customActionKeyPrefix : PropTypes.oneOfType([
823
843
  PropTypes.string,
@@ -71,11 +71,10 @@ export const renderTabsContent = ({tabs,context,data,sessionName,tabsPropsMutato
71
71
  }
72
72
  return null;
73
73
  }
74
-
75
-
76
- export function renderActions({context,isUpdate,newElementLabel,makePhoneCallProps,hasManyData,onPressCopyToClipboard,archived,archivedPermsFilter,canMakePhoneCall,onPressToMakePhoneCall,saveAction,save2newAction,save2closeAction,cloneAction,readOnly,printable,archivable,data,table,perm,tableName,saveButton,datas,rows,currentData,currentDataIndex,onPressToSave,onPressToCreateNew,onPressToPrint,onPressToPrevious,onPressToNext,onPressToArchive,...rest}){
74
+ export const readablePerms = ["read","print"];
75
+ export const defaultArchivedPermsFilter = ({perm})=>!readablePerms.includes(perm) && !readablePerms.includes(perm.toLowerCase());
76
+ export function renderActions({context,isUpdate,newElementLabel,readablePerms:cReadablePerms,makePhoneCallProps,hasManyData,onPressCopyToClipboard,archived,archivedPermsFilter,canMakePhoneCall,onPressToMakePhoneCall,saveAction,save2newAction,save2closeAction,cloneAction,readOnly,printable,archivable,data,table,perm,tableName,saveButton,datas,rows,currentData,currentDataIndex,onPressToSave,onPressToCreateNew,onPressToPrint,onPressToPrevious,onPressToNext,onPressToArchive,...rest}){
77
77
  let textSave = defaultStr(saveButton)
78
- archivedPermsFilter = defaultFunc(archivedPermsFilter,x=>true);
79
78
  table = defaultStr(tableName,table);
80
79
  datas = defaultArray(datas,rows);
81
80
  const self = context || {}
@@ -83,9 +82,10 @@ export function renderActions({context,isUpdate,newElementLabel,makePhoneCallPr
83
82
  const getActionsPerms = isFunction(self.getActionsPerms) ? self.getActionsPerms.bind(self) : undefined;
84
83
  let perms = {};
85
84
  readOnly = defaultBool(readOnly,false);
85
+ const callArgs = {readOnly,perm,archived,isUpdate,currentData,action,data,tableName:table,table,context,datas};
86
86
  if(getActionsPerms){
87
87
  /**** getAction perms est la fonction appelée parl'objet TableData, pour retourner les permission des actions de la tableData */
88
- perms = getActionsPerms.call(self,{readOnly,perm,isUpdate,currentData,action,data,tableName:table,table,context,datas})
88
+ perms = getActionsPerms.call(self,callArgs)
89
89
  } else {
90
90
  perms = (isNonNullString(perm)? Auth.isAllowed({resource:perm.split(':')[0],action}):Auth.isTableDataAllowed({table,action}));
91
91
  }
@@ -97,14 +97,20 @@ export function renderActions({context,isUpdate,newElementLabel,makePhoneCallPr
97
97
  delete perms.archive;
98
98
  delete perms.archivable;
99
99
  }
100
- if(isUpdate && isObj(data) && (data.wasTransferred || (data.approved && !perms.updateapproved))){
101
- archived = true;
100
+ if(archived){
101
+ archivedPermsFilter = defaultFunc(archivedPermsFilter,defaultArchivedPermsFilter);
102
+ Object.map(perms,(p,perm)=>{
103
+ if(archivedPermsFilter({perm,perms,data,tableName,readOnly,currentData,context,tableName,isUpdate})) delete perms[i];
104
+ });
102
105
  }
103
106
  rest = defaultObj(rest);
104
107
  newElementLabel = defaultStr(newElementLabel,"Nouveau");
105
108
  let permsObj = checkPermsActions.call(self,{...defaultObj(perms),isUpdate})
106
- makePhoneCallProps = defaultObj(makePhoneCallProps);
109
+ makePhoneCallProps = typeof makePhoneCallProps ==='function'? makePhoneCallProps({data,rowData:data,context:{},isTableDataActions:true,table,tableName:table}): makePhoneCallProps;
107
110
  self.permsObj = permsObj;
111
+ if(makePhoneCallProps !== false){
112
+ makePhoneCallProps = defaultObj(makePhoneCallProps);
113
+ }
108
114
  let save = (!readOnly && !permsObj.canSave || (saveAction === false))? null: {
109
115
  text :hasManyData? 'Modifier': textSave,
110
116
  title :hasManyData? 'Modifier': textSave,
@@ -197,7 +203,7 @@ export function renderActions({context,isUpdate,newElementLabel,makePhoneCallPr
197
203
  flat : true,
198
204
  onPress : createCallback({context:self,action:'new',callback:onPressToCreateNew})
199
205
  } : null,
200
- makePhoneCall : (canMakePhoneCall && isUpdate)?{
206
+ makePhoneCall : (canMakePhoneCall && isUpdate && isObj(makePhoneCallProps))?{
201
207
  text : defaultStr(makePhoneCallProps.text,makePhoneCallProps.label,'Appeler'),
202
208
  isAction : true,
203
209
  icon : defaultStr(makePhoneCallProps.icon,'phone'),
@@ -88,4 +88,5 @@ DrawerNavigator.propTypes = {
88
88
 
89
89
  export default DrawerNavigator;
90
90
 
91
- export * from "./utils";
91
+ export * from "./utils";
92
+ export * from "./items";
File without changes
@@ -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";