@fto-consult/expo-ui 2.23.10 → 2.24.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.23.10",
3
+ "version": "2.24.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.23.10",
64
+ "@fto-consult/common": "^1.24.6",
65
65
  "@fto-consult/expo-ui": "^2.21.0",
66
66
  "@gorhom/portal": "^1.0.14",
67
67
  "@react-native-async-storage/async-storage": "~1.17.3",
@@ -1245,6 +1245,7 @@ export default class CommonDatagridComponent extends AppComponent {
1245
1245
  m.unshift({
1246
1246
  text : "Fonctions d'aggrégation",
1247
1247
  icon : "material-functions",
1248
+ style : [{fontWeight:'bold'}],
1248
1249
  //divider : true,
1249
1250
  });
1250
1251
  if(withDivider !== false){
@@ -1260,8 +1261,8 @@ export default class CommonDatagridComponent extends AppComponent {
1260
1261
  if(!isValidAggregator(ag) || ag.code == this.state.aggregatorFunction) return null;
1261
1262
  setTimeout(()=>{
1262
1263
  this.setIsLoading(true,()=>{
1263
- this.prepareData({data:this.INITIAL_STATE.data},(state)=>{
1264
- this.setState({...state,aggregatorFunction:ag.code},()=>{
1264
+ this.prepareData({data:this.INITIAL_STATE.data,aggregatorFunction:ag.code},(state)=>{
1265
+ this.setState(state,()=>{
1265
1266
  this.setSessionData("aggregatorFunction",ag.code);
1266
1267
  this.setIsLoading(false,false);
1267
1268
  })
@@ -2017,8 +2018,9 @@ export default class CommonDatagridComponent extends AppComponent {
2017
2018
  return defaultNumber(this.sectionListDataSize.current)
2018
2019
  }
2019
2020
  prepareData(args,cb){
2020
- let {pagination,displayOnlySectionListHeaders:cdisplayOnlySectionListHeaders,data,force,sectionListColumns,updateFooters} = defaultObj(args);
2021
+ let {pagination,aggregatorFunction:customAggregatorFunction,displayOnlySectionListHeaders:cdisplayOnlySectionListHeaders,data,force,sectionListColumns,updateFooters} = defaultObj(args);
2021
2022
  cb = typeof cb ==='function'? cb : typeof args.cb == 'function'? args.cb : undefined;
2023
+ const aggregatorFunction = isNonNullString(customAggregatorFunction) && customAggregatorFunction in this.aggregatorFunctions ? this.aggregatorFunctions[customAggregatorFunction] : this.getActiveAggregatorFunction();
2022
2024
  sectionListColumns = isObj(sectionListColumns) ? sectionListColumns : this.state.sectionListColumns;
2023
2025
  const displayOnlySectionListHeaders = typeof cdisplayOnlySectionListHeaders == 'boolean'?cdisplayOnlySectionListHeaders : this.state.displayOnlySectionListHeaders;
2024
2026
  let isArr = Array.isArray(data);
@@ -2095,7 +2097,7 @@ export default class CommonDatagridComponent extends AppComponent {
2095
2097
  result.push(currentSectionListFooter);
2096
2098
  }
2097
2099
  Object.map(footersColumns,(columnDef,field)=>{
2098
- evalSingleValue({data:d,aggregatorFunction:this.getActiveAggregatorFunction(),aggregatorFunctions:this.aggregatorFunctions,columnDef,field,result,displayLabel:false})
2100
+ evalSingleValue({data:d,aggregatorFunction,aggregatorFunctions:this.aggregatorFunctions,columnDef,field,result,displayLabel:false})
2099
2101
  });
2100
2102
  }
2101
2103
  newData.push(d);
@@ -2126,7 +2128,7 @@ export default class CommonDatagridComponent extends AppComponent {
2126
2128
  } else if(force){
2127
2129
  this.setSelectedRows();
2128
2130
  }
2129
- const state = {data,displayOnlySectionListHeaders};
2131
+ const state = {data,displayOnlySectionListHeaders,aggregatorFunction:aggregatorFunction.code};
2130
2132
  if((cb)){
2131
2133
  cb(state);
2132
2134
  }
@@ -2262,9 +2264,10 @@ export default class CommonDatagridComponent extends AppComponent {
2262
2264
  if(typeof props !=='object' || !props){
2263
2265
  props = {};
2264
2266
  }
2265
- const children = React.isValidElement(this.props.progressBar) ? this.props.progressBar :
2267
+ const ProgressBar = this.props.progressBar;
2268
+ const children = React.isValidElement(ProgressBar) ? ProgressBar :
2266
2269
  this.props.useLinesProgressBar === true || this.props.useLineProgressBar === true ? CommonDatagridComponent.LineProgressBar(props)
2267
- : this.getDefaultPreloader(props);
2270
+ : React.isComponent(ProgressBar) ?<ProgressBar/> : this.getDefaultPreloader(props);
2268
2271
  return <DatagridProgressBar
2269
2272
  {...props}
2270
2273
  onChange = {(context)=>{
@@ -3171,6 +3174,7 @@ CommonDatagridComponent.propTypes = {
3171
3174
  aggregatorFunction : PropTypes.string,
3172
3175
  /*** permet de faire une mutation sur les options de la recherche, immédiatement avant le lancement de la recherche */
3173
3176
  fetchOptionsMutator : PropTypes.func,
3177
+ useLinesProgressBar : PropTypes.bool,//si le progress bar lignes horizontale seront utilisés
3174
3178
  }
3175
3179
 
3176
3180
  const styles = StyleSheet.create({
@@ -284,14 +284,13 @@ const DatagridFactory = (Factory)=>{
284
284
  ,icon : showFooters?'view-column':'view-module'
285
285
  ,text : (showFooters?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux')
286
286
  } : null,
287
- ...this.getAggregatorFunctionsMenuItems(),
288
287
  ...(selectableMultiple ? restItems : [])
289
288
  ] : visibleColumns}
290
289
 
291
290
  />
292
291
  {this.renderSectionListMenu()}
293
292
  {this.renderDisplayTypes()}
294
- {!isMobile && this.renderAggregatorFunctionsMenu() || null}
293
+ {this.renderAggregatorFunctionsMenu()}
295
294
  <View testID={testID+"_HeaderPagination"} style = {styles.paginationItem}>
296
295
  <BottomSheetMenu
297
296
  testID={testID+"_HeaderMenus"}
@@ -6,13 +6,12 @@ import React from "$react";
6
6
  import {defaultObj,isNonNullString} from "$utils";
7
7
  import SimpleSelect from "$ecomponents/SimpleSelect";
8
8
  import Provider from "$ecomponents/Dialog/Provider";
9
- import TextField from "$components/TextField";
10
- import Label from "$components/Label";
9
+ import TextField from "$ecomponents/TextField";
10
+ import Label from "$ecomponents/Label";
11
11
  import theme from "$theme";
12
12
  import notify from "$notify";
13
13
  import { View } from "react-native";
14
- import Icon from "$components/Icon";
15
- import PropTypes from "prop-types";
14
+ import Icon from "$ecomponents/Icon";
16
15
 
17
16
  const DateFormatSelector = React.forwardRef((props,ref)=>{
18
17
  return <SimpleSelect ref={ref} {...selectDateFormatFieldProps(props)}/>
@@ -797,7 +797,7 @@ export default class Field extends AppComponent {
797
797
  jsType,
798
798
  fieldToPopulateOnBlur,
799
799
  ...rest
800
- } = this.props;
800
+ } = this.getComponentProps(this.props);
801
801
  if(this.state.caughtAnError){
802
802
  return <ErrorMessage
803
803
  error={this.state.caughtError}
@@ -0,0 +1,39 @@
1
+ import SelectField from "./SelectField";
2
+ import {selectCurrencyFieldProps} from "$ecomponents/SelectCurrency";
3
+ import appConfig from "$app/config";
4
+ import {defaultStr} from "$utils";
5
+
6
+ export default class FormFieldSelectCurrency extends SelectField{
7
+ constructor(props){
8
+ super(props);
9
+ Object.defineProperties(this,{
10
+ currencyFormatRef : {value : {current:defaultStr(appConfig.currencyFormat)}}
11
+ });
12
+ }
13
+ onValidate(args){
14
+ const {context} = args;
15
+ if(context && context.getField){
16
+ const field = context.getField("currencyFormat");
17
+ if(field && field.setValue){
18
+ field.setValue(defaultStr(this.currencyFormatRef.current));
19
+ }
20
+ }
21
+ super.onValidate(args);
22
+ }
23
+ getComponentProps(props){
24
+ this.currencyFormatRef.current = defaultStr(props.currencyFormat,this.currencyFormatRef.current,appConfig.currencyFormat)
25
+ return {...selectCurrencyFieldProps({...props,onChange:(args)=>{
26
+ this.currencyFormatRef.current = defaultStr(args.currencyFormat);
27
+ if(this.props.onChange){
28
+ this.props.onChange(args);
29
+ }
30
+ }})}
31
+ }
32
+ getValidValue(data){
33
+ data.currencyFormat = this.currencyFormatRef.current;
34
+ return super.getValidValue(data);
35
+ }
36
+ }
37
+ FormFieldSelectCurrency.propTypes = {
38
+ ...SelectField.propTypes,
39
+ }
@@ -130,7 +130,9 @@ export default class FormSelectField extends Field{
130
130
  }}
131
131
  onChange = {(args)=>{
132
132
  this.validateWithCallOnChange(args);
133
- if(typeof this.props.onChange =='function'){
133
+ if(typeof props.onChange =='function'){
134
+ props.onChange(args);
135
+ } else if(this.props.onChange =='function'){
134
136
  this.props.onChange(args);
135
137
  }
136
138
  }}
@@ -4,6 +4,7 @@ import SelectField from "./SelectField";
4
4
  import Switch from "./Switch";
5
5
  import Checkbox from "./Checkbox";
6
6
  import SelectTableData from "./SelectTableData";
7
+ import SelectCurrency from "./SelectCurrency";
7
8
  //import IdField from "./IdField";
8
9
  //import PieceField from "./PieceField";
9
10
  import Slider from "./Slider";
@@ -27,7 +28,8 @@ const defFormFields = {
27
28
  TextField,
28
29
  SelectField,
29
30
  SelectTableData,
30
- SelectCountry
31
+ SelectCountry,
32
+ SelectCurrency
31
33
  ,Switch
32
34
  ,Checkbox
33
35
  //,IdField
@@ -58,6 +60,7 @@ export {
58
60
  SelectField,
59
61
  SelectTableData,
60
62
  SelectCountry
63
+ ,SelectCurrency
61
64
  ,Switch
62
65
  ,Checkbox
63
66
  //,IdField
@@ -8,7 +8,6 @@ const componentTypes = {
8
8
  ...Fields,
9
9
  id : Fields.IdField,
10
10
  selecttabledata : Fields.SelectTableData,
11
- select_tabledata : Fields.SelectTableData,
12
11
  idfield : Fields.IdField,
13
12
  piecefield : Fields.PieceField,
14
13
  piece : Fields.PieceField,
@@ -16,6 +15,7 @@ const componentTypes = {
16
15
  switch : Fields.Switch,
17
16
  selectcountry : Fields.SelectCountry,
18
17
  selectdateformat : Fields.SelectDateFormat,
18
+ selectcurrency : Fields.SelectCurrency,
19
19
  dateformat : Fields.SelectDateFormat,
20
20
  date : Fields.Date,
21
21
  time : Fields.Time,
@@ -100,9 +100,11 @@ export const getFilterComponentProps = (_props)=>{
100
100
  component = Fields.ColorPicker;
101
101
  } else if(type == 'dateformat' || type =='select_dateformat' || type =='select_date_format') {
102
102
  component = Fields.SelectDateFormat;
103
- } else if(React.isComponent()) {
103
+ } else if(React.isComponent(componentTypes[type])) {
104
104
  component = componentTypes[type];
105
- }else {
105
+ } else if(React.isComponent(componentTypes[type.replaceAll("_","")])){
106
+ component = componentTypes[type.replaceAll("_","")];
107
+ } else {
106
108
  const tt = type.replaceAll("_","").toLowerCase();
107
109
  if(React.isComponent(componentTypes[tt])){
108
110
  component = componentTypes[tt];
@@ -14,6 +14,7 @@ const OptimizedHeavyScreen = React.forwardRef(({
14
14
  ),
15
15
  style,
16
16
  children,
17
+ isLoading,
17
18
  timeout,
18
19
  testID,
19
20
  transitionTimeout,
@@ -30,7 +31,7 @@ const OptimizedHeavyScreen = React.forwardRef(({
30
31
  style={[{flex:1},style]}
31
32
  ref={React.useMergeRefs(transitionRef,ref)}
32
33
  >
33
- {areInteractionsComplete ? (children) : placeholder}
34
+ {areInteractionsComplete && isLoading !==true ? (children) : placeholder}
34
35
  </Transitioning.View>
35
36
  )
36
37
  });
@@ -46,7 +47,8 @@ OptimizedHeavyScreen.propTypes = {
46
47
  placeholder : PropTypes.oneOfType([
47
48
  PropTypes.node,
48
49
  PropTypes.element,
49
- ])
50
+ ]),
51
+ isLoading : PropTypes.bool,//si l'écan où les données sont en train d'être chargé
50
52
  }
51
53
 
52
54
  OptimizedHeavyScreen.displayName = "OptimizedHeavyScreenComponent";
@@ -0,0 +1,100 @@
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.
4
+ import React from "$react";
5
+ import {defaultObj,defaultStr,isNonNullString} from "$utils";
6
+ import SimpleSelect from "$ecomponents/SimpleSelect";
7
+ import TextField from "$ecomponents/TextField";
8
+ import Icon from "$ecomponents/Icon";
9
+ import {currencies} from "$ccurrency";
10
+ import { Pressable } from "react-native";
11
+ import {styles} from "$theme";
12
+ import appConfig from "$app/config";
13
+
14
+ const CurrencySelector = React.forwardRef((props,ref)=>{
15
+ return <SimpleSelect ref={ref} {...selectCurrencyFieldProps(props)}/>
16
+ });
17
+
18
+ CurrencySelector.displayName = "CurrencySelector";
19
+
20
+ export default CurrencySelector;
21
+
22
+ export const currencyFormatRef = React.createRef(null);
23
+
24
+ /*** onAdd est appelé lorsqu'on ajoute un format personalisé */
25
+ export const selectCurrencyFieldProps = ({right,disabled,readOnly,isFilter,onChange,onBlur,editable,...props})=>{
26
+ const isEditable = disabled !== true && readOnly !== true && editable !== false;
27
+ const currency = appConfig.currency;
28
+ currencyFormatRef.current = defaultStr(currencyFormatRef.current,appConfig.currencyFormat,"%v%s");
29
+ const iconSize = 25;
30
+ return {
31
+ items : currencies,
32
+ getItemValue : ({item})=>item.code,
33
+ renderItem : ({item}) => "["+item.code+"] " +item.name,
34
+ showAdd : false,
35
+ defaultValue : defaultStr(currency.code),
36
+ enableCopy : false,
37
+ ...props,
38
+ isFilter,
39
+ onChange : (args)=>{
40
+ args.currencyFormat = args.format = defaultStr(currencyFormatRef.current)
41
+ if(onChange){
42
+ onChange(args);
43
+ }
44
+ },
45
+ onBlur : (args)=>{
46
+ args.currencyFormat = args.format = defaultStr(currencyFormatRef.current);
47
+ if(onBlur){
48
+ onBlur(args);
49
+ }
50
+ },
51
+ disabled : !isEditable,
52
+ editable : isEditable,
53
+ right : !isEditable || isFilter ? right : (rP)=>{
54
+ const r = typeof right =='function'? right(rP) : right;
55
+ return <>
56
+ <Pressable style={{width:80}}
57
+ onPress = {(e)=>{
58
+ React.stopEventPropagation(e);
59
+ }}
60
+ >
61
+ <TextField
62
+ {...rP}
63
+ affix = {false}
64
+ editable = {isEditable}
65
+ disabled = {disabled}
66
+ readOnly = {readOnly}
67
+ enableCopy = {false}
68
+ contentContainerProps = {{
69
+ style:{height:37,paddingVertical:0,paddingHorizontal:0},
70
+ }}
71
+ mode = {'flat'}
72
+ left = {(p)=>{
73
+ return <Icon
74
+ {...rP}
75
+ {...p}
76
+ size = {iconSize}
77
+ style = {[styles.ml0,styles.mr0,{width:iconSize,height:iconSize}]}
78
+ icon = "alpha-f-box"
79
+ title="Format d'affichage des valeurs numériques : de la forme %v%s ou %v représente la valeur du montant et %s représente la devise : exemple %s%v => $10 et %s %v => 10 $"
80
+ />
81
+ }}
82
+ defaultValue = {currencyFormatRef.current}
83
+ containerProps = {{
84
+ width:70,
85
+ }}
86
+ onChange = {({value})=>{
87
+ currencyFormatRef.current = value;
88
+ }}
89
+ />
90
+ </Pressable>
91
+ {React.isValidElement(r) && r || null}
92
+ </>
93
+ },
94
+ type : 'select',
95
+ }
96
+ }
97
+
98
+ CurrencySelector.propTypes = {
99
+ ...SimpleSelect.propTypes,
100
+ }
@@ -19,7 +19,7 @@ export default function DatabaseStatisticScreen ({withScreen,title:customTitle,c
19
19
  }
20
20
  let content = [];
21
21
  Object.map(tables,(table,index,suffix)=>{
22
- if(isObj(table) && table.databaseStatistic !== false){
22
+ if(isObj(table) && table.databaseStatistic !== false && table.databaseStatistics !== false){
23
23
  content.push(
24
24
  <Cell elevation = {5} withSurface mobileSize={12} desktopSize={3} tabletSize={4} {...contentProps} key = {index} >
25
25
  <DatabaseStatistic
@@ -68,13 +68,14 @@ export default class FormDataLayout extends FormDataActions {
68
68
  preloader,
69
69
  preloaderProps,
70
70
  testID,
71
+ isLoading,
71
72
  } = this.props;
72
73
  wProps = defaultObj(wProps);
73
- const useHeavyScreen = withHeavyScreen !== false ? true : false;
74
+ const useHeavyScreen = withHeavyScreen !== false || isLoading == true? true : false;
74
75
  const Wrapper = useHeavyScreen ? HeavyScreen : React.Fragment;
75
76
  testID = defaultStr(wProps.testID,testID,"RN_FormDataScren");
76
77
  const placeholder = React.isValidElement(preloader)? preloader : <FormLoader {...defaultObj(preloaderProps)}/>;
77
- const wrapperProps = useHeavyScreen ? {...wProps,placeholder,testID:testID+"_HeavyScreen"} : {}
78
+ const wrapperProps = useHeavyScreen ? {...wProps,isLoading,placeholder,testID:testID+"_HeavyScreen"} : {}
78
79
  return <Wrapper {...wrapperProps}>
79
80
  {content}
80
81
  </Wrapper>
@@ -94,6 +95,7 @@ FormDataLayout.propTypes = {
94
95
  withHeavyScreen : PropTypes.bool,
95
96
  withAppBar : PropTypes.bool, ///si l'appBar sera affiché
96
97
  preloader : PropTypes.node,
98
+ isLoading : PropTypes.bool,//Si l'écran est en mode chargement des données
97
99
  preloaderProps : PropTypes.object,
98
100
  header : PropTypes.oneOfType([
99
101
  PropTypes.element,
@@ -1,4 +1,5 @@
1
1
  import {defaultStr,isNumber,isPromise,defaultVal,extendObj,defaultObj,uniqid,isObj,isObjOrArray} from "$utils";
2
+ import stableHash from "stable-hash";
2
3
  import {FormData} from "$ecomponents/Form";
3
4
  import FormDataScreen from "./FormData";
4
5
  import ScreenContainer from "./Screen";
@@ -71,18 +72,8 @@ export const isDocEditing = (data,fields,checkPrimaryKey)=>{
71
72
  export default class TableDataScreenComponent extends FormDataScreen{
72
73
  constructor(props){
73
74
  super(props);
74
- let cDatas = [];
75
75
  const mainProps = getScreenProps(props);
76
- const hasManyData = isObjOrArray(mainProps.datas) && Object.size(mainProps.datas,true) > 0 ? true : false;
77
- if(hasManyData){
78
- cDatas = Object.toArray(mainProps.datas);
79
- }
80
- extendObj(this.state,{
81
- hasManyData,
82
- datas : cDatas,
83
- currentIndex : 0,
84
- data : hasManyData ? defaultObj(cDatas[0]) : isObj(mainProps.data)? mainProps.data : {}
85
- });
76
+ extendObj(this.state,this.prepareStateData(mainProps));
86
77
  const table = defaultObj(mainProps.table);
87
78
  const fields = {},primaryKeyFields = {};
88
79
  Object.map(table.fields,(field,i)=>{
@@ -157,6 +148,20 @@ export default class TableDataScreenComponent extends FormDataScreen{
157
148
  this.hidePreloader = this.hidePreloader.bind(this);
158
149
  this.showPreloader = this.showPreloader.bind(this);
159
150
  };
151
+ prepareStateData(props){
152
+ const mainProps = defaultObj(props,this.props);
153
+ const hasManyData = isObjOrArray(mainProps.datas) && Object.size(mainProps.datas,true) > 0 ? true : false;
154
+ let cDatas = [];
155
+ if(hasManyData){
156
+ cDatas = Object.toArray(mainProps.datas);
157
+ }
158
+ return {
159
+ hasManyData,
160
+ datas : cDatas,
161
+ currentIndex : 0,
162
+ data : hasManyData ? defaultObj(cDatas[0]) : isObj(mainProps.data)? mainProps.data : {}
163
+ };
164
+ }
160
165
  isCurrentDocEditingUpdate(){
161
166
  return this.isDocEditingRef.current === true ? true : false;
162
167
  }
@@ -561,6 +566,14 @@ export default class TableDataScreenComponent extends FormDataScreen{
561
566
  },typeof timeoutCallback =='number'? timeoutCallback : HIDE_PRELOADER_TIMEOUT);
562
567
  return this.hidePreloaderTimeout;
563
568
  }
569
+ UNSAFE_componentWillReceiveProps(nextProps){
570
+ const props = getScreenProps(nextProps);
571
+ const {data,datas}= props;
572
+ if(stableHash({data,datas}) != stableHash({data:this.state.data,datas:this.state.datas})){
573
+ this.setState(this.prepareStateData(props));
574
+ }
575
+ return;
576
+ }
564
577
  reset (args,cb){
565
578
  if(!this._isMounted()) return;
566
579
  clearTimeout(this.timeoutCallback);