@fto-consult/expo-ui 2.21.3 → 2.23.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.
@@ -24,7 +24,7 @@ import { StyleSheet,Dimensions,useWindowDimensions} from "react-native";
24
24
  import Preloader from "$ecomponents/Preloader";
25
25
  import Checkbox from "../Checkbox";
26
26
  import { TouchableRipple } from "react-native-paper";
27
- import { evalSingleValue,Footer } from "../Footer";
27
+ import { evalSingleValue,Footer,getFooterColumnValue,isValidAggregator,extendAggreagatorFunctions} from "../Footer";
28
28
  import i18n from "$i18n";
29
29
  import { makePhoneCall,canMakePhoneCall as canMakeCall} from "$makePhoneCall";
30
30
  import copyToClipboard from "$capp/clipboard";
@@ -38,10 +38,19 @@ 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";
41
+ import Chart,{getMaxSupportedSeriesSize} from "$ecomponents/Chart";
43
42
 
44
43
  const chart = "chart";
44
+ export const donutChart = {
45
+ isChart : true,
46
+ code : 'donutChart',
47
+ label : 'Graphique|Circulaire',
48
+ icon : "chart-donut",
49
+ type: 'donut',
50
+ isDonut : true,
51
+ isRendable : ({displayOnlySectionListHeaders,isSectionList})=>false,//isSectionList && displayOnlySectionListHeaders,
52
+ tooltip : "Pour pouvoir visulaiser ce type de graphe, vous devez : grouper les données du tableau selon le criètre de votre choix, puis afficher uniquement les totaux des données groupées"
53
+ }
45
54
  export const displayTypes = {
46
55
  table : {
47
56
  code : "table",
@@ -69,14 +78,31 @@ export const displayTypes = {
69
78
  code : 'barChart',
70
79
  label : 'Graphique|Barres',
71
80
  icon : "chart-bar",
72
- type: 'bar'
81
+ type: 'bar',
82
+ isBar : true,
73
83
  },
74
- /*donutChart : {
84
+ donutChart,
85
+ /*rangeChart : {
86
+ code : "rangeChart",
87
+ isChart : true,
88
+ label : 'Graphique|Bar Interval',
89
+ icon : "chart-timeline",
90
+ type: 'boxPlot'
91
+ },*/
92
+ /*chartBoxPlot : {
93
+ code : "chartBoxPlot",
94
+ isChart : true,
95
+ label : 'Graphique|Box plot',
96
+ icon : "chart-waterfall",
97
+ type: 'boxPlot'
98
+ },*/
99
+ //@see : https://apexcharts.com/docs/chart-types/treemap-chart/
100
+ /*treeMap : {
101
+ code : "treeMap",
75
102
  isChart : true,
76
- code : 'donutChart',
77
- label : 'Graphique|Circulaire',
78
- icon : "chart-donut",
79
- type: 'donut'
103
+ label : 'Graphique|Aborescente',
104
+ icon : "chart-tree",
105
+ type: 'treemap'
80
106
  },*/
81
107
  }
82
108
 
@@ -122,7 +148,7 @@ export default class CommonDatagridComponent extends AppComponent {
122
148
  sData.fixedTable = defaultBool(sData.fixedTable,false);
123
149
  extendObj(this.state, {
124
150
  data,
125
- showFilters : this.isFilterable() && defaultBool(props.showFilters,(sData.showFilter? true : this.isPivotDatagrid())) || false,
151
+ showFilters : this.isFilterable() && defaultBool(props.showFilters,(sData.showFilters? true : this.isPivotDatagrid())) || false,
126
152
  showFooters : defaultBool(props.showFooters,(sData.showFooters? true : false)),
127
153
  fixedTable : sData.fixedTable
128
154
  });
@@ -171,10 +197,15 @@ export default class CommonDatagridComponent extends AppComponent {
171
197
  sectionListDataSize : {value : {current : 0}},
172
198
  enablePointerEventsRef : {value : {current:false}},
173
199
  configureSectionListSelectedValues : {value : {}},
200
+ ///la liste des fonctions d'aggregations supportées
201
+ aggregatorFunctions : {value : extendAggreagatorFunctions(this.props.aggregatorFunctions)},
174
202
  ///les types d'affichage
175
203
  displayTypes : {value : hasFoundDisplayTypes ? disTypes : Object.clone(displayTypes)},
176
204
  sectionListColumnsSize : {value : {current:0}}, //la taille du nombre d'éléments de section dans les colonnes
177
205
  })
206
+ const sessionAggregator = defaultStr(this.getSessionData("aggregatorFunction")).trim();
207
+ const aggregatorProps = defaultStr(this.props.aggregatorFunction).trim();
208
+ this.state.aggregatorFunction= aggregatorProps && isValidAggregator(this.aggregatorFunctions[aggregatorProps]) ? aggregatorProps ? isNonNullString(sessionAggregator) && isValidAggregator(this.aggregatorFunctions[sessionAggregator]) : sessionAggregator : Object.keys(this.aggregatorFunctions)[0];
178
209
  this.isLoading = this.isLoading.bind(this);
179
210
  this.getProgressBar = this.getProgressBar.bind(this);
180
211
  this.sortRef.current.dir = defaultStr(this.sortRef.current.dir,this.sortRef.current.column == "date"?"desc":'asc')
@@ -204,12 +235,17 @@ export default class CommonDatagridComponent extends AppComponent {
204
235
  this.state.filteredColumns = defaultObj(this.getSessionData("filteredColumns"+this.getSessionNameKey()),this.props.filters);
205
236
  this.filtersSelectors = {selector:this.getFilters()};
206
237
  const {sectionListColumns} = this.prepareColumns();
238
+ this.state.sectionListColumns = sectionListColumns;
207
239
  if(this.canHandleColumnResize()){
208
240
  this.state.columnsWidths = this.preparedColumns.widths;
209
241
  }
210
- this.state.chartConfig = defaultObj(this.props.chartConfig,this.getSessionData("chartConfig"));
242
+ this.state.chartConfig = extendObj({},this.getSessionData("chartConfig"),this.props.chartConfig);
211
243
  const dType = defaultStr(this.props.displayType,this.getSessionData("displayType"),"table");
212
244
  this.state.displayType = this.displayTypes[dType] ? this.displayTypes[dType].code : "table" in this.displayTypes ? "table" : Object.keys(this.displayTypes)[0]?.code;
245
+ this.state.displayOnlySectionListHeaders = defaultBool(this.getSessionData("displayOnlySectionListHeaders"),this.props.displayOnlySectionListHeaders,false)
246
+ if(this.state.displayOnlySectionListHeaders){
247
+ this.state.showFooters = true;
248
+ }
213
249
  extendObj(this.state,this.prepareData({data}));
214
250
  const {width:windowWidth,height:windowHeight} = Dimensions.get("window");
215
251
  this.state.layout = {
@@ -254,8 +290,6 @@ export default class CommonDatagridComponent extends AppComponent {
254
290
  } else {
255
291
  this.currentDataSources = Object.toArray(this.currentDataSources);
256
292
  }
257
- this.state.sectionListColumns = sectionListColumns;
258
- this.state.displayOnlySectionListHeaders = defaultBool(this.getSessionData("displayOnlySectionListHeaders"),this.props.displayOnlySectionListHeaders,false)
259
293
  }
260
294
 
261
295
  /*** si une ligne peut être selectionable */
@@ -606,7 +640,7 @@ export default class CommonDatagridComponent extends AppComponent {
606
640
  }
607
641
  }
608
642
  }
