@fto-consult/expo-ui 6.13.1 → 6.14.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": "6.13.1",
3
+ "version": "6.14.0",
4
4
  "description": "Bibliothèque de composants UI Expo,react-native",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -62,7 +62,7 @@
62
62
  "@emotion/native": "^11.11.0",
63
63
  "@expo/html-elements": "^0.5.1",
64
64
  "@expo/vector-icons": "^13.0.0",
65
- "@fto-consult/common": "^3.21.3",
65
+ "@fto-consult/common": "^3.22.0",
66
66
  "@gorhom/portal": "^1.0.14",
67
67
  "@pchmn/expo-material3-theme": "^1.3.1",
68
68
  "@react-native-async-storage/async-storage": "1.18.2",
@@ -47,9 +47,7 @@ const AutoSizerVerticalComponent = React.forwardRef(({onLayout,isScrollView,scre
47
47
  },500);
48
48
  }
49
49
  const dim = Dimensions.addEventListener("change",onResizePage);
50
- //APP.on(APP.EVENTS.RESIZE_PAGE,onResizePage);
51
50
  return ()=>{
52
- //APP.off(APP.EVENTS.RESIZE_PAGE,onResizePage);
53
51
  React.setRef(ref,null);
54
52
  dim?.remove();
55
53
  }
@@ -0,0 +1,41 @@
1
+ import React from "$react";
2
+ import {isNonNullString,defaultStr} from "$cutils";
3
+ import apiSession from "./session";
4
+ export const useSession = (sessionName)=>{
5
+ sessionName = defaultStr(sessionName);
6
+ const sessionRef = React.useRef({});
7
+ return React.useStableMemo(()=>{
8
+ if(sessionName){
9
+ return {
10
+ sessionName,
11
+ get name(){
12
+ return sessionName;
13
+ },
14
+ get : (a,b)=> {
15
+ return apiSession.get(sessionName,a,b);
16
+ },
17
+ set : (a,b)=>{
18
+ return apiSession.set(sessionName,a,b);
19
+ }
20
+ }
21
+ }
22
+ return {
23
+ sessionName,
24
+ get name(){
25
+ return sessionName;
26
+ },
27
+ get: key => {
28
+ if(isNonNullString(key)) return sessionRef.current[key];
29
+ return sessionRef.current;
30
+ },
31
+ set:(key,value)=>{
32
+ if(isObj(key)){
33
+ sessionRef.current = {...sessionRef.current,...key}
34
+ } else if(isNonNullString(key)) {
35
+ sessionRef.current[key] = value;
36
+ }
37
+ return sessionRef.current;
38
+ }
39
+ };
40
+ },[sessionName]);
41
+ }
@@ -10,12 +10,12 @@ import Dimensions,{isDesktopMedia,getCurrentMedia} from "$cplatform/dimensions";
10
10
  import {open,close} from "$epreloader";
11
11
  import {DRAWER_POSITIONS,DRAWER_TYPES,MINIMIZED_WIDTH,getDrawerWidth} from './utils';
12
12
  import Icon,{MENU_ICON} from "$ecomponents/Icon";
13
- import apiSession from "./session";
14
13
  import View from "$ecomponents/View";
15
14
  import {closeDrawer} from './DrawerItems/utils';
16
15
  import {DrawerContext} from "./Provider";
17
16
  import NavigationView from "./NavigationView";
18
17
  import { canBeMinimizedOrPermanent } from './utils';
18
+ import { useSession } from './hooks';
19
19
 
20
20
  export * from "./Provider";
21
21
 
@@ -23,6 +23,7 @@ export {DrawerItems};
23
23
  export {default as DrawerItem} from "./DrawerItems/DrawerItem";
24
24
 
25
25
  export * from "./utils";
26
+ export * from "./hooks";
26
27
 
27
28
  const canHandleExtra = x=> true;
28
29
 
