@fto-consult/expo-ui 2.20.1 → 2.21.1

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.20.1",
3
+ "version": "2.21.1",
4
4
  "description": "Bibliothèque de composants UI Expo,react-native",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -61,7 +61,8 @@
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.4",
64
+ "@fto-consult/common": "^1.23.9",
65
+ "@fto-consult/expo-ui": "^2.21.0",
65
66
  "@gorhom/portal": "^1.0.14",
66
67
  "@react-native-async-storage/async-storage": "~1.17.3",
67
68
  "@react-native-community/datetimepicker": "6.5.2",
@@ -1,6 +1,6 @@
1
1
  import React from '$react'
2
2
  import PropTypes from 'prop-types'
3
- import {defaultStr,defaultObj,uniqid,defaultNumber} from "$utils";
3
+ import {defaultStr,defaultVal,extendObj,defaultObj,uniqid,defaultNumber} from "$utils";
4
4
  import {extend} from "./utils"
5
5
  import stableHash from 'stable-hash';
6
6
  import Chart from "./appexChart";
@@ -16,20 +16,15 @@ import Chart from "./appexChart";
16
16
  * options {number} - les options supplémentaires au chart
17
17
  *
18
18
  */
19
- const ChartComponent = React.forwardRef(({type, height,chartId, width, series, options,testID,webViewProps, ...props },ref)=>{
19
+ const ChartComponent = React.forwardRef(({options,height,width,chartId,testID,webViewProps, ...props },ref)=>{
20
20
  const chartContext = React.useRef(null);
21
21
  options = defaultObj(options);
22
- const chartIdRef = React.useRef(defaultStr(chartId,options.chart?.id,uniqid("chart-id")));
23
- const config = extend(options,{
24
- chart: {
25
- type,
26
- height,
27
- width,
28
- id : chartIdRef.current
29
- },
30
- series,
31
- });
32
- config.chart.id = chartIdRef.current;
22
+ const series = options.series;
23
+ options.chart = defaultObj(options.chart);
24
+ const chartIdRef = React.useRef(defaultStr(chartId,options.chart.id,uniqid("chart-id")));
25
+ width = options.chart.width = defaultVal(options.chart.width,width);
26
+ height = options.chart.height = defaultVal(options.chart.height,height)
27
+ options.chart.id = chartIdRef.current;
33
28
  testID = defaultStr(testID,"RN_ChartComponent");
34
29
  const prevWidth = React.usePrevious(width), prevHeight = React.usePrevious(height);
35
30
  const prevOptions = React.usePrevious(options,JSON.stringify);
@@ -40,21 +35,21 @@ const ChartComponent = React.forwardRef(({type, height,chartId, width, series, o
40
35
  options.xaxis.stroke.width = defaultNumber(options.xaxis.stroke.width,2);
41
36
  options.xaxis.stroke.height = defaultNumber(options.xaxis.height,1);
42
37
 
38
+ console.log(options,' is optssss');
43
39
  React.useEffect(()=>{
44
40
  if(chartContext.current && chartContext.current.updateOptions){
45
41
  if((prevSeries == series) || width != prevWidth || height != prevHeight){
46
- chartContext.current.updateOptions(config);
42
+ chartContext.current.updateOptions(options);
47
43
  } else if(prevOptions != options){
48
44
  chartContext.current.updateSeries(series);
49
45
  }
50
46
  }
51
- },[stableHash({type,options,series,width,height})])
52
- return <Chart {...props} options={config} chartId={chartIdRef.current} chartContext={chartContext} testID={testID} ref={ref}/>
47
+ },[stableHash({options,series,width,height})])
48
+ return <Chart {...props} options={options} chartId={chartIdRef.current} chartContext={chartContext} testID={testID} ref={ref}/>
53
49
  });
54
50
 
55
51
 
56
52
  ChartComponent.propTypes = {
57
- type: PropTypes.string.isRequired,
58
53
  width: PropTypes.oneOfType([
59
54
  PropTypes.string,
60
55
  PropTypes.number,
@@ -63,9 +58,12 @@ ChartComponent.propTypes = {
63
58
  PropTypes.string,
64
59
  PropTypes.number,
65
60
  ]),
66
- series: PropTypes.array.isRequired,
67
61
  options: PropTypes.shape({
62
+ chart : PropTypes.shape({
63
+ type: PropTypes.string,
64
+ }),
68
65
  xaxis : PropTypes.object,
66
+ series: PropTypes.array.isRequired,
69
67
  }).isRequired,
70
68
  ///lorsque le chart est rendu en environnement native, les props du webView
71
69
  webViewProps : PropTypes.object,
@@ -339,8 +339,11 @@ const DatagridFactory = (Factory)=>{
339
339
  backToTopProps,
340
340
  testID,
341
341
  renderEmpty,
342
+ chartContainerProps,
342
343
  ...rest
343
344
  } = this.props
345
+ chartContainerProps = defaultObj(chartContainerProps);
346
+ const canRenderChart = this.canRenderChart();
344
347
  const hasData = this.state.data.length ? true : false;
345
348
  testID = defaultStr(testID,"RN_DatagridAccordion");
346
349
  backToTopProps = defaultObj(backToTopProps);
@@ -504,25 +507,25 @@ const DatagridFactory = (Factory)=>{
504
507
  icon : "refresh",
505
508
  onPress : this.refresh.bind(this)
506
509
  },
507
- visibleColumns.length ? {
510
+ !canRenderChart && visibleColumns.length ? {
508
511
  text : 'colonnes',
509
512
  icon : "view-column",
510
513
  items : visibleColumns,
511
514
  closeOnPress : false,
512
515
  } : null,
513
- hasFooterFields ? {
516
+ !canRenderChart && hasFooterFields ? {
514
517
  onPress : ()=>{this.toggleFooters(!showFooters)}
515
518
  ,icon : showFooters?'view-column':'view-module'
516
519
  ,text : (showFooters?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux')
517
520
  }:null,
518
521
  ...this.renderCustomMenu(),
519
522
  ...restItems,
520
- this.canScrollTo() && {
523
+ !canRenderChart && this.canScrollTo() && {
521
524
  text : 'Retour en haut',
522
525
  icon : "arrow-up-bold",
523
526
  onPress : this.scrollToTop.bind(this)
524
527
  },
525
- this.canScrollTo() && {
528
+ !canRenderChart && this.canScrollTo() && {
526
529
  text : 'Aller à la dernière ligne',
527
530
  icon : "arrow-down-bold",
528
531
  onPress : this.scrollToEnd.bind(this)
@@ -570,7 +573,7 @@ const DatagridFactory = (Factory)=>{
570
573
  /> : null}
571
574
  {datagridHeader}
572
575
  {_progressBar}
573
- {showFooters ? (
576
+ {!canRenderChart && showFooters ? (
574
577
  <View testID={testID+"_FooterContainer"} pointerEvents={pointerEvents} style={[{justifyContent:'center'}]}>
575
578
  <View testID={testID+"_FooterContentContainer"} style={[styles.footersContainer]}>
576
579
  <ScrollView testID={testID+"_FooterScrollView"} horizontal contentContainerStyle={[styles.contentContainerStyle]}>
@@ -589,7 +592,7 @@ const DatagridFactory = (Factory)=>{
589
592
  </View>
590
593
  ) : null}
591
594
  </View>
592
- {hasData ? <FlashList
595
+ {hasData && !canRenderChart ? <FlashList
593
596
  estimatedItemSize = {150}
594
597
  prepareItems = {false}
595
598
  {...rest}
@@ -609,11 +612,13 @@ const DatagridFactory = (Factory)=>{
609
612
  return this.backToTopRef.current;
610
613
  }:false}
611
614
  keyExtractor = {this.getRowKey.bind(this)}
612
- /> : <View style={styles.hasNotData}>
615
+ /> : canRenderChart ?<View testID={testID+"_ChartContainer"} {...chartContainerProps} style={[theme.styles.w100,chartContainerProps.style]}>
616
+ {this.renderChart()}
617
+ </View> : <View style={styles.hasNotData}>
613
618
  {this.renderEmpty()}
614
619
  </View>}
615
620
  </View>}
616
- {backToTopRef ? <BackToTop testID={testID+"_BackToTop"} {...backToTopProps} ref={this.backToTopRef} style={[styles.backToTop,backToTopProps.style]} onPress={this.scrollToTop.bind(this)}/>:null}
621
+ {!canRenderChart && backToTopRef ? <BackToTop testID={testID+"_BackToTop"} {...backToTopProps} ref={this.backToTopRef} style={[styles.backToTop,backToTopProps.style]} onPress={this.scrollToTop.bind(this)}/>:null}
617
622
  <BottomSheet
618
623
  testID = {testID+"_BottomSheet"}
619
624
  renderMenuContent = {false}
@@ -38,6 +38,47 @@ import View from "$ecomponents/View";
38
38
  import {Menu} from "$ecomponents/BottomSheet";
39
39
  import {styles as tableStyles} from "$ecomponents/Table";
40
40
  import {DialogProvider} from "$ecomponents/Form/FormData";
41
+ import Chart from "$ecomponents/Chart";
42
+ import { aggregatorFunctions} from "../Footer/Footer";
43
+
44
+ const chart = "chart";
45
+ export const displayTypes = {
46
+ table : {
47
+ code : "table",
48
+ isChart : false,
49
+ label : 'Tableau',
50
+ icon : "table",
51
+ type : 'table',
52
+ },
53
+ lineChart : {
54
+ code : "lineChart",
55
+ isChart : true,
56
+ label : 'Graphique|Linéaire',
57
+ icon : "chart-areaspline",
58
+ type : 'line',
59
+ },
60
+ areaChart : {
61
+ isChart : true,
62
+ code : 'areaChart',
63
+ label : 'Graphique|Surface',
64
+ icon : "chart-areaspline-variant",
65
+ type: 'area',
66
+ },
67
+ barChart : {
68
+ isChart : true,
69
+ code : 'barChart',
70
+ label : 'Graphique|Barres',
71
+ icon : "chart-bar",
72
+ type: 'bar'
73
+ },
74
+ /*donutChart : {
75
+ isChart : true,
76
+ code : 'donutChart',
77
+ label : 'Graphique|Circulaire',
78
+ icon : "chart-donut",
79
+ type: 'donut'
80
+ },*/
81
+ }
41
82
 
42
83
  export const arrayValueSeparator = ", ";
43
84
 
@@ -85,6 +126,17 @@ export default class CommonDatagridComponent extends AppComponent {
85
126
  showFooters : defaultBool(props.showFooters,(sData.showFooters? true : false)),
86
127
  fixedTable : sData.fixedTable
87
128
  });
129
+ const disTypes = {};
130
+ let hasFoundDisplayTypes = false;
131
+ Object.map(this.props.displayTypes,(dType,v)=>{
132
+ if(isNonNullString(dType)){
133
+ dType = dType.toLowerCase().trim();
134
+ if(displayTypes[dType]){
135
+ disTypes[dType] = Object.clone(displayTypes[dType]);
136
+ }
137
+ hasFoundDisplayTypes = true;
138
+ }
139
+ });
88
140
  Object.defineProperties(this,{
89
141
  layoutRef : {
90
142
  value : React.createRef(null),
@@ -119,6 +171,8 @@ export default class CommonDatagridComponent extends AppComponent {
119
171
  sectionListDataSize : {value : {current : 0}},
120
172
  enablePointerEventsRef : {value : {current:false}},
121
173
  configureSectionListSelectedValues : {value : {}},
174
+ ///les types d'affichage
175
+ displayTypes : {value : hasFoundDisplayTypes ? disTypes : Object.clone(displayTypes)},
122
176
  sectionListColumnsSize : {value : {current:0}}, //la taille du nombre d'éléments de section dans les colonnes
123
177
  })
124
178
  this.isLoading = this.isLoading.bind(this);
@@ -150,10 +204,12 @@ export default class CommonDatagridComponent extends AppComponent {
150
204
  this.state.filteredColumns = defaultObj(this.getSessionData("filteredColumns"+this.getSessionNameKey()),this.props.filters);
151
205
  this.filtersSelectors = {selector:this.getFilters()};
152
206
  const {sectionListColumns} = this.prepareColumns();
153
- this.state.sectionListColumns = sectionListColumns;
154
207
  if(this.canHandleColumnResize()){
155
208
  this.state.columnsWidths = this.preparedColumns.widths;
156
209
  }
210
+ this.state.chartConfig = defaultObj(this.props.chartConfig,this.getSessionData("chartConfig"));
211
+ const dType = defaultStr(this.props.displayType,this.getSessionData("displayType"),"table");
212
+ this.state.displayType = this.displayTypes[dType] ? this.displayTypes[dType].code : "table" in this.displayTypes ? "table" : Object.keys(this.displayTypes)[0]?.code;
157
213
  extendObj(this.state,this.prepareData({data}));
158
214
  const {width:windowWidth,height:windowHeight} = Dimensions.get("window");
159
215
  this.state.layout = {
@@ -198,7 +254,10 @@ export default class CommonDatagridComponent extends AppComponent {
198
254
  } else {
199
255
  this.currentDataSources = Object.toArray(this.currentDataSources);
200
256
  }
257
+ this.state.sectionListColumns = sectionListColumns;
258
+ this.state.displayOnlySectionListHeaders = defaultBool(this.getSessionData("displayOnlySectionListHeaders"),this.props.displayOnlySectionListHeaders,false)
201
259
  }
260
+
202
261
  /*** si une ligne peut être selectionable */
203
262
  canSelectRow(row){
204
263
  return isObj(row) && row.isSectionListHeader !== true ? true : false;
@@ -206,7 +265,7 @@ export default class CommonDatagridComponent extends AppComponent {
206
265
  prepareSectionListColumns(props){
207
266
  props = defaultObj(props,this.props);
208
267
  const l = {};
209
- (Array.isArray(props.sectionListColumns) ? props.sectionListColumns : []).map((col)=>{
268
+ (Array.isArray(props.sectionListColumns) ? props.sectionListColumns : defaultArray(this.getSessionData("sectionListColumns"))).map((col)=>{
210
269
  if(isNonNullString(col)){
211
270
  l[col.trim()] = {};
212
271
  }
@@ -230,7 +289,7 @@ export default class CommonDatagridComponent extends AppComponent {
230
289
  let sessionName = this.props.sessionName;
231
290
  let isDatagrid = this.isDatagrid()
232
291
  if(!isNonNullString(sessionName)){
233
- sessionName = 'datagrid';
292
+ //sessionName = 'datagrid';
234
293
  }
235
294
  let userCode = Auth.getLoggedUserCode();
236
295
  Object.defineProperties(this,{
@@ -995,72 +1054,72 @@ export default class CommonDatagridComponent extends AppComponent {
995
1054
  }
996
1055
  return null;
997
1056
  }
998
- /*** configure la */
999
- configureSectionListColumn(column){
1057
+ isSectionListColumnConfigurable(column){
1000
1058
  if(!isObj(column) || !isNonNullString(column.field) || !isObj(this.state.columns[column.field])){
1001
- return Promise.reject({message : 'type de colonne invalide, impossible de configurer la colonne, pour permettre qu\elle soit ajoutée dans les colonnes de groupe du tableau'})
1002
- }
1003
- const col = this.state.columns[column.field];
1004
- const type = defaultStr(col.jsType,col.type).toLowerCase();
1005
- if(type.contains("date") || type =='time'){
1006
- return new Promise((resolve,reject)=>{
1007
- DialogProvider.open({
1008
- title : 'Format de date',
1009
- fields : {
1010
- dateFormat : {
1011
- type : 'select_dateformat',
1012
- required : true,
1013
- text : 'Sélectionnez un format de date',
1014
- defaultValue : defaultStr(this.configureSectionListSelectedValues[column.field],"dd/mm/yyyy"),
1015
- }
1016
- },
1017
- onCancelButtonPress : ()=>{
1059
+ return false;
1060
+ }
1061
+ const type = defaultStr(column.jsType,column.type).toLowerCase();
1062
+ return type.contains("date") || type =='time' ? true : false;
1063
+ }
1064
+ /*** configure la */
1065
+ configureSectionListColumn(column,toggleSectionList){
1066
+ if(!this.isSectionListColumnConfigurable(column)) return Promise.reject({message : 'type de colonne invalide, impossible de configurer la colonne, pour permettre qu\elle soit ajoutée dans les colonnes de groupe du tableau'});
1067
+ const format = defaultStr(defaultObj(this.configureSectionListSelectedValues[column.field]).format,"dd/mm/yyyy")
1068
+ return new Promise((resolve,reject)=>{
1069
+ DialogProvider.open({
1070
+ title : 'Format de date',
1071
+ subtitle : false,
1072
+ fields : {
1073
+ dateFormat : {
1074
+ type : 'select_dateformat',
1075
+ required : true,
1076
+ text : 'Sélectionnez un format de date',
1077
+ defaultValue : format,
1078
+ }
1079
+ },
1080
+ onCancelButtonPress : ()=>{
1081
+ DialogProvider.close();
1082
+ reject({msg:'aucun format sélectionné'})
1083
+ },
1084
+ actions : [{
1085
+ text : "Sélectionnez",
1086
+ icon : "check",
1087
+ onPress : ({data})=>{
1088
+ column.format = data.dateFormat;
1089
+ this.configureSectionListSelectedValues[column.field] = {format:column.format}
1018
1090
  DialogProvider.close();
1019
- reject({msg:'aucun format sélectionné'})
1091
+ setTimeout(()=>{
1092
+ resolve(column);
1093
+ if(toggleSectionList !== false){
1094
+ this.toggleColumnInSectionList(column.field,true);
1095
+ }
1096
+ },100)
1020
1097
  },
1021
- actions : [{
1022
- text : "Sélectionnez",
1023
- icon : "check",
1024
- onPress : ({data})=>{
1025
- column.format = data.dateFormat;
1026
- this.configureSectionListSelectedValues[column.field] = data.dateFormat;
1027
- DialogProvider.close();
1028
- setTimeout(()=>{
1029
- resolve(column);
1030
- },100)
1031
- },
1032
- }]
1033
- })
1098
+ }]
1034
1099
  })
1035
- }
1036
- return Promise.resolve(column);
1100
+ })
1037
1101
  }
1038
- toggleColumnInSectionList(columnName){
1102
+ toggleColumnInSectionList(columnName,enable){
1039
1103
  if(!isNonNullString(columnName) || !isObj(this.state.columns[columnName])) return;
1040
1104
  if(!isObj(this.state.sectionListColumns) || !Array.isArray(this.preparedColumns?.sectionListColumnsMenuItems))return;
1041
1105
  const menuItems = this.preparedColumns?.sectionListColumnsMenuItems;
1042
1106
  if(!menuItems.length) return;
1043
1107
  const sectionListColumns = {...this.state.sectionListColumns};
1044
- if(isObj(sectionListColumns[columnName])){
1108
+ if(enable !== true && isObj(sectionListColumns[columnName])){
1045
1109
  delete sectionListColumns[columnName];
1046
1110
  } else {
1047
1111
  sectionListColumns[columnName] = {field:columnName};
1048
1112
  }
1049
- const cb =()=>{
1113
+ setTimeout(() => {
1050
1114
  const {sectionListColumns:pSListColumns} = this.prepareColumns({sectionListColumns});
1051
1115
  this.setIsLoading(true,()=>{
1052
1116
  this.prepareData({data:this.INITIAL_STATE.data,sectionListColumns:pSListColumns},(state)=>{
1053
1117
  this.setState({...state,sectionListColumns:pSListColumns},()=>{
1054
1118
  this.setIsLoading(false,false);
1119
+ this.setSessionData("sectionListColumns",Object.keys(pSListColumns));
1055
1120
  });
1056
1121
  });
1057
1122
  },true);
1058
- }
1059
- setTimeout(() => {
1060
- if(!isObj(sectionListColumns[columnName])){
1061
- return cb();
1062
- }
1063
- return this.configureSectionListColumn(sectionListColumns[columnName]).then(cb).catch(notify.error)
1064
1123
  }, 100);
1065
1124
  }
1066
1125
  removeAllColumnsInSectionList(){
@@ -1069,16 +1128,338 @@ export default class CommonDatagridComponent extends AppComponent {
1069
1128
  this.prepareData({data:this.INITIAL_STATE.data,sectionListColumns},(state)=>{
1070
1129
  this.setState({...state,sectionListColumns},()=>{
1071
1130
  this.setIsLoading(false,false);
1131
+ this.setSessionData("sectionListColumns",null);
1072
1132
  });
1073
1133
  });
1074
1134
  },true);
1075
1135
  }
1136
+ canDisplayOnlySectionListHeaders(){
1137
+ return this.hasFooterFields() && this.isSectionList() && this.hasSectionListData();
1138
+ }
1139
+ /*** si l'on peut rendre le contenu de type graphique */
1140
+ isChartRendable(){
1141
+ return !this.isPivotDatagrid() && this.hasFooterFields();
1142
+ }
1143
+ isValidChartConfig(config){
1144
+ config = defaultObj(config,this.state.chartConfig);
1145
+ return isNonNullString(config.x) && isNonNullString(config.y);
1146
+ }
1147
+ canRenderChart(){
1148
+ return this.isChartRendable() && isNonNullString(this.state.displayType) && displayTypes[this.state.displayType]?.isChart === true ? true : false;
1149
+ }
1150
+ persistDisplayType(displayType){
1151
+ this.setSessionData("displayType",displayType);
1152
+ }
1153
+ /***modifie le type de données d'affichage du tableau */
1154
+ setDisplayType(type){
1155
+ if(!isObj(type) || !isNonNullString(type.code) || !displayTypes[type.code]) return;
1156
+ const tt = displayTypes[type.code];
1157
+ if(this.state.displayType == tt.code) return;
1158
+ const displayType = tt.code;
1159
+ if(tt.code == 'table'){
1160
+ return this.setIsLoading(true,()=>{
1161
+ this.setState({displayType},()=>{
1162
+ this.setIsLoading(false,false);
1163
+ this.persistDisplayType(displayType);
1164
+ });
1165
+ },true)
1166
+ } else {
1167
+ const cb = (chartConfig)=>{
1168
+ this.setIsLoading(true,()=>{
1169
+ this.setState({chartConfig,displayType},()=>{
1170
+ this.setIsLoading(false,false);
1171
+ this.persistDisplayType(displayType);
1172
+ })
1173
+ },true);
1174
+ }
1175
+ if(!this.isValidChartConfig()){
1176
+ return this.configureChart(false).then((chartConfig)=>{
1177
+ cb(chartConfig);
1178
+ });
1179
+ }
1180
+ cb({...this.state.chartConfig});
1181
+ }
1182
+ }
1183
+ configureChart(refreshChart){
1184
+ if(!this.isChartRendable()){
1185
+ return Promise.reject({message:'Impossible de configurer le graphe car le type de données ne permet pas de rendu de type graphe'});
1186
+ }
1187
+ const xItems = {},yItems = {},config = defaultObj(this.state.chartConfig);
1188
+ const series = {};
1189
+ let hasSeries = false;
1190
+ const isValidConfig = this.isValidChartConfig();
1191
+ Object.map(this.state.columns,(field,f)=>{
1192
+ if(isObj(field) && !this.isSelectableColumn(field) && !this.isIndexColumn(field)){
1193
+ xItems[f] = field;
1194
+ const type = defaultStr(field.jsType,field.type).toLowerCase();
1195
+ if(type === 'number' || type=='decimal'){
1196
+ yItems[f] = field;
1197
+ series[f] = field;
1198
+ }
1199
+ }
1200
+ });
1201
+ const onValidatorValid = ({context,value})=>{
1202
+ if(context && value){
1203
+ const name = context.getName();
1204
+ const isHorizontal = name =="x";
1205
+ const oContext = context.getField(isHorizontal ? "y" : "x");
1206
+ if(!oContext) return;
1207
+ const v2 = oContext.getValue()
1208
+ if(v2 === value){
1209
+ return "la valeur du champ [{0}] doit être différent de celle du champ [{1}]".sprintf(context.getLabel(),oContext.getLabel())
1210
+ }
1211
+ oContext.validate({value:v2,context:oContext});
1212
+ }
1213
+ }
1214
+ return new Promise((resolve,reject)=>{
1215
+ DialogProvider.open({
1216
+ title : 'Configuration des graphes',
1217
+ subtitle : false,
1218
+ fields : {
1219
+ x : {
1220
+ text : 'Axe des x[horizontal]',
1221
+ type : "select",
1222
+ required : true,
1223
+ items : xItems,
1224
+ defaultValue : config.x,
1225
+ onValidatorValid,
1226
+ },
1227
+ y : {
1228
+ text : 'Axe des y[Vertical]',
1229
+ type : "select",
1230
+ required : true,
1231
+ items : yItems,
1232
+ defaultValue : config.y,
1233
+ onValidatorValid,
1234
+ },
1235
+ series : hasSeries && {
1236
+ text : 'Series',
1237
+ type : "select",
1238
+ items : series,
1239
+ multiple : true,
1240
+ },
1241
+ aggregatorFunction : {
1242
+ type : 'select',
1243
+ text : "Foncton d'aggrégation",
1244
+ required : true,
1245
+ multiple : false,
1246
+ defaultValue : "sum",
1247
+ items : aggregatorFunctions,
1248
+ }
1249
+ },
1250
+ actions : [
1251
+ {
1252
+ text : "Configurer",
1253
+ icon : "check",
1254
+ onPress : ({data})=>{
1255
+ const chartConfig = {...config,...data};
1256
+ this.setSessionData("chartConfig",chartConfig);
1257
+ DialogProvider.close();
1258
+ if(false && !isValidConfig && refreshChart !== false){
1259
+ return this.setState({chartConfig},()=>{
1260
+ resolve(chartConfig)
1261
+ })
1262
+ }
1263
+ resolve(chartConfig);
1264
+ }
1265
+ }
1266
+ ]
1267
+ })
1268
+ })
1269
+ }
1270
+ getCharConfig(){
1271
+ return defaultObj(this.state.chartConfig);
1272
+ }
1273
+ renderDisplayTypes(){
1274
+ const m = [];
1275
+ let activeType = null,hasFoundChart = false,hasFoundTable = false;
1276
+ const hasConfig = this.isValidChartConfig();
1277
+ Object.map(this.displayTypes,(type,k)=>{
1278
+ if(type.isChart === true ) {
1279
+ if(!this.isChartRendable()){
1280
+ return null;
1281
+ }
1282
+ if(!hasFoundChart){
1283
+ if(hasFoundTable){
1284
+ m.push({divider:true});
1285
+ }
1286
+ hasFoundChart = true;
1287
+ m.push({
1288
+ divider : true,
1289
+ text : "Configurer les graphes",
1290
+ icon :"material-settings",
1291
+ onPress : this.configureChart.bind(this)
1292
+ });
1293
+ }
1294
+ } else if(k === 'table'){
1295
+ hasFoundTable = true;
1296
+ }
1297
+ const active = this.state.displayType === k;
1298
+ if(active){
1299
+ activeType = type;
1300
+ }
1301
+ m.push({
1302
+ ...type,
1303
+ labelStyle : active && {fontWeight:'bold',color:theme.colors.primaryOnSurface} || null,
1304
+ right : <>
1305
+ {active ? <Icon color={theme.colors.primaryOnSurface} name="check"/>: null}
1306
+ </>,
1307
+ disabled : type.isChart && !hasConfig ? true : undefined,
1308
+ onPress:()=>{
1309
+ this.setDisplayType(type);
1310
+ }
1311
+ });
1312
+ });
1313
+ if(m.length <= 1 || !activeType) return null;
1314
+ if(!isMobileOrTabletMedia()){
1315
+ m.unshift({text:"Type d'affichage des données",divider:true});
1316
+ }
1317
+ return <Menu
1318
+ title = "Type d'affichage"
1319
+ items = {m}
1320
+ anchor = {(p)=>{
1321
+ return <Icon
1322
+ {...p}
1323
+ name = {activeType.icon}
1324
+ title = {"Les données s'affichent actuellement en {0}. Cliquez pour modifier le type d'affichage".sprintf(activeType.label)}
1325
+ />
1326
+ }}
1327
+ />
1328
+ }
1329
+ getEmptyDataValue(){
1330
+ return "N/A";
1331
+ }
1332
+ renderChart(){
1333
+ if(!this.canRenderChart()) return null;
1334
+ if(!this.isValidChartConfig()) return null;
1335
+ const chartType = displayTypes[this.state.displayType];
1336
+ if(!isObj(chartType) || !isNonNullString(chartType.type)) return null;
1337
+ const config = this.getCharConfig();
1338
+ if(!this.state.columns[config.y]) return null;
1339
+ const yaxis = this.state.columns[config.y];
1340
+ const type = defaultStr(yaxis.jsType,yaxis.type).toLowerCase();
1341
+ if(type !== 'number'&& type !== 'decimal') return null;
1342
+ const isEmptyY = config.x === this.emptySectionListHeaderValue;
1343
+ const seriesConfig = Array.isArray(config.series) && config.series.length ? config.series : [yaxis.field];
1344
+ let xaxis = null;
1345
+ if(!isEmptyY){
1346
+ if(!this.state.columns[config.x]){
1347
+ return null;
1348
+ }
1349
+ xaxis = this.state.columns[config.x];
1350
+ }
1351
+ let aggregatorFunction = typeof config.aggregatorFunction =='string' && aggregatorFunctions[config.aggregatorFunction]?aggregatorFunctions[config.aggregatorFunction] :aggregatorFunctions.sum;
1352
+ if(isObj(aggregatorFunction) && typeof aggregatorFunction.eval =='function'){
1353
+ aggregatorFunction = aggregatorFunction.eval;
1354
+ } else {
1355
+ aggregatorFunction = aggregatorFunctions.sum.eval;
1356
+ }
1357
+ if(this.isSectionList()){
1358
+ return null;
1359
+ }
1360
+ const emptyValue = this.getEmptyDataValue();
1361
+ const indexes = {}
1362
+ let count = 0;
1363
+ this.state.data.map((data,index)=>{
1364
+ if(!isObj(data))return null;
1365
+ const txt = this.renderRowCell({
1366
+ data,
1367
+ rowData : data,
1368
+ rowCounterIndex : index,
1369
+ rowIndex : index,
1370
+ columnDef : xaxis,
1371
+ renderRowCell : false,
1372
+ columnField : xaxis.field,
1373
+ });
1374
+ const text = isNonNullString(txt)? txt : emptyValue;
1375
+ Object.map(seriesConfig,(s,v)=>{
1376
+ if(!isNonNullString(s) || !this.state.columns[s]) return null;
1377
+ const col = this.state.columns[s];
1378
+ if(!isObj(col)) return null;
1379
+ const value = defaultNumber(data[col.field]);
1380
+ indexes[s] = defaultObj(indexes[s]);
1381
+ const current = indexes[s];
1382
+ current[text] = typeof current[text] =="number"? current[text] : 0;
1383
+ current[text] = aggregatorFunction(value,current[text],count);
1384
+ })
1385
+ });
1386
+ const series = [];
1387
+ Object.map(indexes,(values,serieName)=>{
1388
+ const col = this.state.columns[serieName];
1389
+ const name = defaultStr(col?.label,col?.text,serieName),data = [];
1390
+ Object.map(values,(v,i)=>{
1391
+ data.push({
1392
+ x : i,
1393
+ y : v,
1394
+ })
1395
+ })
1396
+ series.push({
1397
+ name,
1398
+ type : chartType.type,
1399
+ data,
1400
+ })
1401
+ })
1402
+ const {width,height:winheight} = Dimensions.get("window");
1403
+ const {layout} = this.state;
1404
+ let maxHeight = winheight-100;
1405
+ if(layout && typeof layout.windowHeight =='number' && layout.windowHeight){
1406
+ const diff = winheight - Math.max(defaultNumber(layout.y,layout.top),100);
1407
+ if(winheight<=350){
1408
+ maxHeight = 350;
1409
+ } else {
1410
+ maxHeight = diff;
1411
+ }
1412
+ }
1413
+ const chartProps = defaultObj(chartProps);
1414
+ return <Chart
1415
+ options = {{
1416
+ ...defaultObj(chartProps.options),
1417
+ series,
1418
+ chart : {
1419
+ height :350,
1420
+ maxHeight,
1421
+ ...defaultObj(chartProps.chart),
1422
+ type : chartType.type,
1423
+ },
1424
+ xaxis: {
1425
+ ...defaultObj(chartProps.xaxis),
1426
+ type: 'category'
1427
+ }
1428
+ }}
1429
+ />
1430
+ }
1431
+ toggleDisplayOnlySectionListHeaders(){
1432
+ if(!this.canDisplayOnlySectionListHeaders()) return
1433
+ setTimeout(()=>{
1434
+ const showFooters = true;
1435
+ const displayOnlySectionListHeaders = !!!this.state.displayOnlySectionListHeaders;
1436
+ this.setSessionData("displayOnlySectionListHeaders",displayOnlySectionListHeaders);
1437
+ if(!displayOnlySectionListHeaders){
1438
+ return this.prepareData({data:this.INITIAL_STATE.data,displayOnlySectionListHeaders},(state)=>{
1439
+ this.setState({...state,showFooters})
1440
+ })
1441
+ } else {
1442
+ this.setIsLoading(true,()=>{
1443
+ const data = [];
1444
+ this.state.data.map((d)=>{
1445
+ if(isObj(d) && d.isSectionListHeader === true){
1446
+ data.push(d);
1447
+ }
1448
+ });
1449
+ this.setState({data,displayOnlySectionListHeaders,showFooters},()=>{
1450
+ this.setIsLoading(false),false;
1451
+ });
1452
+ },true)
1453
+ }
1454
+ },100);
1455
+ }
1076
1456
  /*** permet d'effectuer le rendu des colonnes groupable dans le menu item */
1077
1457
  renderSectionListMenu(){
1078
1458
  const m = Array.isArray(this.preparedColumns?.sectionListColumnsMenuItems)? this.preparedColumns?.sectionListColumnsMenuItems : [];
1079
1459
  if(!m.length){
1080
1460
  return null;
1081
1461
  }
1462
+ const hasList = this.sectionListColumnsSize.current;
1082
1463
  const isMobile = isMobileOrTabletMedia();
1083
1464
  return <Menu
1084
1465
  title = {"Grouper les données du tableau"}
@@ -1093,7 +1474,12 @@ export default class CommonDatagridComponent extends AppComponent {
1093
1474
  closeOnPress : false,
1094
1475
  divider : true,
1095
1476
  },
1096
- this.sectionListColumnsSize.current && {
1477
+ this.canDisplayOnlySectionListHeaders() && {
1478
+ text : "Afficher uniquement totaux",
1479
+ icon : this.state.displayOnlySectionListHeaders?"check":null,
1480
+ onPress : this.toggleDisplayOnlySectionListHeaders.bind(this)
1481
+ },
1482
+ hasList && {
1097
1483
  text : "Supprimer les groupes",
1098
1484
  icon: "ungroup",
1099
1485
  divider : true,
@@ -1146,6 +1532,7 @@ export default class CommonDatagridComponent extends AppComponent {
1146
1532
  } = header;
1147
1533
  restCol = Object.clone(defaultObj(restCol));
1148
1534
  let colFilter = defaultVal(restCol.filter,true);
1535
+ const format = defaultStr(restCol.format).toLowerCase();
1149
1536
  field = header.field = defaultStr(header.field,field,headerIndex);
1150
1537
  delete restCol.filter;
1151
1538
 
@@ -1273,19 +1660,32 @@ export default class CommonDatagridComponent extends AppComponent {
1273
1660
  ...header,
1274
1661
  width,
1275
1662
  type,
1663
+ ...defaultObj(this.configureSectionListSelectedValues[field]),
1276
1664
  ...sListColumns[field],
1277
1665
  };///les colonnes de sections
1278
1666
  this.sectionListColumnsSize.current++;
1279
1667
  }
1280
- sectionListColumnsMenuItems.push({
1668
+ const mItem = {
1281
1669
  field,
1670
+ type,
1671
+ format,
1282
1672
  onPress : ()=>{
1283
1673
  this.toggleColumnInSectionList(field);
1284
1674
  return false;
1285
1675
  },
1286
1676
  title : title,
1287
1677
  icon : isInSectionListHeader?CHECKED_ICON_NAME : null,
1288
- });
1678
+ };
1679
+ if(this.isSectionListColumnConfigurable(mItem)){
1680
+ mItem.right = (p)=>{
1681
+ return <Icon name="material-settings" {...p} onPress={(e)=>{
1682
+ React.stopEventPropagation(e);
1683
+ this.configureSectionListColumn(mItem);
1684
+ return false;
1685
+ }}/>
1686
+ }
1687
+ }
1688
+ sectionListColumnsMenuItems.push(mItem);
1289
1689
  }
1290
1690
  columnIndex++;
1291
1691
  visibleColumnIndex++;
@@ -1314,9 +1714,9 @@ export default class CommonDatagridComponent extends AppComponent {
1314
1714
  getFooterValues(){
1315
1715
  return defaultObj(this.___evaluatedFootersValues);
1316
1716
  }
1317
-
1717
+
1318
1718
  /**** s'il s'agit d'une section list */
1319
- isSectionList(sectionListColumns){
1719
+ isSectionList (sectionListColumns){
1320
1720
  sectionListColumns = isObj(sectionListColumns) ? sectionListColumns : this.state.sectionListColumns;
1321
1721
  return !this.isPivotDatagrid() && isObj(sectionListColumns) && Object.size(sectionListColumns,true) ? true : false;
1322
1722
  }
@@ -1341,13 +1741,14 @@ export default class CommonDatagridComponent extends AppComponent {
1341
1741
  return defaultNumber(this.sectionListDataSize.current)
1342
1742
  }
1343
1743
  prepareData(args,cb){
1344
- let {pagination,data,force,sectionListColumns,updateFooters} = defaultObj(args);
1744
+ let {pagination,displayOnlySectionListHeaders:cdisplayOnlySectionListHeaders,data,force,sectionListColumns,updateFooters} = defaultObj(args);
1345
1745
  cb = typeof cb ==='function'? cb : typeof args.cb == 'function'? args.cb : undefined;
1346
1746
  sectionListColumns = isObj(sectionListColumns) ? sectionListColumns : this.state.sectionListColumns;
1747
+ const displayOnlySectionListHeaders = typeof cdisplayOnlySectionListHeaders == 'boolean'?cdisplayOnlySectionListHeaders : this.state.displayOnlySectionListHeaders;
1347
1748
  let isArr = Array.isArray(data);
1348
1749
  //let push = (d,index) => isArr ? newData.push(d) : newData[index] = d;
1349
1750
  const hasLocalFilter = this.props.filters !== false && this.hasLocalFilters;
1350
- let footersColumns = this.getFooterFields(),hasFooterFields = Object.size(footersColumns,true);
1751
+ let footersColumns = this.getFooterFields(),hasFooterFields = this.hasFooterFields();
1351
1752
  const canUpdateFooters = !!(updateFooters !== false && hasFooterFields);
1352
1753
  this.hasFoundSectionData.current = false;
1353
1754
  this.sectionListDataSize.current = 0;
@@ -1430,9 +1831,11 @@ export default class CommonDatagridComponent extends AppComponent {
1430
1831
  //this.sectionListData[i] = sortConfig ? sortBy(this.sectionListData[i],sortConfig):this.sectionListData[i];
1431
1832
  //const v = i;// === this.emptySectionListHeaderValue ? "" : i;
1432
1833
  data.push({isSectionListHeader:true,sectionListHeaderKey:i});
1433
- this.sectionListData[i].map((d)=>{
1434
- data.push(d);
1435
- })
1834
+ if(!displayOnlySectionListHeaders){
1835
+ this.sectionListData[i].map((d)=>{
1836
+ data.push(d);
1837
+ })
1838
+ }
1436
1839
  }
1437
1840
  }
1438
1841
  if(!this.hasSectionListData() && this.canPaginateData()){
@@ -1444,7 +1847,7 @@ export default class CommonDatagridComponent extends AppComponent {
1444
1847
  } else if(force){
1445
1848
  this.setSelectedRows();
1446
1849
  }
1447
- const state = {data};
1850
+ const state = {data,displayOnlySectionListHeaders};
1448
1851
  if((cb)){
1449
1852
  cb(state);
1450
1853
  }
@@ -2275,6 +2678,10 @@ CommonDatagridComponent.getDefaultPreloader = (props)=>{
2275
2678
  return <Preloader {...defaultObj(props)}/>
2276
2679
  }
2277
2680
 
2681
+ const chartDisplayType = PropTypes.oneOf(Object.keys(displayTypes).filter(type=>{
2682
+ const x = displayTypes[type];
2683
+ return typeof x =='object' && x && typeof x.disabled !== true && x.isChart === true && true || false;
2684
+ }));
2278
2685
  CommonDatagridComponent.propTypes = {
2279
2686
  canMakePhoneCall : PropTypes.bool,//si l'on peut faire un appel sur la données sélectionnées
2280
2687
  makePhoneCallProps : PropTypes.oneOfType([
@@ -2451,6 +2858,17 @@ CommonDatagridComponent.propTypes = {
2451
2858
  /**** la fonction permettant de faire le rendu dun contenu paginé, personalisé */
2452
2859
  renderCustomPagination : PropTypes.func,
2453
2860
  getActionsArgs : PropTypes.func,//fonction permettant de récupérer les props supplémentaires à passer aux actions du datagrid
2861
+ displayOnlySectionListHeaders : PropTypes.bool,// si uniquement les entêtes des sections seront affichés, valides uniquement en affichage des sectionHeader
2862
+ /*** les options de configuration du graphe */
2863
+ chartConfig : PropTypes.shape({
2864
+ //type : PropTypes.oneOfType(chartDisplayType).isRequired,//le type de graphe : l'une des valeurs parmis les éléments cités plus haut
2865
+ x : PropTypes.string.isRequired, //l'axe horizontal
2866
+ y : PropTypes.string.isRequired, //l'axe des y, les colonnes de type nombre
2867
+ series : PropTypes.arrayOf([PropTypes.string]), //les séries, le nombre de courbe a afficher sur le graphe, en fonction du type
2868
+ }),
2869
+ displayType : chartDisplayType,
2870
+ /*** les types d'afichates supportés par l'application */
2871
+ displayTypes : PropTypes.arrayOf(chartDisplayType)
2454
2872
  }
2455
2873
 
2456
2874
  const styles = StyleSheet.create({
@@ -0,0 +1,12 @@
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
+
5
+ Les propriétés d'états :
6
+ displayType, displayOnlySectionListHeaders, sectionListHeaderColumns sont persistées
7
+ ça sous entend que lorsqu'on charge la table data, ces valeurs sont restaurées
8
+
9
+ - Pour faire un affichage en graphique, il faut :
10
+ 1. grouper les éléments du tableau avec au moins une colonne
11
+ 2. disposer dans le tableau, des colonnes de totalisation (de type montant et somme)
12
+ 3.
@@ -14,6 +14,50 @@ let methods = {
14
14
  max : 'Maximum',
15
15
  count : "Nombre",
16
16
  }
17
+ /*** les fonction d'aggreations */
18
+ export const aggregatorFunctions = {
19
+ sum : {
20
+ code : "sum",
21
+ label : "Somme",
22
+ eval : (current,prev,count)=>{
23
+ current = typeof current =='number'?current : 0;
24
+ prev = typeof prev =='number'? prev : 0;
25
+ return current+prev;
26
+ }
27
+ },
28
+ /*average : {
29
+ code : "average",
30
+ label : "Moyenne",
31
+ eval : ()=>{
32
+
33
+ }
34
+ },*/
35
+ min : {
36
+ code : "min",
37
+ label : "Minimum",
38
+ eval : (current,prev,count)=>{
39
+ current = typeof current =='number'?current : 0;
40
+ prev = typeof prev =='number'? prev : 0;
41
+ return Math.min(current,prev);
42
+ },
43
+ },
44
+ max : {
45
+ code : "max",
46
+ label: 'Maximum',
47
+ eval : (current,prev,count)=>{
48
+ current = typeof current =='number'?current : 0;
49
+ prev = typeof prev =='number'? prev : 0;
50
+ return Math.max(current,prev);
51
+ },
52
+ },
53
+ count : {
54
+ code : "count",
55
+ label : "Nombre",
56
+ eval : (current,prev,count)=>{
57
+ return (typeof count =='number'? count : 0)+1;
58
+ }
59
+ },
60
+ }
17
61
  const formatValue = ({value,format,method})=>{
18
62
  return (format === 'money' && method != 'count')? value.formatMoney():value.formatNumber();
19
63
  }
@@ -116,9 +116,12 @@ const DatagridFactory = (Factory)=>{
116
116
  selectable,pagin,showPagination,
117
117
  sessionName,onMount,onUnmount,onFetchData,dataSourceSelector,dataSourceSelectorProps,queryLimit,
118
118
  filters,
119
+ chartContainerProps,
119
120
  accordion, //pour le rendu du header en accordion
120
121
  ...rest
121
122
  } = this.props;
123
+ const canRenderChart = this.canRenderChart();
124
+ chartContainerProps = defaultObj(chartContainerProps);
122
125
  testID = defaultStr(testID,'RN_DatagridTableComponent');
123
126
  rest = defaultObj(rest);
124
127
  let showDataSourceSelector = false;
@@ -168,7 +171,7 @@ const DatagridFactory = (Factory)=>{
168
171
  if(selectableMultiple && max){
169
172
  max = max.formatNumber();
170
173
  restItems = [
171
- ...(selectableMultiple ? [{
174
+ ...(selectableMultiple && !canRenderChart ? [{
172
175
  label : "Sélectionner "+max.formatNumber(),
173
176
  icon : "select-all",
174
177
  onPress : (x,event)=>{
@@ -188,9 +191,9 @@ const DatagridFactory = (Factory)=>{
188
191
  const {layout} = this.state;
189
192
  let maxHeight = winheight-100;
190
193
  if(layout && typeof layout.windowHeight =='number' && layout.windowHeight){
191
- const diff = winheight - Math.max(defaultNumber(layout.y,layout.top),150);
192
- if(winheight<=300){
193
- maxHeight = 300;
194
+ const diff = winheight - Math.max(defaultNumber(layout.y,layout.top),100);
195
+ if(winheight<=350){
196
+ maxHeight = 350;
194
197
  } else {
195
198
  maxHeight = diff;
196
199
  }
@@ -216,7 +219,7 @@ const DatagridFactory = (Factory)=>{
216
219
  {showFilters?'Masquer/Filtres':'Afficher/Filtres'}
217
220
  </Button>
218
221
  )}
219
- {hasFooterFields ? <Button
222
+ {hasFooterFields && !canRenderChart ? <Button
220
223
  normal
221
224
  style={styles.paginationItem}
222
225
  onPress = {()=>{this.toggleFooters(!showFooters)} }
@@ -284,6 +287,7 @@ const DatagridFactory = (Factory)=>{
284
287
 
285
288
  />
286
289
  {this.renderSectionListMenu()}
290
+ {this.renderDisplayTypes()}
287
291
  <View testID={testID+"_HeaderPagination"} style = {styles.paginationItem}>
288
292
  <BottomSheetMenu
289
293
  testID={testID+"_HeaderMenus"}
@@ -325,20 +329,28 @@ const DatagridFactory = (Factory)=>{
325
329
  {rPagination}
326
330
  {_progressBar}
327
331
  </View>
328
- <Table
332
+ {<Table
333
+ renderListContent = {canRenderChart? false:true}
329
334
  ref = {this.listRef}
330
335
  {...rest}
331
336
  onLayout = {(args)=>{
332
337
  if(rest.onLayout){
333
338
  rest.onLayout(args);
334
339
  }
340
+ if(canRenderChart){
341
+ return;
342
+ }
335
343
  this.updateLayout(args);
336
344
  }}
345
+ children = {canRenderChart ? <View testID={testID+"_ChartContainer"} {...chartContainerProps} style={[theme.styles.w100,chartContainerProps.style]}>
346
+ {this.renderChart()}
347
+ </View> : null}
337
348
  getItemType = {this.getFlashListItemType.bind(this)}
338
349
  renderItem = {this.renderFlashListItem.bind(this)}
339
- hasFooters = {hasFooterFields}
350
+ hasFooters = {hasFooterFields && !canRenderChart ? true : false}
340
351
  showFilters = {showFilters}
341
- showFooters = {showFooters}
352
+ showFooters = {showFooters && !canRenderChart ? true : false}
353
+ showHeaders = { canRenderChart ? !!showFilters : true}
342
354
  headerContainerProps = {{}}
343
355
  headerCellContainerProps = {{
344
356
  style : showFilters?{justifyContent:'flex-start'}:null
@@ -359,7 +371,7 @@ const DatagridFactory = (Factory)=>{
359
371
  renderFilterCell={this.renderFilterCell.bind(this)}
360
372
  renderFooterCell={this.renderFooterCell.bind(this)}
361
373
  renderEmpty = {this.renderEmpty.bind(this)}
362
- />
374
+ />}
363
375
  </View>
364
376
  }
365
377
  }
@@ -85,7 +85,7 @@ export const selectDateFormatFieldProps = ({onAdd:customOnAdd,onAddCustomFormat,
85
85
  showAdd : false,
86
86
  }
87
87
  }
88
- export const getDateFormatSelectorItems = x=> Object.map(DateLib.formats,(format)=>{
88
+ export const getDateFormatSelectorItems = x=> Object.map(DateLib.sortedFormats,(format)=>{
89
89
  return {code : format,label:new Date().toFormat(format)}
90
90
  });
91
91
 
@@ -318,6 +318,9 @@ export default class Field extends AppComponent {
318
318
  getName (){
319
319
  return this.name;
320
320
  }
321
+ getLabel (){
322
+ return defaultStr(this.label,this.props.label,this.props.text,this.props.title,this.name);
323
+ }
321
324
  getFormName(){
322
325
  return this.formName;
323
326
  }
@@ -2,6 +2,7 @@ import Field from "./Field";
2
2
  import Dropdown from "$ecomponents/Dropdown";
3
3
  import PropTypes from "prop-types";
4
4
  import {isObj} from "$utils";
5
+ import appConfig from "$capp/config";
5
6
 
6
7
  export default class FormSelectField extends Field{
7
8
  constructor(props) {
@@ -115,8 +116,9 @@ export default class FormSelectField extends Field{
115
116
  delete props.format;
116
117
  delete props.import;
117
118
  delete props.exports;
118
- if( defaultStr(props.name,this.name) === 'country' && props.multiple !== true){
119
- props.defaultValue = defaultStr(props.defaultValue);
119
+ const isCountry = this.type.contains('country');
120
+ if( isCountry && props.multiple !== true){
121
+ props.defaultValue = defaultStr(props.defaultValue,appConfig.countryCode,appConfig.get("countryCode","country"));
120
122
  }
121
123
  return <Dropdown
122
124
  {...props}
@@ -1,5 +1,6 @@
1
1
  import PhoneInput from "$ecomponents/PhoneInput";
2
2
  import Field from "./Field";
3
+ import appConfig from "$capp/config";
3
4
 
4
5
  export default class FormPhoneInputField extends Field {
5
6
  canFocus(){
@@ -9,6 +10,7 @@ export default class FormPhoneInputField extends Field {
9
10
  props.onChange = (args)=>{
10
11
  this.validate(args);
11
12
  }
13
+ props.country = defaultStr(props.country,appConfig.countryCode,appConfig.get("countryCode","country"))
12
14
  return <PhoneInput
13
15
  {...props}
14
16
  setRef = {setRef}
@@ -48,6 +48,7 @@ const FormDataDialogProvider = React.forwardRef((props,innerRef)=>{
48
48
  return <Dialog
49
49
  responsive
50
50
  withScrollView
51
+ subtitle ={false}
51
52
  {...props}
52
53
  {...state}
53
54
  formProps = {formProps}
@@ -9,7 +9,7 @@ import theme,{black,white,Colors,StylePropTypes,DISABLED_OPACITY} from "$theme";
9
9
  import PropTypes from "prop-types";
10
10
  import { Dimensions } from 'react-native';
11
11
  import View from "$ecomponents/View";
12
- import {defaultVal} from "$utils";
12
+ import {defaultVal,isNonNullString,defaultStr,defaultObj} from "$utils";
13
13
  import Tooltip from "$ecomponents/Tooltip";
14
14
  import Label from "$ecomponents/Label";
15
15
 
@@ -27,14 +27,17 @@ const MenuItemComponent = React.forwardRef(({
27
27
  style,
28
28
  contentStyle,
29
29
  testID,
30
- titleStyle,
30
+ labelStyle,
31
31
  primary,
32
32
  secondary,
33
33
  iconProps,
34
34
  isBottomSheetItem,
35
35
  accessibilityLabel,
36
+ right,
37
+ contentContainerProps,
36
38
  ...rest
37
39
  },ref) => {
40
+ contentContainerProps = defaultObj(contentContainerProps);
38
41
  title = defaultVal(label,text,title);
39
42
  const disabledColor = color(theme.dark ? white : black)
40
43
  .alpha(0.32)
@@ -49,20 +52,21 @@ const MenuItemComponent = React.forwardRef(({
49
52
  ? disabledColor
50
53
  : color(theme.colors.text).alpha(theme.ALPHA).rgb().string();
51
54
  iconProps = defaultObj(iconProps);
52
- titleStyle = Object.assign({},StyleSheet.flatten(titleStyle));
55
+ labelStyle = Object.assign({},StyleSheet.flatten(labelStyle));
53
56
  style = Object.assign({},StyleSheet.flatten(style));
54
57
 
55
- if(Colors.isValid(titleStyle.color)){
56
- iconColor = titleStyle.color;
58
+ if(Colors.isValid(labelStyle.color)){
59
+ iconColor = labelStyle.color;
57
60
  } else if(Colors.isValid(style.color)){
58
61
  titleColor = iconColor = style.color;
59
62
  }
60
-
61
63
  const pointerEvents = disabled ? 'none' : 'auto';
62
64
  const disabledStyle = disabled ? {opacity:DISABLED_OPACITY} : null;
63
65
  const winW = Dimensions.get("window").width-30;
64
66
  const maxWidthStyle = isBottomSheetItem ? {width:winW,maxWidth:null} : undefined;
65
67
  const maxWidthTextStyle = isBottomSheetItem ? {width:winW-50} : null;
68
+ right = typeof right =='function'? right ({color:titleColor}) : right;
69
+ if(!React.isValidElement(right)) right = null;
66
70
  return (
67
71
  <Tooltip
68
72
  {...defaultObj(rest)}
@@ -77,31 +81,37 @@ const MenuItemComponent = React.forwardRef(({
77
81
  accessibilityState={{ disabled }}
78
82
  pointerEvents = {pointerEvents}
79
83
  >
80
- <View style={[styles.row]} ref={ref}>
84
+ <View style={[styles.row]} ref={ref} testID={testID+"_ContentContainer"}>
81
85
  {icon ? (
82
- <View style={[styles.item, styles.icon]} pointerEvents="box-none">
86
+ <View testID={testID+"_IconContainer"} style={[styles.item, styles.icon]} pointerEvents="box-none">
83
87
  <Icon source={icon} size={24} {...iconProps} style={[iconProps.style,styles.iconT]} color={iconColor} />
84
88
  </View>
85
89
  ) : null}
86
90
  <View
91
+ testID={testID+"_Right2LabelContainer"}
92
+ pointerEvents={!right?"none":"auto"}
93
+ {...contentContainerProps}
87
94
  style={[
88
95
  styles.item,
89
96
  styles.content,
90
97
  !maxWidthStyle && icon ? styles.widthWithIcon : null,
91
98
  contentStyle,
92
99
  !icon?styles.titleNoIcon:null,
93
- maxWidthStyle
100
+ right && theme.styles.row,
101
+ maxWidthStyle,
102
+ contentContainerProps.style
94
103
  ]}
95
- pointerEvents="none"
96
104
  >
97
105
  <Label
106
+ testID={testID+"_Label"}
98
107
  selectable={false}
99
108
  numberOfLines={1}
100
109
  ellipsizeMode = {"tail"}
101
- style={[styles.title, { color: titleColor }, titleStyle,styles.noMargin,maxWidthTextStyle]}
110
+ style={[styles.title, { color: titleColor }, labelStyle,styles.noMargin,!right ? maxWidthTextStyle : null]}
102
111
  >
103
112
  {title}
104
113
  </Label>
114
+ {right}
105
115
  </View>
106
116
  </View>
107
117
  </Tooltip>
@@ -153,6 +163,9 @@ const styles = StyleSheet.create({
153
163
  widthWithIcon: {
154
164
  maxWidth: maxWidth - iconWidth /*+ 48*/,
155
165
  },
166
+ widthWithRight: {
167
+ maxWidth: maxWidth - iconWidth /*+ 48*/,
168
+ },
156
169
  titleNoIcon : {
157
170
  marginHorizontal : 8
158
171
  }
@@ -185,7 +198,7 @@ MenuItemComponent.propTypes = {
185
198
  */
186
199
  style: StylePropTypes,
187
200
  contentStyle: StylePropTypes,
188
- titleStyle : StylePropTypes,
201
+ labelStyle : StylePropTypes,
189
202
  /**
190
203
  * @optional
191
204
  */
@@ -124,7 +124,7 @@ const Snackbar = ({
124
124
  testID={testID+"_Container"}
125
125
  {...containerProps}
126
126
  pointerEvents="box-none"
127
- style={[styles.wrapper, containerPaddings,containerProps.style]}
127
+ style={[styles.wrapper, containerPaddings,action && theme.styles.pr1,containerProps.style]}
128
128
  >
129
129
  <Surface
130
130
  pointerEvents="box-none"
@@ -43,7 +43,7 @@ const getOnScrollCb = (refs,pos,cb2)=>{
43
43
  return isMobileNative()? cb : debounce(cb,200);
44
44
  }
45
45
 
46
- const TableComponent = React.forwardRef(({containerProps,renderEmpty,renderItem,isRowSelected,headerScrollViewProps,footerScrollViewProps,scrollViewProps,showFooters,renderFooterCell,footerCellContainerProps,filterCellContainerProps,headerContainerProps,headerCellContainerProps,headerProps,rowProps:customRowProps,renderCell,cellContainerProps,hasFooters,renderHeaderCell,renderFilterCell,columnProps,getRowKey,columnsWidths,colsWidths,footerContainerProps,showFilters,columns,data,testID,...props},tableRef)=>{
46
+ const TableComponent = React.forwardRef(({containerProps,renderListContent,children,renderEmpty,renderItem,isRowSelected,headerScrollViewProps,footerScrollViewProps,scrollViewProps,showFooters,renderFooterCell,footerCellContainerProps,filterCellContainerProps,headerContainerProps,headerCellContainerProps,headerProps,rowProps:customRowProps,renderCell,cellContainerProps,hasFooters,renderHeaderCell,renderFilterCell,columnProps,getRowKey,columnsWidths,colsWidths,footerContainerProps,showHeaders,showFilters,columns,data,testID,...props},tableRef)=>{
47
47
  containerProps = defaultObj(containerProps);
48
48
  testID = defaultStr(testID,"RN_TableComponent");
49
49
  cellContainerProps = defaultObj(cellContainerProps);
@@ -138,7 +138,7 @@ const TableComponent = React.forwardRef(({containerProps,renderEmpty,renderItem,
138
138
  React.useEffect(()=>{
139
139
  setState({...state,...prepareState()});
140
140
  },[data,columns]);
141
- const emptyData = typeof renderEmpty =='function' && !Object.size(data,true)? renderEmpty() : null;
141
+ const emptyData = renderListContent === false ?null : typeof renderEmpty =='function' && !Object.size(data,true)? renderEmpty() : null;
142
142
  const hasEmptyData = emptyData && React.isValidElement(emptyData);
143
143
  const {visibleColumns,columns:cols,headers,footers,filters} = state;
144
144
  headerContainerProps = defaultObj(headerContainerProps);
@@ -199,10 +199,11 @@ const TableComponent = React.forwardRef(({containerProps,renderEmpty,renderItem,
199
199
  get scrollViewContext(){ return scrollViewRef.current},
200
200
  get headerScrollViewContext(){return headerScrollViewRef.current},
201
201
  }
202
- const hContent = h.length ? <View testID={testID+"_Header"}{...hProps} {...headerContainerProps} style={[styles.header,headerContainerProps.style,hProps.style,f.length]}>
202
+ const showTableHeaders = showHeaders !== false || showFilters ;
203
+ const hContent = showTableHeaders && h.length ? <View testID={testID+"_Header"}{...hProps} {...headerContainerProps} style={[styles.header,headerContainerProps.style,hProps.style,f.length]}>
203
204
  {h}
204
205
  </View> : null,
205
- fContent = f.length ? <View testID={testID+"_Footer"} {...footerContainerProps} style={[styles.header,styles.footers,footerContainerProps.style,theme.styles.pt0,theme.styles.pb0,theme.styles.ml0,theme.styles.mr0]}>
206
+ fContent = showTableHeaders && f.length ? <View testID={testID+"_Footer"} {...footerContainerProps} style={[styles.header,styles.footers,footerContainerProps.style,theme.styles.pt0,theme.styles.pb0,theme.styles.ml0,theme.styles.mr0]}>
206
207
  {f}
207
208
  </View> : null,
208
209
  filtersContent = fFilters.length ? <View testID={testID+"_Filters"} style={[styles.header,styles.footers,theme.styles.pt0,theme.styles.pb0,theme.styles.ml0,theme.styles.mr0]}>
@@ -241,7 +242,8 @@ const TableComponent = React.forwardRef(({containerProps,renderEmpty,renderItem,
241
242
  </View>
242
243
  </ScrollView>
243
244
  </RNView>
244
- {hasEmptyData ? <View testID={testID+"_Empty"} style={styles.hasNotData}>
245
+ {renderListContent === false ? (React.isValidElement(children)? children : null) :
246
+ hasEmptyData ? <View testID={testID+"_Empty"} style={styles.hasNotData}>
245
247
  {emptyData}
246
248
  </View> : <ScrollView {...scrollViewProps} scrollEventThrottle = {scrollEventThrottle} horizontal contentContainerStyle={[scrollContentContainerStyle,scrollViewProps.contentContainerStyle]} showsVerticalScrollIndicator={false}
247
249
  onScroll = {getOnScrollCb([headerScrollViewRef,footerScrollViewRef],null,(args)=>{
@@ -379,7 +381,7 @@ export const styles = StyleSheet.create({
379
381
  container : {
380
382
  width : '100%',
381
383
  minHeight : 300,
382
- paddingBottom : 50,
384
+ paddingBottom : 10,
383
385
  paddingLeft : 10,
384
386
  paddingRight : 0,
385
387
  flex : 1,
@@ -497,6 +499,7 @@ TableComponent.popTypes = {
497
499
  PropTypes.func,
498
500
  PropTypes.object
499
501
  ]),
502
+ renderListContent : PropTypes.bool,//si l'on devra rendre le contenu de la FlashList
500
503
  renderItem : PropTypes.func,//la fonction permettant de gérer le rendu des item
501
504
  headerScrollViewProps : PropTypes.object,
502
505
  footerScrollViewProps : PropTypes.object,