609
- let footers = this.getFooterFields(true);
643
+ const footers = this.getFootersFields(true);
610
644
  let isAccordion = this.isAccordion();
611
645
  Object.mapToArray(columns,(headerCol1,headerIndex)=>{
612
646
  if(!isObj(headerCol1)) return;
@@ -614,6 +648,7 @@ export default class CommonDatagridComponent extends AppComponent {
614
648
  if(isAccordion && headerCol.accordion === false) return null;
615
649
  let header = {...headerCol};
616
650
  header.field = defaultStr(header.field, headerIndex)
651
+ header.type = defaultStr(header.jsType,header.type,"text").toLowerCase();
617
652
  /**** pour ignorer une colonne du datagrid, il suffit de passer le paramètre datagrid à false */
618
653
  if(!isNonNullString(header.field) || header.datagrid === false) {
619
654
  return;
@@ -624,7 +659,7 @@ export default class CommonDatagridComponent extends AppComponent {
624
659
  this.state.columns[header.field] = header;
625
660
  /*** les pieds de pages sont les données de type decimal, où qu'on peut compter */
626
661
  if(header.footer !== false && ((arrayValueExists(['decimal','number','money'],header.type) && header.format) || header.format == 'money' || header.format =='number')){
627
- footers[header.field] = header;
662
+ footers[header.field] = Object.clone(header);
628
663
  }
629
664
  if(!this.hasColumnsHalreadyInitialized){
630
665
  this.initColumnsCallback({...header,colIndex,columnField:header.field});
@@ -632,12 +667,12 @@ export default class CommonDatagridComponent extends AppComponent {
632
667
  })
633
668
  return footers;
634
669
  }
635
- getFooterFields(init){
670
+ getFootersFields(init){
636
671
  this[this.footerFieldName] = init === true ? {} : defaultObj(this[this.footerFieldName]);
637
672
  return this[this.footerFieldName];
638
673
  }
639
- hasFooterFields(){
640
- return Object.size(this.getFooterFields(),true) ? true : false;
674
+ hasFootersFields(){
675
+ return Object.size(this.getFootersFields(),true) ? true : false;
641
676
  }
642
677
  getActionsArgs(selected){
643
678
  const r = isObj(selected)? selected : {};
@@ -948,7 +983,7 @@ export default class CommonDatagridComponent extends AppComponent {
948
983
  this.isUpdating = true;
949
984
  this.setState( {showFilters:true},()=>{
950
985
  this.isUpdating = false;
951
- this.setSessionData({showFilter:true});
986
+ this.setSessionData({showFilters:true});
952
987
  })
953
988
  }
954
989
  hideFilters (){
@@ -959,12 +994,12 @@ export default class CommonDatagridComponent extends AppComponent {
959
994
  if(this.isUpdating) return false;
960
995
  this.setState({showFilters:false},()=>{
961
996
  this.isUpdating = false;
962
- this.setSessionData({showFilter:false})
997
+ this.setSessionData({showFilters:false})
963
998
  })
964
999
  }
965
1000
 
966
1001
  toggleFooters(showOrHide){
967
- if(typeof showOrHide !=='boolean' || this.state.showFooters === showOrHide) return;
1002
+ if(typeof showOrHide !=='boolean' || this.canShowFooters() === showOrHide) return;
968
1003
  if(!this._isMounted()) {
969
1004
  this.isUpdating = false;
970
1005
  return;
@@ -1030,7 +1065,7 @@ export default class CommonDatagridComponent extends AppComponent {
1030
1065
  if(!isNonNullString(field)) return;
1031
1066
  let columns = {...this.state.columns};
1032
1067
  columns[field].visible = !columns[field].visible;
1033
- let footers = this.getFooterFields();
1068
+ let footers = this.getFootersFields();
1034
1069
  if(isObj(footers[field])){
1035
1070
  footers[field].visible = columns[field].visible;
1036
1071
  }
@@ -1039,6 +1074,10 @@ export default class CommonDatagridComponent extends AppComponent {
1039
1074
  if(removeFocus) document.body.click();
1040
1075
  });
1041
1076
  }
1077
+ /****le nombre maximum de courbes supportées */
1078
+ getMaxSeriesSize(){
1079
+ return getMaxSupportedSeriesSize();
1080
+ }
1042
1081
  prepareFilter(props,filteredColumns){
1043
1082
  filteredColumns.push(props);
1044
1083
  }
@@ -1064,7 +1103,7 @@ export default class CommonDatagridComponent extends AppComponent {
1064
1103
  /*** configure la */
1065
1104
  configureSectionListColumn(column,toggleSectionList){
1066
1105
  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")
1106
+ const format = defaultStr(defaultObj(this.configureSectionListSelectedValues[column.field]).format,column.format,"dd/mm/yyyy")
1068
1107
  return new Promise((resolve,reject)=>{
1069
1108
  DialogProvider.open({
1070
1109
  title : 'Format de date',
@@ -1126,19 +1165,20 @@ export default class CommonDatagridComponent extends AppComponent {
1126
1165
  const {sectionListColumns} = this.prepareColumns({sectionListColumns:{}});
1127
1166
  this.setIsLoading(true,()=>{
1128
1167
  this.prepareData({data:this.INITIAL_STATE.data,sectionListColumns},(state)=>{
1129
- this.setState({...state,sectionListColumns},()=>{
1168
+ this.setState({...state,sectionListColumns,displayOnlySectionListHeaders:false},()=>{
1130
1169
  this.setIsLoading(false,false);
1131
1170
  this.setSessionData("sectionListColumns",null);
1171
+ this.setSessionData("displayOnlySectionListHeaders",false);
1132
1172
  });
1133
1173
  });
1134
1174
  },true);
1135
1175
  }
1136
1176
  canDisplayOnlySectionListHeaders(){
1137
- return this.hasFooterFields() && this.isSectionList() && this.hasSectionListData();
1177
+ return this.hasFootersFields() && this.isSectionList() && this.hasSectionListData();
1138
1178
  }
1139
1179
  /*** si l'on peut rendre le contenu de type graphique */
1140
1180
  isChartRendable(){
1141
- return !this.isPivotDatagrid() && this.hasFooterFields();
1181
+ return !this.isPivotDatagrid() && this.hasFootersFields();
1142
1182
  }
1143
1183
  isValidChartConfig(config){
1144
1184
  config = defaultObj(config,this.state.chartConfig);
@@ -1165,12 +1205,14 @@ export default class CommonDatagridComponent extends AppComponent {
1165
1205
  },true)
1166
1206
  } else {
1167
1207
  const cb = (chartConfig)=>{
1168
- this.setIsLoading(true,()=>{
1169
- this.setState({chartConfig,displayType},()=>{
1170
- this.setIsLoading(false,false);
1171
- this.persistDisplayType(displayType);
1172
- })
1173
- },true);
1208
+ setTimeout(()=>{
1209
+ this.setIsLoading(true,()=>{
1210
+ this.setState({chartConfig,displayType},()=>{
1211
+ this.setIsLoading(false,false);
1212
+ this.persistDisplayType(displayType);
1213
+ })
1214
+ },true);
1215
+ },200);
1174
1216
  }
1175
1217
  if(!this.isValidChartConfig()){
1176
1218
  return this.configureChart(false).then((chartConfig)=>{
@@ -1180,21 +1222,79 @@ export default class CommonDatagridComponent extends AppComponent {
1180
1222
  cb({...this.state.chartConfig});
1181
1223
  }
1182
1224
  }
1225
+ getActiveAggregatorFunction(){
1226
+ if(isNonNullString(this.state.aggregatorFunction) && this.aggregatorFunctions[this.state.aggregatorFunction]){
1227
+ return this.aggregatorFunctions[this.state.aggregatorFunction]
1228
+ }
1229
+ return this.aggregatorFunctions[Object.keys(this.aggregatorFunctions)[0]];
1230
+ }
1231
+ /**** récupère l'item de menu permettant lié à la sélection de la fonction d'aggggrégation */
1232
+ getAggregatorFunctionsMenuItems(withDivider){
1233
+ if(!this.hasFootersFields()) return [];
1234
+ const m = [];
1235
+ const aggregatorFunction = this.getActiveAggregatorFunction().code;
1236
+ Object.map(this.aggregatorFunctions,(ag)=>{
1237
+ const active = ag.code == aggregatorFunction;
1238
+ m.push({
1239
+ ...ag,
1240
+ icon : active?"check":null,
1241
+ onPress : active ? undefined : ()=>{
1242
+ this.toggleActiveAggregatorFunction(ag);
1243
+ }
1244
+ })
1245
+ });
1246
+ if(m.length){
1247
+ m.unshift({
1248
+ text : "Fonctions d'aggrégation",
1249
+ icon : "material-functions",
1250
+ divider : true,
1251
+ });
1252
+ if(withDivider !== false){
1253
+ m.unshift({divider:true});
1254
+ }
1255
+ if(withDivider !== false){
1256
+ m[m.length-1].divider = true;
1257
+ }
1258
+ }
1259
+ return m;
1260
+ }
1261
+ toggleActiveAggregatorFunction(ag){
1262
+ if(!isValidAggregator(ag) || ag.code == this.state.aggregatorFunction) return null;
1263
+ setTimeout(()=>{
1264
+ this.setIsLoading(true,()=>{
1265
+ this.prepareData({data:this.INITIAL_STATE.data},(state)=>{
1266
+ this.setState({...state,aggregatorFunction:ag.code},()=>{
1267
+ this.setSessionData("aggregatorFunction",ag.code);
1268
+ this.setIsLoading(false,false);
1269
+ })
1270
+ })
1271
+ },true);
1272
+ },200);
1273
+ }
1274
+ renderAggregatorFunctionsMenu(){
1275
+ const m = this.getAggregatorFunctionsMenuItems(false,false);
1276
+ if(!m.length) return null;
1277
+ return <Menu
1278
+ items = {m}
1279
+ title = "Fonctions d'aggrégations. Veuillez sélectionner la fonction à utiliser par défaut pour la totalisation des données des colonnes de type nombre"
1280
+ anchor = {(p)=><Icon name="material-functions" {...p}/>}
1281
+ />
1282
+ }
1183
1283
  configureChart(refreshChart){
1184
1284
  if(!this.isChartRendable()){
1185
1285
  return Promise.reject({message:'Impossible de configurer le graphe car le type de données ne permet pas de rendu de type graphe'});
1186
1286
  }
1187
1287
  const xItems = {},yItems = {},config = defaultObj(this.state.chartConfig);
1188
1288
  const series = {};
1189
- let hasSeries = false;
1190
1289
  const isValidConfig = this.isValidChartConfig();
1191
1290
  Object.map(this.state.columns,(field,f)=>{
1192
1291
  if(isObj(field) && !this.isSelectableColumn(field) && !this.isIndexColumn(field)){
1193
- xItems[f] = field;
1194
1292
  const type = defaultStr(field.jsType,field.type).toLowerCase();
1195
1293
  if(type === 'number' || type=='decimal'){
1196
1294
  yItems[f] = field;
1197
1295
  series[f] = field;
1296
+ } else {
1297
+ xItems[f] = field;
1198
1298
  }
1199
1299
  }
1200
1300
  });
@@ -1215,6 +1315,7 @@ export default class CommonDatagridComponent extends AppComponent {
1215
1315
  DialogProvider.open({
1216
1316
  title : 'Configuration des graphes',
1217
1317
  subtitle : false,
1318
+ data : this.getCharConfig(),
1218
1319
  fields : {
1219
1320
  x : {
1220
1321
  text : 'Axe des x[horizontal]',
@@ -1232,20 +1333,33 @@ export default class CommonDatagridComponent extends AppComponent {
1232
1333
  defaultValue : config.y,
1233
1334
  onValidatorValid,
1234
1335
  },
1235
- series : hasSeries && {
1336
+ series : {
1236
1337
  text : 'Series',
1237
1338
  type : "select",
1238
1339
  items : series,
1239
1340
  multiple : true,
1240
1341
  },
1241
- aggregatorFunction : {
1242
- type : 'select',
1243
- text : "Foncton d'aggrégation",
1244
- required : true,
1245
- multiple : false,
1246
- defaultValue : "sum",
1247
- items : aggregatorFunctions,
1248
- }
1342
+ sectionListHeadersSeries : {
1343
+ text : "Series des données groupées",
1344
+ type : "select",
1345
+ items : yItems,
1346
+ multiple : true,
1347
+ },
1348
+ stacked : {
1349
+ type : 'switch',
1350
+ text : "Graphe empilé?",
1351
+ disabled : this.state.displayType !== "barChart",
1352
+ checkedValue : true,
1353
+ uncheckedValue : false,
1354
+ },
1355
+ title : {
1356
+ text : "Titre du graphe",
1357
+ format :'hashtag',
1358
+ },
1359
+ titleColor: {
1360
+ text : "Couleur de titre",
1361
+ type :"color",
1362
+ },
1249
1363
  },
1250
1364
  actions : [
1251
1365
  {
@@ -1270,15 +1384,24 @@ export default class CommonDatagridComponent extends AppComponent {
1270
1384
  getCharConfig(){
1271
1385
  return defaultObj(this.state.chartConfig);
1272
1386
  }
1387
+ getChartIsRendableArgs(){
1388
+ return {
1389
+ context:this,isSectionList:this.isSectionList(),hasSectionListData:this.hasSectionListData(),
1390
+ data : this.state.data,
1391
+ displayOnlySectionListHeaders : this.canDisplayOnlySectionListHeaders(),
1392
+ };
1393
+ }
1273
1394
  renderDisplayTypes(){
1274
1395
  const m = [];
1275
1396
  let activeType = null,hasFoundChart = false,hasFoundTable = false;
1276
1397
  const hasConfig = this.isValidChartConfig();
1277
1398
  Object.map(this.displayTypes,(type,k)=>{
1399
+ let isChartDisabled = false;
1278
1400
  if(type.isChart === true ) {
1279
1401
  if(!this.isChartRendable()){
1280
1402
  return null;
1281
1403
  }
1404
+ isChartDisabled = typeof type.isRendable =='function' && type.isRendable(this.getChartIsRendableArgs()) === false ? true : false;
1282
1405
  if(!hasFoundChart){
1283
1406
  if(hasFoundTable){
1284
1407
  m.push({divider:true});
@@ -1288,7 +1411,16 @@ export default class CommonDatagridComponent extends AppComponent {
1288
1411
  divider : true,
1289
1412
  text : "Configurer les graphes",
1290
1413
  icon :"material-settings",
1291
- onPress : this.configureChart.bind(this)
1414
+ onPress : ()=>{
1415
+ this.configureChart(false).then((chartConfig)=>{
1416
+ this.setIsLoading(true,()=>{
1417
+ this.setState({chartConfig},()=>{
1418
+ this.setSessionData("chartConfig",chartConfig);
1419
+ this.setIsLoading(false,false);
1420
+ });
1421
+ },true)
1422
+ })
1423
+ }
1292
1424
  });
1293
1425
  }
1294
1426
  } else if(k === 'table'){
@@ -1304,7 +1436,7 @@ export default class CommonDatagridComponent extends AppComponent {
1304
1436
  right : <>
1305
1437
  {active ? <Icon color={theme.colors.primaryOnSurface} name="check"/>: null}
1306
1438
  </>,
1307
- disabled : type.isChart && !hasConfig ? true : undefined,
1439
+ disabled : isChartDisabled || type.isChart && !hasConfig ? true : undefined,
1308
1440
  onPress:()=>{
1309
1441
  this.setDisplayType(type);
1310
1442
  }
@@ -1329,105 +1461,242 @@ export default class CommonDatagridComponent extends AppComponent {
1329
1461
  getEmptyDataValue(){
1330
1462
  return "N/A";
1331
1463
  }
1464
+ /*** retourne les sectionHeaderSeries par défautt */
1465
+ getDefaultSectionHeadersSeries(){
1466
+ if(!this.isSectionList() || !this.hasFootersFields()) return [];
1467
+ const footersFields = this.getFootersFields();
1468
+ const r = [],max = this.getMaxSeriesSize();
1469
+ let counter = 0;
1470
+ for(let i in footersFields){
1471
+ const footer = footersFields[i];
1472
+ if(!isObj(footer)) continue;
1473
+ if(counter >= max) break;
1474
+ r.push(i);
1475
+ }
1476
+ return r;
1477
+ }
1478
+ formatChartDataValue({value,column,options}){
1479
+ return value;
1480
+ }
1481
+ /*****
1482
+ * retourne les paramètres à passer au chart, lorsque les données sont groupées, ie les sectionsListColumns est non nulle
1483
+ * @param {object} de la forme suivante :
1484
+ * @param {object} chartType le type de chart, l'un des types du tableau displayTypes en haut du présent fichier
1485
+ * @param {object} yAxisColumn la colonne de l'axe vertical y
1486
+ * @param {object} xAxisColumn la colonne de l'axe des x de la courbe, pris dans les configurations du chart, chartConfig
1487
+ * @param {object} la fonction d'aggrégation, l'une des fonctions issues des fonctions d'aggrégations aggregatorsFuncions, @see : dans $components/Datagrid/Footer
1488
+ * en affichage des tableaux de type sectionList, seul les colonnes de totalisation sont utilisées pour l'affichage du graphe
1489
+ * Le nombre de graphes (series) à afficher est valable pour tous les graphes sauf les graphes de type pie/donut.
1490
+ * il est récupéré dans la variable chartConfig des configuration du chart, où par défaut le nombre de colonnes de totalisation des tableau (inférieur au nombre maximum de courbes surpportées par appexchart)
1491
+ */
1492
+ getSectionListHeadersChartOptions({chartType,yAxisColumn,xAxisColumn,aggregatorFunction}){
1493
+ if(!this.isSectionList()) return null;
1494
+ if(!isObj(chartType) || !isObj(yAxisColumn) || !yAxisColumn.field) return null;
1495
+ if(!isValidAggregator(aggregatorFunction)){
1496
+ aggregatorFunction = this.getActiveAggregatorFunction();
1497
+ }
1498
+ const code = aggregatorFunction.code;
1499
+ const isDonut = chartType.isDonut || chartType.isRadial;
1500
+ const config = this.getCharConfig();
1501
+ //@see : https://apexcharts.com/docs/series/
1502
+ ///lorsqu'on affiche uniquement les totaux des sections, alors la visualition se fait sur uniquement sur la base des valeurs
1503
+ ///on parcoure uniquement les entêtes des sectionLis
1504
+ const dataIndexes={},dataInexesNames = {};
1505
+ //la variable sectionListHeaderSeries, permet de récupérer les colonnes de type montant à utiliser pour le rendu du chart
1506
+ let serieName = "";
1507
+ const tableFooters = this.getFootersFields();
1508
+ const defaultSectionListHeadersSeries = this.getDefaultSectionHeadersSeries();
1509
+ let seriesConfig = isDonut ? [] : Array.isArray(config.sectionListHeadersSeries) && config.sectionListHeadersSeries.length ? config.sectionListHeadersSeries : [];
1510
+ if(!isDonut){
1511
+ if(seriesConfig.length){
1512
+ const ss = [];
1513
+ seriesConfig.map((s)=>{
1514
+ if(!isNonNullString(s) || !tableFooters[s]) return null;
1515
+ ss.push(s);
1516
+ });
1517
+ seriesConfig = ss;
1518
+ }
1519
+ if(!seriesConfig.length){
1520
+ seriesConfig = defaultSectionListHeadersSeries;
1521
+ }
1522
+ }
1523
+ /**** boucle sur chaque éléments trouvée dans le tableau des données sectionListData */
1524
+ const loopForFooter = ({column,serieName,footers,header})=>{
1525
+ if(!isObj(column) || !isObj(footers)) return null;
1526
+ if(!isObj(footers[column.field])) return null;
1527
+ const footer = footers[column.field]
1528
+ if(typeof footer[code] !== 'number') return null;
1529
+ const label = defaultStr(footer.label,footer.text,footer.field);
1530
+ if(typeof footer[code] !== 'number') return null;
1531
+ if(isDonut){
1532
+ serieName = label;
1533
+ dataIndexes[header] = footer[code];
1534
+ } else {
1535
+ dataIndexes[serieName] = defaultArray(dataIndexes[serieName]);
1536
+ dataIndexes[serieName].push({x:header,y:footer[code]});
1537
+ }
1538
+ }
1539
+ Object.map(this.sectionListData,(data,header)=>{
1540
+ if(!isObj(this.sectionListHeaderFooters[header])) return null;
1541
+ const footers = this.sectionListHeaderFooters[header];
1542
+ if(isDonut){
1543
+ loopForFooter({footers,header,column:yAxisColumn})
1544
+ } else {
1545
+ seriesConfig.map((s)=>{
1546
+ const serie = this.state.columns[s];
1547
+ const serieName = defaultStr(s.label,s.text,s);
1548
+ loopForFooter({footers,serie,serieName,header,column:tableFooters[s]})
1549
+ })
1550
+ }
1551
+ });
1552
+ if(isDonut){
1553
+ const series = [],labels = [];
1554
+ Object.map(dataIndexes,(serie,index)=>{
1555
+ series.push(serie);
1556
+ labels.push(index);
1557
+ });
1558
+ return {
1559
+ series,
1560
+ labels,
1561
+ }
1562
+ } else {
1563
+ const series = [];
1564
+ Object.map(dataIndexes,(data,name)=>{
1565
+ series.push({name,data});
1566
+ });
1567
+ return {
1568
+ series
1569
+ }
1570
+ }
1571
+ }
1332
1572
  renderChart(){
1333
1573
  if(!this.canRenderChart()) return null;
1334
1574
  if(!this.isValidChartConfig()) return null;
1335
1575
  const chartType = displayTypes[this.state.displayType];
1336
1576
  if(!isObj(chartType) || !isNonNullString(chartType.type)) return null;
1577
+ if(typeof chartType.isRendable =='function' && chartType.isRendable(this.getChartIsRendableArgs()) === false) {
1578
+ console.warn("impossible d'afficher le graphe de type ",chartType.label," car le type de données requis pour le rendu de ce graphe est invalide")
1579
+ return null;
1580
+ }
1337
1581
  const config = this.getCharConfig();
1338
1582
  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();
1583
+ const yAxisColumn = this.state.columns[config.y];
1584
+ const type = defaultStr(yAxisColumn.jsType,yAxisColumn.type).toLowerCase();
1341
1585
  if(type !== 'number'&& type !== 'decimal') return null;
1342
1586
  const isEmptyY = config.x === this.emptySectionListHeaderValue;
1343
- const seriesConfig = Array.isArray(config.series) && config.series.length ? config.series : [yaxis.field];
1344
- let xaxis = null;
1587
+ let seriesConfig = Array.isArray(config.series) && config.series.length ? config.series : [yAxisColumn.field];
1588
+ const snConfig = [];
1589
+ Object.map(seriesConfig,(s,v)=>{
1590
+ if(!isNonNullString(s) || !this.state.columns[s]) return null;
1591
+ snConfig.push(s);
1592
+ });
1593
+ seriesConfig = snConfig.length> 0 ? snConfig : [yAxisColumn.field];
1594
+ let xAxisColumn = null;
1345
1595
  if(!isEmptyY){
1346
1596
  if(!this.state.columns[config.x]){
1347
1597
  return null;
1348
1598
  }
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;
1599
+ xAxisColumn = this.state.columns[config.x];
1359
1600
  }
1601
+ const aggregatorFunction = this.getActiveAggregatorFunction().eval;
1360
1602
  const emptyValue = this.getEmptyDataValue();
1361
- const indexes = {}
1603
+ const indexes = {};
1604
+ let series = [],xaxis = {},customConfig = {};
1362
1605
  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,
1606
+ if(!this.isSectionList()){
1607
+ this.state.data.map((data,index)=>{
1608
+ if(!isObj(data))return null;
1609
+ const txt = this.renderRowCell({
1610
+ data,
1611
+ rowData : data,
1612
+ rowCounterIndex : index,
1613
+ rowIndex : index,
1614
+ columnDef : xAxisColumn,
1615
+ renderRowCell : false,
1616
+ columnField : xAxisColumn.field,
1617
+ });
1618
+ const text = isNonNullString(txt)? txt : emptyValue;
1619
+ Object.map(seriesConfig,(s,v)=>{
1620
+ if(!isNonNullString(s) || !this.state.columns[s]) return null;
1621
+ const columnDef = this.state.columns[s];
1622
+ if(!isObj(columnDef)) return null;
1623
+ indexes[s] = defaultObj(indexes[s]);
1624
+ const current = indexes[s];
1625
+ current[text] = typeof current[text] =="number"? current[text] : 0;
1626
+ const value = getFooterColumnValue({data,columnDef,columnField:columnDef.field});
1627
+ const rArgs = aggregatorFunction =='average'?{sum:current[text]+value,count:count+1} : {};
1628
+ current[text] = aggregatorFunction({value,total:current[text],data,columnDef,columnField:columnDef.field,count,...rArgs});
1629
+ });
1373
1630
  });
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,
1631
+ Object.map(indexes,(values,serieName)=>{
1632
+ const col = this.state.columns[serieName];
1633
+ const name = defaultStr(col?.label,col?.text,serieName),data = [];
1634
+ Object.map(values,(v,i)=>{
1635
+ data.push({
1636
+ x : i,
1637
+ y : v,
1638
+ })
1639
+ })
1640
+ series.push({
1641
+ name,
1642
+ type : chartType.type,
1643
+ data,
1394
1644
  })
1395
1645
  })
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;
1646
+ } else {
1647
+ const configs = this.getSectionListHeadersChartOptions({chartType,aggregatorFunction,xAxisColumn,yAxisColumn});
1648
+ if(isObj(configs)){
1649
+ const {series :customSeries,xaxis:customXaxis,...rest} = configs;
1650
+ series = Array.isArray(customSeries) ? customSeries : series;
1651
+ xaxis = isObj(customXaxis)? customXaxis : xaxis;
1652
+ customConfig = rest;
1409
1653
  } else {
1410
- maxHeight = diff;
1654
+ return null;
1411
1655
  }
1412
1656
  }
1413
- const chartProps = defaultObj(chartProps);
1657
+ customConfig = defaultObj(customConfig);
1658
+ customConfig.chart = defaultObj(customConfig.chart);
1659
+ const chartProps = extendObj(true,{},this.props.chartProps,customConfig);
1660
+ if(chartType.isBar){
1661
+ chartProps.chart.stacked = !!config.stacked;
1662
+ } else chartProps.chart.stacked = undefined;
1414
1663
  return <Chart
1415
1664
  options = {{
1416
- ...defaultObj(chartProps.options),
1665
+ ...chartProps,
1666
+ title : {
1667
+ text: defaultStr(config.title,chartProps.title),
1668
+ align: 'left',
1669
+ //margin: 10,
1670
+ //offsetX: 0,
1671
+ //offsetY: 0,
1672
+ //floating: false,
1673
+ style: {
1674
+ //fontSize: '14px',
1675
+ //fontWeight: 'bold',
1676
+ //fontFamily: undefined,
1677
+ color: theme.Colors.isValid(config.titleColor)?config.titleColor : undefined,
1678
+ },
1679
+ },
1417
1680
  series,
1418
1681
  chart : {
1419
1682
  height :350,
1420
- maxHeight,
1421
1683
  ...defaultObj(chartProps.chart),
1422
1684
  type : chartType.type,
1423
1685
  },
1424
1686
  xaxis: {
1425
1687
  ...defaultObj(chartProps.xaxis),
1426
- type: 'category'
1688
+ type: 'category',
1689
+ ...defaultObj(xaxis)
1427
1690
  }
1428
1691
  }}
1429
1692
  />
1430
1693
  }
1694
+ canShowFooters(){
1695
+ return this.state.showFooters || this.hasSectionListData() && this.state.displayOnlySectionListHeaders;
1696
+ }
1697
+ canShowFilters(){
1698
+ return this.state.showFilters
1699
+ }
1431
1700
  toggleDisplayOnlySectionListHeaders(){
1432
1701
  if(!this.canDisplayOnlySectionListHeaders()) return
1433
1702
  setTimeout(()=>{
@@ -1532,7 +1801,6 @@ export default class CommonDatagridComponent extends AppComponent {
1532
1801
  } = header;
1533
1802
  restCol = Object.clone(defaultObj(restCol));
1534
1803
  let colFilter = defaultVal(restCol.filter,true);
1535
- const format = defaultStr(restCol.format).toLowerCase();
1536
1804
  field = header.field = defaultStr(header.field,field,headerIndex);
1537
1805
  delete restCol.filter;
1538
1806
 
@@ -1588,9 +1856,13 @@ export default class CommonDatagridComponent extends AppComponent {
1588
1856
  sortedColumn.header = header;
1589
1857
  sortedColumn.label = restCol.label;
1590
1858
  const isDesc = currentSortedColumn.dir === "desc";
1859
+ const sortDir = isDesc ? "descending" : "ascending";
1591
1860
  const prefix = (sortType =='number' || sortType == 'decimal') ? "numeric" : sortType =='boolean'?'bool' : sortType.contains('date') ? 'calendar': sortType =='time'? 'clock' : 'alphabetical';
1592
- sortedColumn.icon = 'sort-'+prefix+'-'+(isDesc ? "descending" : "ascending");
1861
+ sortedColumn.icon = 'sort-'+prefix+'-'+sortDir;
1593
1862
  sortedColumn.title = (isDesc ? "Trié par ordre décroissant":"Trié par ordre croissant ")+ " du champ ["+restCol.label+"]";
1863
+ if(sortType.contains('date') || sortType.contains("time")){
1864
+ sortedColumn.title = "le champ {0} est actuellement trié du plus {1} au plus {2}".sprintf(restCol.label,isDesc?"récent":"ancien",isDesc?"ancien":"récent")
1865
+ }
1594
1866
  }
1595
1867
  sortedColumns[field] = restCol.label;
1596
1868
  sortedColumnsLength++;
@@ -1660,15 +1932,15 @@ export default class CommonDatagridComponent extends AppComponent {
1660
1932
  ...header,
1661
1933
  width,
1662
1934
  type,
1935
+ ...sListColumns[field],
1663
1936
  ...defaultObj(this.configureSectionListSelectedValues[field]),
1664
- ...sListColumns[field],
1665
1937
  };///les colonnes de sections
1666
1938
  this.sectionListColumnsSize.current++;
1667
1939
  }
1668
1940
  const mItem = {
1941
+ ...header,
1669
1942
  field,
1670
1943
  type,
1671
- format,
1672
1944
  onPress : ()=>{
1673
1945
  this.toggleColumnInSectionList(field);
1674
1946
  return false;
@@ -1679,9 +1951,9 @@ export default class CommonDatagridComponent extends AppComponent {
1679
1951
  if(this.isSectionListColumnConfigurable(mItem)){
1680
1952
  mItem.right = (p)=>{
1681
1953
  return <Icon name="material-settings" {...p} onPress={(e)=>{
1682
- React.stopEventPropagation(e);
1954
+ //React.stopEventPropagation(e);
1683
1955
  this.configureSectionListColumn(mItem);
1684
- return false;
1956
+ //return false;
1685
1957
  }}/>
1686
1958
  }
1687
1959
  }
@@ -1748,11 +2020,25 @@ export default class CommonDatagridComponent extends AppComponent {
1748
2020
  let isArr = Array.isArray(data);
1749
2021
  //let push = (d,index) => isArr ? newData.push(d) : newData[index] = d;
1750
2022
  const hasLocalFilter = this.props.filters !== false && this.hasLocalFilters;
1751
- let footersColumns = this.getFooterFields(),hasFooterFields = this.hasFooterFields();
1752
- const canUpdateFooters = !!(updateFooters !== false && hasFooterFields);
2023
+ const footersColumns = this.getFootersFields(),hasFootersFields = this.hasFootersFields();
2024
+ const canUpdateFooters = !!(updateFooters !== false && hasFootersFields);
1753
2025
  this.hasFoundSectionData.current = false;
1754
2026
  this.sectionListDataSize.current = 0;
1755
2027
  const isSList = this.isSectionList(sectionListColumns);
2028
+ if(this.canAutoSort() && isNonNullString(this.sortRef.current.column)){
2029
+ if(isObj(this.state.columns) && this.state.columns[this.sortRef.current.column]){
2030
+ let field = this.state.columns[this.sortRef.current.column];
2031
+ const sortConfig = Object.assign({},this.sortRef.current);
2032
+ sortConfig.getItem = (item,columnName,{getItem})=>{
2033
+ if(isObj(item) && (field.type =='decimal' || field.type =="number")){
2034
+ const v = item[columnName];
2035
+ return isDecimal(v)? v : isNonNullString(v)? parseDecimal(v) : 0;
2036
+ }
2037
+ return getItem(item,columnName);
2038
+ }
2039
+ data = sortBy(data,sortConfig);//on trie tout d'abord les données
2040
+ }
2041
+ }
1756
2042
  if(hasLocalFilter || !isArr || canUpdateFooters || isSList) {
1757
2043
  if(canUpdateFooters){
1758
2044
  this.___evaluatedFootersValues = {}
@@ -1765,10 +2051,12 @@ export default class CommonDatagridComponent extends AppComponent {
1765
2051
  Object.map(this.sectionListData,(v,i)=>{
1766
2052
  delete this.sectionListData[i];
1767
2053
  });
1768
- //on réinnitialise tous les footes
1769
- Object.map(this.sectionListHeaderFooters,(v,i)=>{
1770
- delete this.sectionListHeaderFooters[i];
1771
- })
2054
+ if(canUpdateFooters){
2055
+ //on réinnitialise tous les footes
2056
+ Object.map(this.sectionListHeaderFooters,(v,i)=>{
2057
+ delete this.sectionListHeaderFooters[i];
2058
+ })
2059
+ }
1772
2060
  }
1773
2061
  let currentSectionListFooter = null;
1774
2062
  const sectionListData = this.sectionListData;//l'ensemble des données de sectionList
@@ -1791,6 +2079,7 @@ export default class CommonDatagridComponent extends AppComponent {
1791
2079
  }
1792
2080
  sectionListData[r].push(d);
1793
2081
  if(canUpdateFooters){
2082
+ ///garde pour chaque éléments de groue, la valeur des champs de son footer
1794
2083
  this.sectionListHeaderFooters[r] = defaultObj(this.sectionListHeaderFooters[r]);
1795
2084
  currentSectionListFooter = this.sectionListHeaderFooters[r];
1796
2085
  }
@@ -1802,7 +2091,7 @@ export default class CommonDatagridComponent extends AppComponent {
1802
2091
  result.push(currentSectionListFooter);
1803
2092
  }
1804
2093
  Object.map(footersColumns,(columnDef,field)=>{
1805
- evalSingleValue({data:d,columnDef,field,result,displayLabel:false})
2094
+ evalSingleValue({data:d,aggregatorFunction:this.getActiveAggregatorFunction(),aggregatorFunctions:this.aggregatorFunctions,columnDef,field,result,displayLabel:false})
1806
2095
  });
1807
2096
  }
1808
2097
  newData.push(d);
@@ -1810,20 +2099,6 @@ export default class CommonDatagridComponent extends AppComponent {
1810
2099
  });
1811
2100
  data = newData;
1812
2101
  }
1813
- if(this.canAutoSort() && isNonNullString(this.sortRef.current.column)){
1814
- if(isObj(this.state.columns) && this.state.columns[this.sortRef.current.column]){
1815
- let field = this.state.columns[this.sortRef.current.column];
1816
- const sortConfig = Object.assign({},this.sortRef.current);
1817
- sortConfig.getItem = (item,columnName,{getItem})=>{
1818
- if(isObj(item) && (field.type =='decimal' || field.type =="number")){
1819
- const v = item[columnName];
1820
- return isDecimal(v)? v : isNonNullString(v)? parseDecimal(v) : 0;
1821
- }
1822
- return getItem(item,columnName);
1823
- }
1824
- data = sortBy(data,sortConfig);//on trie tout d'abord les données
1825
- }
1826
- }
1827
2102
  this.INITIAL_STATE.data = data;
1828
2103
  if(this.hasFoundSectionData.current){
1829
2104
  data = [];
@@ -1905,7 +2180,7 @@ export default class CommonDatagridComponent extends AppComponent {
1905
2180
  }
1906
2181
  }
1907
2182
  let cells = null;
1908
- if(this.state.showFooters && isObj(this.sectionListHeaderFooters[key])){
2183
+ if(this.canShowFooters() && isObj(this.sectionListHeaderFooters[key])){
1909
2184
  const {visibleColumnsNames,widths} = defaultObj(this.preparedColumns);
1910
2185
  if(isObj(visibleColumnsNames) &&isObj(widths)){
1911
2186
  cells = [];
@@ -1927,6 +2202,8 @@ export default class CommonDatagridComponent extends AppComponent {
1927
2202
  key = {key}
1928
2203
  testID={testID+"_FooterItem_"+key}
1929
2204
  {...footer}
2205
+ aggregatorFunction = {this.getActiveAggregatorFunction().code}
2206
+ aggregatorFunctions = {this.aggregatorFunctions}
1930
2207
  displayLabel = {false}
1931
2208
  //anchorProps = {{style:[theme.styles.ph1,theme.styles.mh05]}}
1932
2209
  />
@@ -2258,10 +2535,18 @@ export default class CommonDatagridComponent extends AppComponent {
2258
2535
  getMaxSelectedRows(){
2259
2536
  return 0;
2260
2537
  }
2538
+ ///retourne la longueur réelle du nombre d'éléments du tableau en excluant les valerus d'entêtes de section
2539
+ getStateDataSize(includeSectionListDataSize){
2540
+ const dSize = defaultNumber(this.state.data.length);
2541
+ if(!dSize) return 0;
2542
+ if(includeSectionListDataSize === false) return dSize;
2543
+ return Math.max(dSize - this.getSectionListDataSize(),0);
2544
+ }
2261
2545
  getMaxSelectableRows(){
2262
2546
  let max = this.getMaxSelectedRows();
2263
- if(this.state.data && typeof this.state.data.length ==='number'){
2264
- max = max ? Math.min(max,this.state.data.length,max) : this.state.data.length;
2547
+ const dataSize = this.getStateDataSize();
2548
+ if(dataSize){
2549
+ max = max ? Math.min(max,dataSize,max) : dataSize;
2265
2550
  }
2266
2551
  return Math.max(max-this.getSectionListDataSize(),0);
2267
2552
  }
@@ -2865,10 +3150,14 @@ CommonDatagridComponent.propTypes = {
2865
3150
  x : PropTypes.string.isRequired, //l'axe horizontal
2866
3151
  y : PropTypes.string.isRequired, //l'axe des y, les colonnes de type nombre
2867
3152
  series : PropTypes.arrayOf([PropTypes.string]), //les séries, le nombre de courbe a afficher sur le graphe, en fonction du type
3153
+ /**** les series à utiliser pour l'affichage des données lorsque les colonnes sont groupées, ie les montant de totalisation sont utilisés */
3154
+ sectionListHeadersSeries : PropTypes.arrayOf([PropTypes.string]),
2868
3155
  }),
2869
3156
  displayType : chartDisplayType,
2870
3157
  /*** les types d'afichates supportés par l'application */
2871
- displayTypes : PropTypes.arrayOf(chartDisplayType)
3158
+ displayTypes : PropTypes.arrayOf(chartDisplayType),
3159
+ /***le code de la fonction d'aggregation à utilier par défaut, dans la liste des fonctions d'aggrégations du composant */
3160
+ aggregatorFunction : PropTypes.string,
2872
3161
  }
2873
3162
 
2874
3163
  const styles = StyleSheet.create({