@@ -33,40 +34,12 @@ const DrawerComponent = React.forwardRef((props,ref)=>{
33
34
  minimized,drawerItems,hideStatusBar,overlayColor, onDrawerMinimize,onDrawerToggleMinimize,
34
35
  onDrawerOpen,onDrawerClose,onDrawerToggle,header,headerProps,toggleIconProps,
35
36
  permanentToggleIcon,minimizedToggleIcon,temporaryToggleIcon,withMinimizedIcon,
36
- isItemActive,onPageResize,navigationViewRef,
37
- children,
38
- testID,
39
- drawerType} = props;
40
- sessionName = defaultStr(sessionName);
37
+ isItemActive,onPageResize,navigationViewRef,children,testID,drawerType} = props;
38
+
41
39
  testID = defaultStr(testID,"RN_DrawerComponent")
42
- const sessionRef = React.useRef({});
43
- const session = React.useMemo(()=>{
44
- if(sessionName){
45
- return {
46
- get : (a,b)=> {
47
- return apiSession.get(sessionName,a,b);
48
- },
49
- set : (a,b)=>{
50
- return apiSession.set(sessionName,a,b);
51
- }
52
- }
53
- }
54
- return {
55
- get: key => {
56
- if(isNonNullString(key)) return sessionRef.current[key];
57
- return sessionRef.current;
58
- },
59
- set:(key,value)=>{
60
- if(isObj(key)){
61
- sessionRef.current = {...sessionRef.current,...key}
62
- } else if(isNonNullString(key)) {
63
- sessionRef.current[key] = value;
64
- }
65
- return sessionRef.current;
66
- }
67
- };
68
- },[sessionName]);
69
- const sessionValue = session.get();
40
+ const session = useSession(sessionName);
41
+ sessionName = session.sessionName;
42
+ const sessionValue = session.get();
70
43
  const drawerRef = React.useRef(null);
71
44
  const onSlideCallbackRef = React.useRef(null);
72
45
  drawerItemsContainerProps = defaultObj(drawerItemsContainerProps);
@@ -247,7 +220,8 @@ const DrawerComponent = React.forwardRef((props,ref)=>{
247
220
  }
248
221
  }
