@fto-consult/expo-ui 2.12.7 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fto-consult/expo-ui",
3
- "version": "2.12.7",
3
+ "version": "2.13.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.0",
62
62
  "@expo/html-elements": "^0.2.0",
63
63
  "@expo/vector-icons": "^13.0.0",
64
- "@fto-consult/common": "^1.20.2",
64
+ "@fto-consult/common": "^1.21.1",
65
65
  "@gorhom/portal": "^1.0.14",
66
66
  "@react-native-async-storage/async-storage": "~1.17.3",
67
67
  "@react-native-community/datetimepicker": "6.5.2",
@@ -102,7 +102,8 @@
102
102
  "react-native-web": "~0.18.7",
103
103
  "react-native-webview": "11.23.1",
104
104
  "sharp-cli": "^2.1.0",
105
- "tippy.js": "^6.3.7"
105
+ "tippy.js": "^6.3.7",
106
+ "xlsx": "^0.18.5"
106
107
  },
107
108
  "devDependencies": {
108
109
  "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
package/readMe.md CHANGED
@@ -5,4 +5,7 @@
5
5
  -->
6
6
 
7
7
  ## Installation des dépendences, packages de dévéloppement
8
- npm i --D @babel/plugin-proposal-export-namespace-from @babel/preset-react babel-plugin-inline-dotenv babel-plugin-module-resolver babel-plugin-transform-inline-environment-variables @expo/metro-config @expo/webpack-config
8
+
9
+ ```
10
+ npm i --D @babel/plugin-proposal-export-namespace-from @babel/preset-react babel-plugin-inline-dotenv babel-plugin-module-resolver babel-plugin-transform-inline-environment-variables @expo/metro-config @expo/webpack-config
11
+ ```
@@ -5,7 +5,7 @@ import {defaultNumber,defaultStr} from "$utils";
5
5
 
6
6
  export const getDefaultStep = (count)=>{
7
7
  count = Math.abs(parseInt(count)||0);
8
- return count <= 20 ? 1 : count <= 50 ? 3 : count <= 100 ? 10 : Math.floor(count/20);
8
+ return count <= 20 ? 5 : count <= 50 ? 5 : count <= 100 ? 10 : Math.floor(count/10);
9
9
  }
10
10
 
11
11
  const CountUpComponent = ({from,to,format,type,duration,step,formatter,interval,...props})=>{
@@ -12,6 +12,7 @@ import Auth from "$cauth";
12
12
  import DateLib from "$lib/date";
13
13
  import {getFetchOptions} from "$cutils/filters";
14
14
  import {setQueryParams} from "$cutils/uri";
15
+ import { getFetcherOptions } from "$capi/fetch";
15
16
  import Icon from "$ecomponents/Icon";
16
17
  import Label from "$ecomponents/Label";
17
18
  import { StyleSheet,View } from "react-native";
@@ -93,28 +94,40 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
93
94
  fetchPath = defaultStr(fetchPath,table.queryPath,tableName.toLowerCase()).trim();
94
95
  const innerRef = React.useRef(null);
95
96
  const showProgressRef = React.useRef(true);
96
- const {data:fetchedData, error, isValidating,size, setSize,isLoading,refresh} = useSWR(fetchPath,{
97
- fetchOptionsMutator : (opts)=>{
98
- const {url} = opts;
99
- const fo = fetchOptionsRef.current;
100
- if(Object.size(fo.queryParams,true)){
101
- return {url : setQueryParams(url,fo.queryParams)};
97
+ const dataRef = React.useRef(null);
98
+ const hasResultRef = React.useRef(false);
99
+ const totalRef = React.useRef(0);
100
+ const isFetchingRef = React.useRef(false);
101
+ const {error, isValidating,isLoading,refresh} = useSWR(fetchPath,{
102
+ fetcher : (url,opts)=>{
103
+ opts = extendObj({},opts,fetchOptionsRef.current);
104
+ opts.queryParams = defaultObj(opts.queryParams);
105
+ opts.queryParams.withTotal = true;
106
+ const fetchCB = ({data,total})=>{
107
+ totalRef.current = total;
108
+ dataRef.current = data;
109
+ hasResultRef.current = true;
110
+ return data;
111
+ };
112
+ hasResultRef.current = false;
113
+ isFetchingRef.current = true;
114
+ if(typeof fetcher =='function'){
115
+ url = setQueryParams(url,opts.queryParams);
116
+ return fetcher(url,opts).then(fetchCB).finally(()=>{
117
+ isFetchingRef.current = false;
118
+ });
102
119
  }
120
+ const {url:fUrl,fetcher:cFetcher,...rest} = getFetcherOptions(url,opts);
121
+ return cFetcher(fUrl,rest).then(fetchCB).finally(()=>{
122
+ isFetchingRef.current = false;
123
+ });
103
124
  },
104
- fetcher,
105
125
  showError : false,
106
126
  swrOptions : {
107
127
  ...swrOptions,
108
128
  ...defaultObj(appConfig.swr),
109
129
  },
110
130
  });
111
- const fetchedRef = React.useRef(fetchedData);
112
- const hasResult = !isLoading && !isValidating && isObj(fetchedData) && 'data' in fetchedData;
113
- if(hasResult){
114
- fetchedRef.current = fetchedData;
115
- }
116
- const data = fetchedRef.current?.data;
117
- const total = fetchedRef.current?.total;
118
131
  React.useEffect(()=>{
119
132
  innerRef.current && innerRef.current.setIsLoading && innerRef.current.setIsLoading(isLoading);
120
133
  },[isLoading])
@@ -126,7 +139,8 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
126
139
  }
127
140
  },[isValidating,isLoading])
128
141
  const doRefresh = (showProgress)=>{
129
- showProgressRef.current = showProgress ? typeof showProgress ==='boolean' : false;
142
+ if(isFetchingRef.current) return;
143
+ showProgressRef.current = showProgress ? typeof showProgress ==='boolean' : true;
130
144
  refreshCBRef.current = ()=>{
131
145
  showProgressRef.current = true;
132
146
  };
@@ -152,15 +166,14 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
152
166
  isLoading = {isLoading|| isValidating && !error && showProgressRef.current && true || false}
153
167
  beforeFetchData = {({fetchOptions:opts})=>{
154
168
  opts.fields = fetchFields;
155
- opts = getFetchOptions({fetcher,showError:showProgressRef.current,...opts});
156
- opts.queryParams.withTotal = true;
169
+ opts = getFetchOptions({showError:showProgressRef.current,...opts});
157
170
  fetchOptionsRef.current = opts;
158
171
  doRefresh(true);
159
172
  return false;
160
173
  }}
161
174
  isTableData
162
175
  fetchData = {undefined}
163
- data = {data}
176
+ data = {dataRef.current}
164
177
  canMakePhoneCall={canMakePhoneCall}
165
178
  key={tableName}
166
179
  sessionName={defaultStr(sessionName,'list-data')}
@@ -10,7 +10,7 @@ import DrawerItems from './DrawerItems';
10
10
  import {isDesktopMedia} from "$cplatform/dimensions";
11
11
  import {isMobileNative} from "$cplatform";
12
12
  import {open,close} from "$epreloader";
13
- import {DRAWER_POSITIONS,DRAWER_TYPES,MINIMIZED_WIDTH,DRAWER_WIDTH,MINIMIZED_ICON_SIZE,ICON_SIZE} from './utils';
13
+ import {DRAWER_POSITIONS,DRAWER_TYPES,MINIMIZED_WIDTH,getDrawerWidth,MINIMIZED_ICON_SIZE,ICON_SIZE} from './utils';
14
14
  import Icon,{MENU_ICON} from "$ecomponents/Icon";
15
15
  import apiSession from "./session";
16
16
  import View from "$ecomponents/View";
@@ -82,7 +82,7 @@ const DrawerComponent = React.forwardRef((props,ref)=>{
82
82
  if(!overlayColor){
83
83
  overlayColor = theme.colors.backdrop;// drawerType === DRAWER_TYPES.front ? 'black' : '#00000000';
84
84
  }
85
- let drawerWidth = DRAWER_WIDTH;
85
+ let drawerWidth = getDrawerWidth();
86
86
  const restP = {};
87
87
  const isDesktop = isDesktopMedia();
88
88
  const _canBeMinimizedOrPermanent = canBeMinimizedOrPermanent();
@@ -155,7 +155,7 @@ const DrawerComponent = React.forwardRef((props,ref)=>{
155
155
  isPermanent = false;
156
156
  isMinimized = false;
157
157
  restP.drawerLockMode = "unlocked";
158
- drawerWidth = DRAWER_WIDTH;
158
+ drawerWidth = drawerWidth;
159
159
  }
160
160
  context.isMinimizable = ()=>{
161
161
  return minimizable !== false && canBeMinimizedOrPermanent() ? true : false;
@@ -1,4 +1,4 @@
1
- import {isDesktopMedia} from "$cdimensions";
1
+ import Dimensions,{isDesktopMedia} from "$cdimensions";
2
2
 
3
3
  export * from "./DrawerItems/utils";
4
4
 
@@ -6,7 +6,17 @@ let activeItem = null;
6
6
  export const getActiveItem = x=> activeItem;
7
7
  export {default as session} from "./session";
8
8
 
9
- export const DRAWER_WIDTH = 300;
9
+ const DRAWER_WIDTH = 320;
10
+ const DESKTOP_DRAWER_WIDTH = 280;
11
+
12
+ export const getDrawerWidth = ()=>{
13
+ if(isDesktopMedia()) return DESKTOP_DRAWER_WIDTH;
14
+ const {width} = Dimensions.get("window");
15
+ if(width > DRAWER_WIDTH + 100) return DRAWER_WIDTH;
16
+ const percent = Math.floor((width <= DRAWER_WIDTH?90:80)*width/100);
17
+ if(width <= DESKTOP_DRAWER_WIDTH) return percent;
18
+ return Math.max(percent,DESKTOP_DRAWER_WIDTH);
19
+ }
10
20
 
11
21
  export const MINIMIZED_WIDTH = 85;
12
22
 
@@ -0,0 +1,96 @@
1
+ import {defaultStr,isNonNullString} from "$utils";
2
+ import React from "$react";
3
+ import CountUp from "$ecomponents/CountUp";
4
+ import Avatar from "$ecomponents/Avatar";
5
+ import { ActivityIndicator } from "react-native-paper";
6
+ import Item from "$ecomponents/Expandable/Item";
7
+ import {navigateToTableDataList} from "$enavigation/utils";
8
+ import theme from "$theme";
9
+ import Label from "$ecomponents/Label";
10
+ import PropTypes from "prop-types";
11
+ import APP from "$capp/instance"
12
+ import cActions from "$cactions";
13
+ import {View} from "react-native";
14
+
15
+ export default function DatabaseStatisticContainer (props){
16
+ const [state,setState] = React.useState({
17
+ isLoading : true,
18
+ count : 0,
19
+ });
20
+ let {table,fetchCount,index,testID,title,icon,onPress} = props;
21
+ title = defaultStr(title)
22
+ table = defaultObj(table);
23
+ const tableName = defaultStr(table.tableName,table.table).toUpperCase();
24
+ fetchCount = typeof table.fetchCount =='function'? table.fetchCount : typeof fetchCount =='function'? fetchCount : undefined;
25
+ if(!fetchCount || !tableName) return null;
26
+ const refreshingRef = React.useRef(null);
27
+ const isMounted = React.useIsMounted();
28
+ const refresh = ()=>{
29
+ if(refreshingRef.current || !isMounted()) return;
30
+ refreshingRef.current = true;
31
+ setTimeout(()=>{
32
+ fetchCount().then((count)=>{
33
+ setState({...state,isLoading:false,count});
34
+ refreshingRef.current = false;
35
+ }).catch((e)=>{
36
+ setState({
37
+ isLoading : false, count : 0,
38
+ });
39
+ refreshingRef.current = false;
40
+ });
41
+ },100);
42
+ }
43
+
44
+ React.useEffect(()=>{
45
+ APP.on(cActions.upsert(tableName),refresh);
46
+ APP.on(cActions.remove(tableName),refresh);
47
+ refresh();
48
+ return ()=>{
49
+ APP.off(cActions.upsert(tableName),refresh);
50
+ APP.off(cActions.remove(tableName),refresh);
51
+ }
52
+ },[]);
53
+ React.useEffect(()=>{
54
+ refresh();
55
+ },[props])
56
+ const {isLoading,count} = state;
57
+ return <Item
58
+ testID = {defaultStr(testID,"RN_DatabaseStatistic_"+table)}
59
+ onPress = {(args)=>{
60
+ if(onPress && onPress(args) === false) return;
61
+ navigateToTableDataList(tableName,{
62
+ tableName
63
+ })
64
+ }}
65
+ left = {(aProps)=>{
66
+ return <Avatar suffix={index} {...aProps} icon= {icon} size={40} label={title}/>
67
+ }}
68
+ //right = {(rP)=><Icon {...rP} name='refresh' onPress={refresh}/>}
69
+ title = {<Label splitText numberOfLines={1} primary style={[{fontSize:15}]}>{title}</Label>}
70
+ titleProps = {{primary : true}}
71
+ description = {isLoading?<View style={[theme.styles.justifyContentFlexStart,theme.styles.alignItemsFlexStart]}>
72
+ <ActivityIndicator color={theme.colors.primary}/>
73
+ </View>:<CountUp
74
+ from={0}
75
+ to={count}
76
+ style = {{fontSize:20,color:theme.colors.secondaryOnSurface}}
77
+ />}
78
+ >
79
+
80
+ </Item>
81
+ }
82
+
83
+ /*** DBSTAT, prend en paramètre le nom de la bd ainsi que celui de la table et affiche en statistic,:
84
+ * Le nombre d'éléments crées en bases ainsi que ceux actifs
85
+ */
86
+ DatabaseStatisticContainer.propTypes = {
87
+ ...Item.propTypes,
88
+ /*** La méthode fetchCount doit retourner une promèsse qui lorsqu'elle est résolue, résoue le nombre d'éléments de la table de données en bd */
89
+ fetchCount : PropTypes.func,//la fonction permettant de counter les éléments de la table data
90
+ table : PropTypes.shape({
91
+ table : PropTypes.string,
92
+ tableName : PropTypes.string,
93
+ fetchCount : PropTypes.func,//la fonction permettant de counter les éléments de la table data
94
+ }).isRequired,
95
+ title : PropTypes.string, //le titre à afficher
96
+ }
@@ -0,0 +1,54 @@
1
+ import Screen from "$screen";
2
+ import Grid,{Cell} from "$components/Grid";
3
+ import {defaultStr,defaultNumber,defaultVal} from "$utils";
4
+ import React from "$react";
5
+ import DatabaseStatistic from "./DatabaseStatistic";
6
+ import theme from "$theme";
7
+ import PropTypes from "prop-types";
8
+
9
+ export const title = 'Statistiques en BD';
10
+ export default function DatabaseStatisticScreen ({withScreen,title:customTitle,contentProps,containerProps,tables,Component,...props}){
11
+ Component = React.isComponent(Component)? Component : Grid;
12
+ containerProps = defaultObj(containerProps);
13
+ const title = containerProps.title = defaultStr(containerProps.title,DatabaseStatisticScreen.title);
14
+ contentProps = defaultObj(contentProps);
15
+ if(Component == Cell){
16
+ containerProps.desktopSize = defaultNumber(containerProps.desktopSize,12);
17
+ containerProps.tabletSize = defaultNumber(containerProps.tabletSize,8);
18
+ containerProps.phoneSize = defaultNumber(containerProps.phoneSize,4);
19
+ }
20
+ let content = [];
21
+ Object.map(tables,(table,index,suffix)=>{
22
+ if(isObj(table)){
23
+ content.push(
24
+ <Cell elevation = {5} withSurface mobileSize={12} desktopSize={3} tabletSize={4} {...contentProps} key = {index} >
25
+ <DatabaseStatistic
26
+ icon = {table.icon}
27
+ key = {index}
28
+ table = {table}
29
+ index = {suffix}
30
+ title = {table.text|| table.title}
31
+ ></DatabaseStatistic>
32
+ </Cell>
33
+ )
34
+ }
35
+ });
36
+ if(!content.length) {
37
+ Auth.showError();
38
+ return null;
39
+ }
40
+ content = <Component {...containerProps} style={[containerProps.style,theme.styles.mr1,theme.styles.ml1]}>
41
+ {content}
42
+ </Component>;
43
+ return withScreen !== false ? <Screen withScrollView title={defaultVal(customTitle,title)} {...props}>{content}</Screen> : content;
44
+ }
45
+
46
+ export const screenName = DatabaseStatisticScreen.screenName = "DatabaseStatistics";
47
+ DatabaseStatisticScreen.title = title;
48
+
49
+ DatabaseStatisticScreen.propTypes = {
50
+ tables : PropTypes.oneOfType([
51
+ PropTypes.arrayOf(PropTypes.object),
52
+ PropTypes.objectOf(PropTypes.object)
53
+ ]).isRequired
54
+ }
@@ -0,0 +1,16 @@
1
+ import { readAsStringAsync } from "./utils";
2
+ import { loadAsset } from "../Assets/utils";
3
+
4
+
5
+ export * from "./utils";
6
+
7
+ export const readFileAsText = async (asset)=>{
8
+ return await new Promise((resolve,reject)=>{
9
+ loadAsset(asset).then((a)=>{
10
+ readAsStringAsync(a.localUri).then((data) => {
11
+ resolve(data);
12
+ }).catch(reject);
13
+ }).catch(reject);
14
+ })
15
+ }
16
+ export const readFile = readFileAsText;
@@ -1,20 +1,9 @@
1
- import { readAsStringAsync } from "./utils";
2
- import { loadAsset } from "../Assets/utils";
1
+ // Copyright 2022 @fto-consult/Boris Fouomene. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style
3
+ // license that can be found in the LICENSE file.
3
4
 
4
- export * from "./utils";
5
+ import * as FileSystem from "./exports";
5
6
 
6
- export const readFileAsText = async (asset)=>{
7
- return await new Promise((resolve,reject)=>{
8
- loadAsset(asset).then((a)=>{
9
- readAsStringAsync(a.localUri).then((data) => {
10
- resolve(data);
11
- }).catch(reject);
12
- }).catch(reject);
13
- })
14
- }
15
- export const readFile = readFileAsText;
7
+ export default FileSystem;
16
8
 
17
- export default {
18
- readFileAsText,
19
- readFile,
20
- }
9
+ export * from "./exports";
@@ -29,4 +29,6 @@ const readAs = async (asset,fn)=>{
29
29
  }
30
30
  export const readAsStringAsync = async (asset)=>{
31
31
  return readAs(asset);
32
- }
32
+ }
33
+
34
+ export * from "./write";
@@ -1,9 +1,12 @@
1
1
  // Copyright 2022 @fto-consult/Boris Fouomene. All rights reserved.
2
2
  // Use of this source code is governed by a BSD-style
3
3
  // license that can be found in the LICENSE file.
4
- import {defaultStr,isNonNullString,defaultNumber,defaultBool,dataURLToBase64,isBlob,isBase64,isDataURL} from "$utils";
4
+ import {defaultStr,isNonNullString,getFileName,getFileExtension,defaultNumber,defaultBool,dataURLToBase64,isBlob,isBase64,isDataURL} from "$utils";
5
5
  const FileSaver = require('file-saver');
6
6
  const mime = require('mime-types')
7
+ const XLSX = require("xlsx");
8
+ import Preloader from "$preloader";
9
+
7
10
  /**** sauvegarde un fichier sur le disque
8
11
  * @param {object} {
9
12
  * content {mix}: le contenu du fichier à enregistrer
@@ -16,9 +19,9 @@ const mime = require('mime-types')
16
19
  * isBinary : si c'est un fichier binaire
17
20
  * }
18
21
  */
19
- export const write = ({content,type,isBinary,timeout,delay,share,contentType,filename,path,directory,fileName})=>{
22
+ export const write = ({content,type,isBinary,timeout,delay,share,contentType,path,directory,fileName})=>{
20
23
  share = defaultBool(share,true);
21
- fileName = sanitizeFileName(defaultStr(fileName,filename));
24
+ fileName = sanitizeFileName(fileName);
22
25
  contentType = defaultStr(contentType) || mime.contentType(fileName) || mime.contentType(".txt");
23
26
  return new Promise((resolve,reject)=>{
24
27
  if(!isNonNullString(fileName)){
@@ -27,7 +30,7 @@ const mime = require('mime-types')
27
30
  }
28
31
  content = isBlob(content)? content : new Blob(content,contentType);
29
32
  try {
30
- FileSaver.saveAs(blob, fileName);
33
+ FileSaver.saveAs(content, fileName);
31
34
  setTimeout(() => {
32
35
  resolve({path:fileName,isWeb : true});
33
36
  }, defaultNumber(timeout,delay,3000));
@@ -39,4 +42,27 @@ const mime = require('mime-types')
39
42
 
40
43
  export const writeText = (args)=>{
41
44
  return write({...args,contentType:mime.contentType(".txt")});
45
+ }
46
+ /***
47
+ * @see https://ourtechroom.com/tech/mime-type-for-excel/ for excel mimesTypes
48
+ * .xls : application/vnd.ms-excel
49
+ */
50
+ export const writeExcel = ({workbook,content,contentType,fileName,...rest})=>{
51
+ if(!isNonNullString(contentType) || !contentType.contains("application/vnd.")){
52
+ //contentType = "application/vnd.ms-excel";
53
+ contentType : "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
54
+ }
55
+ let ext = defaultStr(getFileExtension(fileName,true),"xlsx");
56
+ fileName = sanitizeFileName(getFileName(fileName,true))+"."+ext;
57
+ if(!isNonNullString(fileName)){
58
+ return Promise.reject({status:false,message:'Nom de fichier invalide pour le contenu excel à créer'});
59
+ }
60
+ if(isBlob(content)){
61
+ return write({...rest,content,fileName,contentType})
62
+ }
63
+ Preloader.open("génération du fichier excel "+fileName);
64
+ XLSX.writeFile(workbook, fileName);
65
+ setTimeout(()=>{
66
+ Preloader.close();
67
+ },1000);
42
68
  }