249
222
  return (
250
- <DrawerContext.Provider value={{
223
+ <DrawerContext.Provider
224
+ value={{
251
225
  ...context,
252
226
  context,
253
227
  drawerRef,
@@ -109,7 +109,7 @@ export default class FormComponent extends React.AppComponent {
109
109
  return null;
110
110
  }
111
111
  rest = defaultObj(rest);
112
- const ComponentNode = defaultVal(Component,defaultComponentNode);
112
+ const ComponentNode = React.isComponent(Component) && Component || defaultComponentNode;
113
113
  testID = defaultStr(testID,"RN_FormComponent");
114
114
  return (<KeyboardAvoidingView testID={testID+"_KeyboardAvoidingView"}>
115
115
  <ComponentNode {...rest} testID={testID} style={[styles.container,rest.style]}>
@@ -0,0 +1,87 @@
1
+ import React from "$react";
2
+ import PropTypes from "prop-types";
3
+ import {defaultStr,isNonNullString,defaultObj,defaultBool} from "$cutils";
4
+ import getComponentFromType from "./componentsTypes";
5
+ import appConfig from "$capp/config";
6
+ import Colors from "$theme/colors";
7
+
8
+ const FieldContentComponent = React.forwardRef(({field,data,index,fields,formName,fieldProps,backgroundColor,primaryKey:customPrimaryKey,increment,fieldProp,archivable,disabled,windowWidth,responsive,responsiveProps,archived,...props},ref)=>{
9
+ if(field?.form === false) return null;
10
+ fieldProp = defaultObj(fieldProp);
11
+ const name = defaultStr(field.name,field.field,index);
12
+ const type = defaultStr(field.jsType,field.type,"text").trim().toLowerCase().replaceAll(" ","").replaceAll("-","").replaceAll("_","");
13
+ const jsType = defaultStr(field.jsType,field.type,"text");
14
+ const isDate = (type.contains('date') || type.contains('time'));
15
+ let {defaultValue,renderfilter,render_filter,useDefaultValueFromData,primaryKey,hidden,renderFormDataField,getMediaQueryStyle,printLabels,queryLimit,selected,value,dataFilesInterest,perm,ignore,form,responsiveProps:customResponsiveProps,...rest} = field;
16
+ delete rest.import;
17
+ delete rest.export;
18
+ const isFilter = renderfilter || render_filter || false;
19
+ if(customPrimaryKey === true){
20
+ primaryKey = true;
21
+ }
22
+ if(form === false || ignore || (isNonNullString(perm) && !Auth.isAllowedFromStr(perm))){
23
+ return null;
24
+ }
25
+ if(rest.nullable === false){
26
+ rest.required = true;
27
+ }
28
+ if(primaryKey === true && typeof rest.required !=='boolean'){
29
+ rest.required = true;
30
+ }
31
+ if(typeof rest.filter !=='function'){
32
+ delete rest.filter;
33
+ }
34
+ if(name){
35
+ rest.defaultValue = useDefaultValueFromData === false ? defaultValue : (name in data && data[name] !== undefined && data[name] !== null? data[name]: defaultValue);
36
+ if((type == 'selecttabledata' || type == 'datafile')){
37
+ rest._defaultValue = data[rest.name];
38
+ }
39
+ } else {
40
+ rest.defaultValue = defaultValue;
41
+ }
42
+ if(rest.defaultValue === null){
43
+ rest.defaultValue = undefined;
44
+ }
45
+ // les champs de type date par défaut qui sont requis, auront comme valeur par défaut la date actuelle s'il ne sont pas définies
46
+ if(rest.autoSetDefaultValue !== false && (!rest.defaultValue && typeof rest.defaultValue !=='boolean' && typeof rest.defaultValue !=='number')){
47
+ if(isDate && rest.required === true ){
48
+ rest.defaultValue = new Date();
49
+ } else if(!isDate && isNonNullString(rest.appConfigDefaultValueKey)){
50
+ rest.defaultValue = appConfig.get(rest.appConfigDefaultValueKey);
51
+ }
52
+ }
53
+ return React.useMemo(()=>{
54
+ customResponsiveProps = defaultObj(customResponsiveProps);
55
+ const Component = getComponentFromType(jsType);
56
+ return <Component
57
+ {...props}
58
+ data = {data}
59
+ windowWidth = {windowWidth}
60
+ index = {index}
61
+ disabled = {disabled || archived}
62
+ archived = {archived}
63
+ {...fieldProps}
64
+ {...rest}
65
+ style = {[fieldProps.style,Colors.isValid(backgroundColor) && {backgroundColor},rest.style]}
66
+ formName = {formName}
67
+ key={index}
68
+ type = {type}
69
+ jsType = {jsType}
70
+ archivable = {archivable}
71
+ name = {name}
72
+ responsive = {responsive}
73
+ responsiveProps = {{...responsiveProps,...customResponsiveProps,style:[responsiveProps.style,customResponsiveProps.style]}}
74
+ />
75
+ },[rest.defaultValue,type,jsType,defaultValue,rest,windowWidth,backgroundColor,isFilter,name,!!responsive]);
76
+ });
77
+
78
+ FieldContentComponent.displayName = "FieldContentComponent";
79
+
80
+ export default FieldContentComponent;
81
+
82
+ FieldContentComponent.propTypes = {
83
+ field : PropTypes.object.isRequired,
84
+ data : PropTypes.object,
85
+ formName : PropTypes.string.isRequired,
86
+ responsiveProps : PropTypes.object,
87
+ }
@@ -1,81 +1,48 @@
1
1
  import React from "$react";
2
2
  import Divider from "$ecomponents/Divider";
3
3
  import {isObj,isNonNullString,defaultStr,defaultObj} from "$utils";
4
- import getComponentFromType from "./componentsTypes";
5
- import appConfig from "$capp/config";
6
4
  import {flattenStyle,Colors} from "$theme";
5
+ import FieldContent from "./FieldContent";
6
+ import stableHash from "stable-hash";
7
7
 
8
8
  export default function FieldsContent({fields,formName,disabled,archived,archivable,fieldProps,primaryKeyFields,responsiveProps,responsive,style,data,windowWidth}){
9
9
  windowWidth = typeof windowWidth ==='number' && windowWidth > 100?windowWidth : undefined;
10
- return React.useStableMemo(()=>{
10
+ primaryKeyFields = isObj(primaryKeyFields) ? primaryKeyFields : Array.isArray(primaryKeyFields) ? primaryKeyFields: [];
11
+ const isPArray = Array.isArray(primaryKeyFields);
12
+ const backgroundColor = flattenStyle(style)?.backgroundColor;
13
+ const prevResponsive = React.usePrevious(responsive);
14
+ return React.useMemo(()=>{
11
15
  const content = [];
12
- style = defaultObj(flattenStyle(style));
13
- primaryKeyFields = Array.isArray(primaryKeyFields) ? primaryKeyFields: [];
14
16
  fieldProps = defaultObj(fieldProps);
15
- Object.map(fields,(field,index,i)=>{//on ignore tous les champs supposés être à ignorer
17
+ console.log("rendering fields contens alla ",fieldProps,fields,data,responsive,prevResponsive);
18
+ //console.log("rendering form data ",formName,archived,backgroundColor,primaryKeyFields,responsive,responsiveProps)
19
+ Object.map(fields,(field,index,i)=>{
16
20
  if(field === 'divider'){
17
21
  content.push(<Divider key = {index} style={theme.styles.w100}/>)
18
- } else if(isObj(field) && field.form !== false) {
19
- const name = defaultStr(field.name,field.field,index);
20
- const type = defaultStr(field.jsType,field.type,"text").trim().toLowerCase().replaceAll(" ","").replaceAll("-","").replaceAll("_","");
21
- const isDate = (type.contains('date') || type.contains('time'));
22
- const Component = getComponentFromType(defaultStr(field.jsType,field.type,"text"));
23
- let {defaultValue,useDefaultValueFromData,primaryKey,hidden,renderFormDataField,getMediaQueryStyle,printLabels,queryLimit,selected,value,dataFilesInterest,perm,ignore,form,responsiveProps:customResponsiveProps,...rest} = field;
24
- delete rest.import;
25
- delete rest.export;
26
- if(primaryKey === true && name && !field.filter){
27
- primaryKeyFields[name] = field;
28
- }
29
- if(form === false || ignore || (isNonNullString(perm) && !Auth.isAllowedFromStr(perm))){
30
- return null;
31
- }
32
- if(rest.nullable === false){
33
- rest.required = true;
34
- }
35
- if(primaryKey === true && typeof rest.required !=='boolean'){
36
- rest.required = true;
37
- }
38
- if(typeof rest.filter !=='function'){
39
- delete rest.filter;
40
- }
41
- if(name){
42
- rest.defaultValue = useDefaultValueFromData === false ? defaultValue : (name in data && data[name] !== undefined && data[name] !== null? data[name]: defaultValue);
43
- if((type == 'selecttabledata' || type == 'datafile')){
44
- rest._defaultValue = data[rest.name];
45
- }
46
- } else {
47
- rest.defaultValue = defaultValue;
48
- }
49
- if(rest.defaultValue === null){
50
- rest.defaultValue = undefined;
51
- }
52
- // les champs de type date par défaut qui sont requis, auront comme valeur par défaut la date actuelle s'il ne sont pas définies
53
- if(rest.autoSetDefaultValue !== false && (!rest.defaultValue && typeof rest.defaultValue !=='boolean' && typeof rest.defaultValue !=='number')){
54
- if(isDate && rest.required === true ){
55
- rest.defaultValue = new Date();
56
- } else if(!isDate && isNonNullString(rest.appConfigDefaultValueKey)){
57
- rest.defaultValue = appConfig.get(rest.appConfigDefaultValueKey);
58
- }
59
- }
60
- customResponsiveProps = defaultObj(customResponsiveProps);
61
- content.push(<Component
62
- data = {data}
63
- windowWidth = {windowWidth}
64
- index = {index}
65
- disabled = {disabled || archived}
66
- archived = {archived}
67
- {...fieldProps}
68
- {...rest}
69
- style = {[fieldProps.style,Colors.isValid(style.backgroundColor) && {backgroundColor:style.backgroundColor},rest.style]}
70
- formName = {formName}
71
- key={index}
72
- archivable = {archivable}
73
- name = {name}
74
- responsive = {responsive}
75
- responsiveProps = {{...responsiveProps,...customResponsiveProps,style:[responsiveProps.style,customResponsiveProps.style]}}
76
- />);
22
+ return;
77
23
  }
24
+ //on ignore tous les champs supposés être à ignorer
25
+ if(!isObj(field) || field.form === false) return null;
26
+ const name = defaultStr(field?.name,field?.field,i);
27
+ content.push(<FieldContent
28
+ field = {field}
29
+ index = {index}
30
+ increment = {i}
31
+ archivable={archivable}
32
+ disabled = {disabled}
33
+ archived = {archived}
34
+ backgroundColor = {backgroundColor}
35
+ windowWidth = {windowWidth}
36
+ responsive = {responsive}
37
+ responsiveProps = {responsiveProps}
38
+ key = {defaultStr(field.name,field.field,index)|| index}
39
+ formName = {formName}
40
+ fields = {fields}
41
+ primaryKey = {isPArray && primaryKeyFields.includes(name) || primaryKeyFields[name] || (name in primaryKeyFields)}
42
+ fieldProps = {fieldProps}
43
+ data = {data}
44
+ />)
78
45
  });
79
46
  return content;
80
- },[formName,archived,archivable,disabled,fields,data,windowWidth,responsive,responsiveProps])
47
+ },[formName,archived,backgroundColor,archivable,primaryKeyFields,isPArray,disabled,fields,responsiveProps,responsive])
81
48
  }
@@ -7,7 +7,7 @@ import {Vertical as AutoSizeVertical} from "$ecomponents/AutoSizer";
7
7
  const ScrollViewComponent = React.forwardRef(({withAutoSizer,autoSizerProps,testID,...rest},ref) => {
8
8
  testID = defaultStr(testID,'RN_ScrollViewComponent');
9
9
  const autoSize = React.useRef(withAutoSizer).current;
10
- if(!autoSize || rest.horizontal === true || rest.vertical === false){
10
+ if(!autoSize || rest.horizontal === true || rest.vertical === false){
11
11
  return <ScrollView testID={testID} ref={ref} {...rest}/>
12
12
  }
13
13
  autoSizerProps = defaultObj(autoSizerProps);
@@ -79,7 +79,7 @@ const TabComponent = React.forwardRef((props,ref)=>{
79
79
  })
80
80
  return {tabs,contents,childrenProps}
81
81
  },[children]);
82
- return <View {...rest} testID={testID} style={[styles.container,tabItemsProps.style]}>
82
+ return <View {...rest} testID={testID} style={[styles.container,tabItemsProps.style,rest.style]}>
83
83
  <TabItems testID={testID+"_TabItems"} {...tabItemsProps} activeIndex={index} style={[styles.tab,rest.style]} onChange={setActiveIndex}>
84
84
  {tabs}
85
85
  </TabItems>
@@ -399,7 +399,7 @@ export default class TableDataScreenComponent extends FormDataScreen{
399
399
  contentProps.style = [contentProps.style,styles.noMarging,styles.noPadding,styles.content]
400
400
  mainContent = ct;
401
401
  } else {
402
- mainContent = <View {...contentProps} testID={testID+"_ContentContainer"} style={[styles.container,styles.noPadding]}>
402
+ mainContent = <View {...contentProps} testID={testID+"_ContentContainer"} style={[styles.container,styles.noPadding,contentProps.style]}>
403
403
  <ScrollView withAutoSizer testID={testID+"_MainContentScrollView"} contentProps={{style:theme.styles.p1}}>
404
404
  <Surface elevation={elevation} testID={testID+"_ContentHeader"} style={[styles.screenContent,theme.styles.p1,header?styles.screenContentWithHeader:null]}>
405
405
  {header}
@@ -412,14 +412,14 @@ export default class TableDataScreenComponent extends FormDataScreen{
412
412
  </View>
413
413
  }
414
414
  } else {
415
- mainContent = <Surface {...contentProps} testID={testID+"_MainContentContainer"} elevation={elevation} style={[styles.container,styles.noPadding,{paddingTop:0,marginTop:0}]}>
415
+ mainContent = <View {...contentProps} testID={testID+"_MainContentContainer"} elevation={elevation} style={[styles.container,{backgroundColor:theme.colors[theme.isDark()?"background":"surface"]},styles.noPadding,{paddingTop:0,marginTop:0},contentProps.style]}>
416
416
  <ScrollView withAutoSizer testID={testID+"_MainContentScrollViewWithoutTab"}>
417
417
  <View testID={testID+"_MainContent"} style={[styles.screenContent,!isMobOrTab && theme.styles.p1,header?styles.screenContentWithHeader:null]}>
418
418
  {header}
419
419
  {content}
420
420
  </View>
421
421
  </ScrollView>
422
- </Surface>
422
+ </View>
423
423
  }
424
424
  const appBarProps = this.getAppBarActionsProps(restProps);
425
425
  if(hasTabs && isMobOrTab){
@@ -8,14 +8,12 @@ import Login from "$eauth/Login";
8
8
  import {navigate} from "$cnavigation";
9
9
  import theme from "$theme";
10
10
  import Logo from "$ecomponents/Logo";
11
- import Dimensions from "$dimensions";
12
11
 
13
12
  const DrawerNavigator = React.forwardRef(({content,children:customChildren,state,...props},ref)=>{
14
13
  const drawerRef = React.useRef(null);
15
14
  const mergedRefs = React.useMergeRefs(drawerRef,ref);
16
15
  const forceRender = React.useForceRender();
17
16
  const refreshItemsRef = React.useRef(false);
18
- const [currentMedia,setCurrentMedia] = React.useState(Dimensions.getCurrentMedia());
19
17
  const items = useGetItems({refresh:()=>{
20
18
  if(drawerRef.current && drawerRef.current && drawerRef.current.forceRenderNavigationView){
21
19
  return drawerRef.current.forceRenderNavigationView();
@@ -31,23 +29,13 @@ const DrawerNavigator = React.forwardRef(({content,children:customChildren,state
31
29
  forceRender();
32
30
  refreshItemsRef.current = false;
33
31
  };
34
- const onResizePage = ()=>{
35
- //forceRender();
36
- }
37
32
  APP.on(APP.EVENTS.AUTH_LOGOUT_USER,onLogoutUser);
38
- const bindResize = Dimensions.addEventListener("change",()=>{
39
- const cMedia = Dimensions.getCurrentMedia();
40
- if(cMedia !== currentMedia){
41
- setCurrentMedia(cMedia);
42
- }
43
- })
44
33
  APP.on(APP.EVENTS.UPDATE_THEME,refreshItems);
45
34
  return ()=>{
46
35
  APP.off(APP.EVENTS.AUTH_LOGOUT_USER,onLogoutUser);
47
36
  APP.off(APP.EVENTS.UPDATE_THEME,refreshItems);
48
- if(typeof bindResize ==='function') bindResize();
49
37
  }
50
- },[])
38
+ },[]);
51
39
  const headerCB = ({isMinimized})=>{
52
40
  if(isMinimized) return null;
53
41
  if(!theme.showProfilAvatarOnDrawer){
@@ -29,12 +29,11 @@ const useGetItems = (options)=>{
29
29
  APP.on(APP.EVENTS.REFRESH_MAIN_DRAWER,refreshItems);
30
30
  APP.on(APP.EVENTS.AUTH_LOGIN_USER,refreshItems);
31
31
  APP.on(APP.EVENTS.AUTH_LOGOUT_USER,refreshItems);
32
- //APP.on(APP.EVENTS.UPDATE_THEME,refreshItems);
33
32
  return ()=>{
34
33
  APP.off(APP.EVENTS.REFRESH_MAIN_DRAWER,refreshItems);
35
34
  APP.off(APP.EVENTS.AUTH_LOGIN_USER,refreshItems);
36
35
  APP.off(APP.EVENTS.AUTH_LOGOUT_USER,refreshItems);
37
- //APP.off(APP.EVENTS.UPDATE_THEME,onLogoutUser);
36
+ APP.off(APP.EVENTS.UPDATE_THEME,onLogoutUser);
38
37
  }
39
38
  },[])
40
39
  return useMemo(()=>{