@fto-consult/expo-ui 2.21.3 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/appConfig.txt ADDED
@@ -0,0 +1,11 @@
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
+ /**** l'ENSEMBLE DES CONFIGURATIONS SUPPORTES, fichier appConfig***/
6
+
7
+ {
8
+
9
+ /*** le nombre maximal de courbes qu'on peut afficher sur le même graphe***/
10
+ maxSupportedChartSeries {number}
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fto-consult/expo-ui",
3
- "version": "2.21.3",
3
+ "version": "2.22.0",
4
4
  "description": "Bibliothèque de composants UI Expo,react-native",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -10,7 +10,9 @@ const AppexChartComponent = React.forwardRef(({chartContext,options,...props},re
10
10
  return ()=>{
11
11
  if (chartContext.current && typeof chartContext.current.destroy === 'function') {
12
12
  setTimeout(()=>{
13
- try {chartContext.current.destroy();} catch{}
13
+ try {
14
+ if(typeof chartContext.current?.destroy =='function') chartContext.current.destroy();
15
+ } catch{}
14
16
  },1000);
15
17
  }
16
18
  }
@@ -1,10 +1,11 @@
1
1
  import React from '$react'
2
2
  import PropTypes from 'prop-types'
3
3
  import {defaultStr,defaultVal,extendObj,defaultObj,uniqid,defaultNumber} from "$utils";
4
- import {extend} from "./utils"
5
4
  import stableHash from 'stable-hash';
6
5
  import Chart from "./appexChart";
7
6
 
7
+ export * from "./utils";
8
+
8
9
  /**** pour le rendu webview chart, voir : https://github.com/flexmonster/react-native-flexmonster/blob/master/src/index.js */
9
10
  /**** le composant Chart s'appuie sur le composant appexChart : https://apexcharts.com/
10
11
  * pour le formattage des date, voir : https://apexcharts.com/docs/datetime/
@@ -16,35 +17,23 @@ import Chart from "./appexChart";
16
17
  * options {number} - les options supplémentaires au chart
17
18
  *
18
19
  */
19
- const ChartComponent = React.forwardRef(({options,height,width,chartId,testID,webViewProps, ...props },ref)=>{
20
+ const ChartComponent = React.forwardRef(({options:customOptions,height,width,chartId,testID,webViewProps, ...props },ref)=>{
20
21
  const chartContext = React.useRef(null);
21
- options = defaultObj(options);
22
- const series = options.series;
22
+ const {series,xaxis:customXaxis,...options} = customOptions;
23
+ const xaxis = defaultObj(customXaxis);
23
24
  options.chart = defaultObj(options.chart);
24
25
  const chartIdRef = React.useRef(defaultStr(chartId,options.chart.id,uniqid("chart-id")));
25
26
  width = options.chart.width = defaultVal(options.chart.width,width);
26
27
  height = options.chart.height = defaultVal(options.chart.height,height)
27
28
  options.chart.id = chartIdRef.current;
28
29
  testID = defaultStr(testID,"RN_ChartComponent");
29
- const prevWidth = React.usePrevious(width), prevHeight = React.usePrevious(height);
30
- const prevOptions = React.usePrevious(options,JSON.stringify);
31
- const prevSeries = React.usePrevious(series,JSON.stringify);
32
- /***change size chart, @see : https://apexcharts.com/docs/chart-types/line-chart/ */
33
- options.xaxis = defaultObj(options.xaxis);
34
- options.xaxis.stroke = defaultObj(options.xaxis.stroke);
35
- options.xaxis.stroke.width = defaultNumber(options.xaxis.stroke.width,2);
36
- options.xaxis.stroke.height = defaultNumber(options.xaxis.height,1);
37
-
38
- console.log(options,' is optssss');
30
+ options.xaxis = xaxis;
31
+ options.series = series;
39
32
  React.useEffect(()=>{
40
33
  if(chartContext.current && chartContext.current.updateOptions){
41
- if((prevSeries == series) || width != prevWidth || height != prevHeight){
42
- chartContext.current.updateOptions(options);
43
- } else if(prevOptions != options){
44
- chartContext.current.updateSeries(series);
45
- }
34
+ chartContext.current.updateOptions(options);
46
35
  }
47
- },[stableHash({options,series,width,height})])
36
+ },[stableHash(options)])
48
37
  return <Chart {...props} options={options} chartId={chartIdRef.current} chartContext={chartContext} testID={testID} ref={ref}/>
49
38
  });
50
39
 
@@ -1,25 +1,14 @@
1
1
  // Copyright 2022 @fto-consult/Boris Fouomene. All rights reserved.
2
2
  // Use of this source code is governed by a BSD-style
3
3
  // license that can be found in the LICENSE file.
4
- import {isObj} from "$utils";
5
- export function extend (target,source){
6
- const output = Object.assign({}, target);
7
- if (isObj(target) && isObj(source)) {
8
- Object.keys(source).forEach((key) => {
9
- if (isObj(source[key])) {
10
- if (!(key in target)) {
11
- Object.assign(output, {
12
- [key]: source[key]
13
- })
14
- } else {
15
- output[key] = this.extend(target[key], source[key])
16
- }
17
- } else {
18
- Object.assign(output, {
19
- [key]: source[key]
20
- })
21
- }
22
- })
23
- }
24
- return output
4
+ import {isObj,defaultNumber} from "$utils";
5
+ import appConfig from "$app/config";
6
+
7
+ /*** retourne le nombre maximal de courbes pouvant s'afficher sur un même graphe
8
+ * exempt du graphe de type pie/donut
9
+ * par défaut, on peut afficher au maximum 5 courbes sur le même graphe
10
+ */
11
+ export const getMaxSupportedSeriesSize = ()=>{
12
+ const m = defaultNumber(appConfig.get("maxSupportedChartSeries"));
13
+ return m > 3 ? m : 5;
25
14
  }
@@ -263,13 +263,13 @@ const DatagridFactory = (Factory)=>{
263
263
  this.isUpdating = false;
264
264
  return;
265
265
  }
266
- this.setSessionData({showFilter:true});
266
+ this.setSessionData({showFilters:true});
267
267
  }
268
268
  hideFilters (){
269
269
  if(!this._isMounted()) {
270
270
  return;
271
271
  }
272
- this.setSessionData({showFilter:false});
272
+ this.setSessionData({showFilters:false});
273
273
  }
274
274
  toggleFilterColumnVisibility(field,visible){
275
275
  if(!isNonNullString(field)) return;
@@ -399,7 +399,8 @@ const DatagridFactory = (Factory)=>{
399
399
  let exportTableProps = this.getExportableProps();
400
400
 
401
401
  filter = defaultFunc(filter,x=>true);
402
- let {showFilters,showFooters} = this.state;
402
+ const showFooters = this.canShowFooters();
403
+ const showFilters = this.canShowFilters();
403
404
  let max = this.getMaxSelectableRows();
404
405
  let restItems = [];
405
406
 
@@ -438,7 +439,7 @@ const DatagridFactory = (Factory)=>{
438
439
  filteredColumns,
439
440
  filters :headerFilters,
440
441
  } = this.preparedColumns;
441
- const hasFooterFields = this.hasFooterFields();
442
+ const hasFootersFields = this.hasFootersFields();
442
443
  const datagridHeader = <View testID={testID+"_HeaderContainer"} pointerEvents={pointerEvents} style={[styles.datagridHeader]}>
443
444
  <ScrollView testID={testID+"_HeaderScrollView"} horizontal contentContainerStyle={StyleSheet.flatten([styles.contentContainerStyle,styles.minW100])}>
444
445
  <View testID={testID+"_HeaderContentCntainer"} style={[styles.table,styles.pullRight]}>
@@ -513,10 +514,10 @@ const DatagridFactory = (Factory)=>{
513
514
  items : visibleColumns,
514
515
  closeOnPress : false,
515
516
  } : null,
516
- !canRenderChart && hasFooterFields ? {
517
+ !canRenderChart && hasFootersFields ? {
517
518
  onPress : ()=>{this.toggleFooters(!showFooters)}
518
519
  ,icon : showFooters?'view-column':'view-module'
519
- ,text : (showFooters?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux')
520
+ ,text : (showFooters?'Masquer les totaux':'Afficher les totaux')
520
521
  }:null,
521
522
  ...this.renderCustomMenu(),
522
523
  ...restItems,
@@ -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 } 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,20 @@ 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";
41
+ import Chart,{getMaxSupportedSeriesSize} from "$ecomponents/Chart";
42
42
  import { aggregatorFunctions} from "../Footer/Footer";
43
43
 
44
44
  const chart = "chart";
45
+ export const donutChart = {
46
+ isChart : true,
47
+ code : 'donutChart',
48
+ label : 'Graphique|Circulaire',
49
+ icon : "chart-donut",
50
+ type: 'donut',
51
+ isDonut : true,
52
+ isRendable : ({displayOnlySectionListHeaders,isSectionList})=>false,//isSectionList && displayOnlySectionListHeaders,
53
+ 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"
54
+ }
45
55
  export const displayTypes = {
46
56
  table : {
47
57
  code : "table",
@@ -69,14 +79,31 @@ export const displayTypes = {
69
79
  code : 'barChart',
70
80
  label : 'Graphique|Barres',
71
81
  icon : "chart-bar",
72
- type: 'bar'
82
+ type: 'bar',
83
+ isBar : true,
73
84
  },
74
- /*donutChart : {
85
+ donutChart,
86
+ /*rangeChart : {
87
+ code : "rangeChart",
88
+ isChart : true,
89
+ label : 'Graphique|Bar Interval',
90
+ icon : "chart-timeline",
91
+ type: 'boxPlot'
92
+ },*/
93
+ /*chartBoxPlot : {
94
+ code : "chartBoxPlot",
95
+ isChart : true,
96
+ label : 'Graphique|Box plot',
97
+ icon : "chart-waterfall",
98
+ type: 'boxPlot'
99
+ },*/
100
+ //@see : https://apexcharts.com/docs/chart-types/treemap-chart/
101
+ /*treeMap : {
102
+ code : "treeMap",
75
103
  isChart : true,
76
- code : 'donutChart',
77
- label : 'Graphique|Circulaire',
78
- icon : "chart-donut",
79
- type: 'donut'
104
+ label : 'Graphique|Aborescente',
105
+ icon : "chart-tree",
106
+ type: 'treemap'
80
107
  },*/
81
108
  }
82
109
 
@@ -122,7 +149,7 @@ export default class CommonDatagridComponent extends AppComponent {
122
149
  sData.fixedTable = defaultBool(sData.fixedTable,false);
123
150
  extendObj(this.state, {
124
151
  data,
125
- showFilters : this.isFilterable() && defaultBool(props.showFilters,(sData.showFilter? true : this.isPivotDatagrid())) || false,
152
+ showFilters : this.isFilterable() && defaultBool(props.showFilters,(sData.showFilters? true : this.isPivotDatagrid())) || false,
126
153
  showFooters : defaultBool(props.showFooters,(sData.showFooters? true : false)),
127
154
  fixedTable : sData.fixedTable
128
155
  });
@@ -204,12 +231,17 @@ export default class CommonDatagridComponent extends AppComponent {
204
231
  this.state.filteredColumns = defaultObj(this.getSessionData("filteredColumns"+this.getSessionNameKey()),this.props.filters);
205
232
  this.filtersSelectors = {selector:this.getFilters()};
206
233
  const {sectionListColumns} = this.prepareColumns();
234
+ this.state.sectionListColumns = sectionListColumns;
207
235
  if(this.canHandleColumnResize()){
208
236
  this.state.columnsWidths = this.preparedColumns.widths;
209
237
  }
210
- this.state.chartConfig = defaultObj(this.props.chartConfig,this.getSessionData("chartConfig"));
238
+ this.state.chartConfig = extendObj({},this.getSessionData("chartConfig"),this.props.chartConfig);
211
239
  const dType = defaultStr(this.props.displayType,this.getSessionData("displayType"),"table");
212
240
  this.state.displayType = this.displayTypes[dType] ? this.displayTypes[dType].code : "table" in this.displayTypes ? "table" : Object.keys(this.displayTypes)[0]?.code;
241
+ this.state.displayOnlySectionListHeaders = defaultBool(this.getSessionData("displayOnlySectionListHeaders"),this.props.displayOnlySectionListHeaders,false)
242
+ if(this.state.displayOnlySectionListHeaders){
243
+ this.state.showFooters = true;
244
+ }
213
245
  extendObj(this.state,this.prepareData({data}));
214
246
  const {width:windowWidth,height:windowHeight} = Dimensions.get("window");
215
247
  this.state.layout = {
@@ -254,8 +286,6 @@ export default class CommonDatagridComponent extends AppComponent {
254
286
  } else {
255
287
  this.currentDataSources = Object.toArray(this.currentDataSources);
256
288
  }
257
- this.state.sectionListColumns = sectionListColumns;
258
- this.state.displayOnlySectionListHeaders = defaultBool(this.getSessionData("displayOnlySectionListHeaders"),this.props.displayOnlySectionListHeaders,false)
259
289
  }
260
290
 
261
291
  /*** si une ligne peut être selectionable */
@@ -606,7 +636,7 @@ export default class CommonDatagridComponent extends AppComponent {
606
636
  }
607
637
  }
608
638
  }
609
- let footers = this.getFooterFields(true);
639
+ const footers = this.getFootersFields(true);
610
640
  let isAccordion = this.isAccordion();
611
641
  Object.mapToArray(columns,(headerCol1,headerIndex)=>{
612
642
  if(!isObj(headerCol1)) return;
@@ -614,6 +644,7 @@ export default class CommonDatagridComponent extends AppComponent {
614
644
  if(isAccordion && headerCol.accordion === false) return null;
615
645
  let header = {...headerCol};
616
646
  header.field = defaultStr(header.field, headerIndex)
647
+ header.type = defaultStr(header.jsType,header.type,"text").toLowerCase();
617
648
  /**** pour ignorer une colonne du datagrid, il suffit de passer le paramètre datagrid à false */
618
649
  if(!isNonNullString(header.field) || header.datagrid === false) {
619
650
  return;
@@ -624,7 +655,7 @@ export default class CommonDatagridComponent extends AppComponent {
624
655
  this.state.columns[header.field] = header;
625
656
  /*** les pieds de pages sont les données de type decimal, où qu'on peut compter */
626
657
  if(header.footer !== false && ((arrayValueExists(['decimal','number','money'],header.type) && header.format) || header.format == 'money' || header.format =='number')){
627
- footers[header.field] = header;
658
+ footers[header.field] = Object.clone(header);
628
659
  }
629
660
  if(!this.hasColumnsHalreadyInitialized){
630
661
  this.initColumnsCallback({...header,colIndex,columnField:header.field});
@@ -632,12 +663,12 @@ export default class CommonDatagridComponent extends AppComponent {
632
663
  })
633
664
  return footers;
634
665
  }
635
- getFooterFields(init){
666
+ getFootersFields(init){
636
667
  this[this.footerFieldName] = init === true ? {} : defaultObj(this[this.footerFieldName]);
637
668
  return this[this.footerFieldName];
638
669
  }
639
- hasFooterFields(){
640
- return Object.size(this.getFooterFields(),true) ? true : false;
670
+ hasFootersFields(){
671
+ return Object.size(this.getFootersFields(),true) ? true : false;
641
672
  }
642
673
  getActionsArgs(selected){
643
674
  const r = isObj(selected)? selected : {};
@@ -948,7 +979,7 @@ export default class CommonDatagridComponent extends AppComponent {
948
979
  this.isUpdating = true;
949
980
  this.setState( {showFilters:true},()=>{
950
981
  this.isUpdating = false;
951
- this.setSessionData({showFilter:true});
982
+ this.setSessionData({showFilters:true});
952
983
  })
953
984
  }
954
985
  hideFilters (){
@@ -959,12 +990,12 @@ export default class CommonDatagridComponent extends AppComponent {
959
990
  if(this.isUpdating) return false;
960
991
  this.setState({showFilters:false},()=>{
961
992
  this.isUpdating = false;
962
- this.setSessionData({showFilter:false})
993
+ this.setSessionData({showFilters:false})
963
994
  })
964
995
  }
965
996
 
966
997
  toggleFooters(showOrHide){
967
- if(typeof showOrHide !=='boolean' || this.state.showFooters === showOrHide) return;
998
+ if(typeof showOrHide !=='boolean' || this.canShowFooters() === showOrHide) return;
968
999
  if(!this._isMounted()) {
969
1000
  this.isUpdating = false;
970
1001
  return;
@@ -1030,7 +1061,7 @@ export default class CommonDatagridComponent extends AppComponent {
1030
1061
  if(!isNonNullString(field)) return;
1031
1062
  let columns = {...this.state.columns};
1032
1063
  columns[field].visible = !columns[field].visible;
1033
- let footers = this.getFooterFields();
1064
+ let footers = this.getFootersFields();
1034
1065
  if(isObj(footers[field])){
1035
1066
  footers[field].visible = columns[field].visible;
1036
1067
  }
@@ -1039,6 +1070,10 @@ export default class CommonDatagridComponent extends AppComponent {
1039
1070
  if(removeFocus) document.body.click();
1040
1071
  });
1041
1072
  }
1073
+ /****le nombre maximum de courbes supportées */
1074
+ getMaxSeriesSize(){
1075
+ return getMaxSupportedSeriesSize();
1076
+ }
1042
1077
  prepareFilter(props,filteredColumns){
1043
1078
  filteredColumns.push(props);
1044
1079
  }
@@ -1064,7 +1099,7 @@ export default class CommonDatagridComponent extends AppComponent {
1064
1099
  /*** configure la */
1065
1100
  configureSectionListColumn(column,toggleSectionList){
1066
1101
  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")
1102
+ const format = defaultStr(defaultObj(this.configureSectionListSelectedValues[column.field]).format,column.format,"dd/mm/yyyy")
1068
1103
  return new Promise((resolve,reject)=>{
1069
1104
  DialogProvider.open({
1070
1105
  title : 'Format de date',
@@ -1126,19 +1161,20 @@ export default class CommonDatagridComponent extends AppComponent {
1126
1161
  const {sectionListColumns} = this.prepareColumns({sectionListColumns:{}});
1127
1162
  this.setIsLoading(true,()=>{
1128
1163
  this.prepareData({data:this.INITIAL_STATE.data,sectionListColumns},(state)=>{
1129
- this.setState({...state,sectionListColumns},()=>{
1164
+ this.setState({...state,sectionListColumns,displayOnlySectionListHeaders:false},()=>{
1130
1165
  this.setIsLoading(false,false);
1131
1166
  this.setSessionData("sectionListColumns",null);
1167
+ this.setSessionData("displayOnlySectionListHeaders",false);
1132
1168
  });
1133
1169
  });
1134
1170
  },true);
1135
1171
  }
1136
1172
  canDisplayOnlySectionListHeaders(){
1137
- return this.hasFooterFields() && this.isSectionList() && this.hasSectionListData();
1173
+ return this.hasFootersFields() && this.isSectionList() && this.hasSectionListData();
1138
1174
  }
1139
1175
  /*** si l'on peut rendre le contenu de type graphique */
1140
1176
  isChartRendable(){
1141
- return !this.isPivotDatagrid() && this.hasFooterFields();
1177
+ return !this.isPivotDatagrid() && this.hasFootersFields();
1142
1178
  }
1143
1179
  isValidChartConfig(config){
1144
1180
  config = defaultObj(config,this.state.chartConfig);
@@ -1165,12 +1201,14 @@ export default class CommonDatagridComponent extends AppComponent {
1165
1201
  },true)
1166
1202
  } else {
1167
1203
  const cb = (chartConfig)=>{
1168
- this.setIsLoading(true,()=>{
1169
- this.setState({chartConfig,displayType},()=>{
1170
- this.setIsLoading(false,false);
1171
- this.persistDisplayType(displayType);
1172
- })
1173
- },true);
1204
+ setTimeout(()=>{
1205
+ this.setIsLoading(true,()=>{
1206
+ this.setState({chartConfig,displayType},()=>{
1207
+ this.setIsLoading(false,false);
1208
+ this.persistDisplayType(displayType);
1209
+ })
1210
+ },true);
1211
+ },200);
1174
1212
  }
1175
1213
  if(!this.isValidChartConfig()){
1176
1214
  return this.configureChart(false).then((chartConfig)=>{
@@ -1186,15 +1224,15 @@ export default class CommonDatagridComponent extends AppComponent {
1186
1224
  }
1187
1225
  const xItems = {},yItems = {},config = defaultObj(this.state.chartConfig);
1188
1226
  const series = {};
1189
- let hasSeries = false;
1190
1227
  const isValidConfig = this.isValidChartConfig();
1191
1228
  Object.map(this.state.columns,(field,f)=>{
1192
1229
  if(isObj(field) && !this.isSelectableColumn(field) && !this.isIndexColumn(field)){
1193
- xItems[f] = field;
1194
1230
  const type = defaultStr(field.jsType,field.type).toLowerCase();
1195
1231
  if(type === 'number' || type=='decimal'){
1196
1232
  yItems[f] = field;
1197
1233
  series[f] = field;
1234
+ } else {
1235
+ xItems[f] = field;
1198
1236
  }
1199
1237
  }
1200
1238
  });
@@ -1215,6 +1253,7 @@ export default class CommonDatagridComponent extends AppComponent {
1215
1253
  DialogProvider.open({
1216
1254
  title : 'Configuration des graphes',
1217
1255
  subtitle : false,
1256
+ data : this.getCharConfig(),
1218
1257
  fields : {
1219
1258
  x : {
1220
1259
  text : 'Axe des x[horizontal]',
@@ -1232,12 +1271,18 @@ export default class CommonDatagridComponent extends AppComponent {
1232
1271
  defaultValue : config.y,
1233
1272
  onValidatorValid,
1234
1273
  },
1235
- series : hasSeries && {
1274
+ series : {
1236
1275
  text : 'Series',
1237
1276
  type : "select",
1238
1277
  items : series,
1239
1278
  multiple : true,
1240
1279
  },
1280
+ sectionListHeadersSeries : {
1281
+ text : "Series des données groupées",
1282
+ type : "select",
1283
+ items : yItems,
1284
+ multiple : true,
1285
+ },
1241
1286
  aggregatorFunction : {
1242
1287
  type : 'select',
1243
1288
  text : "Foncton d'aggrégation",
@@ -1245,7 +1290,22 @@ export default class CommonDatagridComponent extends AppComponent {
1245
1290
  multiple : false,
1246
1291
  defaultValue : "sum",
1247
1292
  items : aggregatorFunctions,
1248
- }
1293
+ },
1294
+ stacked : {
1295
+ type : 'switch',
1296
+ text : "Graphe empilé?",
1297
+ disabled : this.state.displayType !== "barChart",
1298
+ checkedValue : true,
1299
+ uncheckedValue : false,
1300
+ },
1301
+ title : {
1302
+ text : "Titre du graphe",
1303
+ format :'hashtag',
1304
+ },
1305
+ titleColor: {
1306
+ text : "Couleur de titre",
1307
+ type :"color",
1308
+ },
1249
1309
  },
1250
1310
  actions : [
1251
1311
  {
@@ -1270,15 +1330,24 @@ export default class CommonDatagridComponent extends AppComponent {
1270
1330
  getCharConfig(){
1271
1331
  return defaultObj(this.state.chartConfig);
1272
1332
  }
1333
+ getChartIsRendableArgs(){
1334
+ return {
1335
+ context:this,isSectionList:this.isSectionList(),hasSectionListData:this.hasSectionListData(),
1336
+ data : this.state.data,
1337
+ displayOnlySectionListHeaders : this.canDisplayOnlySectionListHeaders(),
1338
+ };
1339
+ }
1273
1340
  renderDisplayTypes(){
1274
1341
  const m = [];
1275
1342
  let activeType = null,hasFoundChart = false,hasFoundTable = false;
1276
1343
  const hasConfig = this.isValidChartConfig();
1277
1344
  Object.map(this.displayTypes,(type,k)=>{
1345
+ let isChartDisabled = false;
1278
1346
  if(type.isChart === true ) {
1279
1347
  if(!this.isChartRendable()){
1280
1348
  return null;
1281
1349
  }
1350
+ isChartDisabled = typeof type.isRendable =='function' && type.isRendable(this.getChartIsRendableArgs()) === false ? true : false;
1282
1351
  if(!hasFoundChart){
1283
1352
  if(hasFoundTable){
1284
1353
  m.push({divider:true});
@@ -1288,7 +1357,16 @@ export default class CommonDatagridComponent extends AppComponent {
1288
1357
  divider : true,
1289
1358
  text : "Configurer les graphes",
1290
1359
  icon :"material-settings",
1291
- onPress : this.configureChart.bind(this)
1360
+ onPress : ()=>{
1361
+ this.configureChart(false).then((chartConfig)=>{
1362
+ this.setIsLoading(true,()=>{
1363
+ this.setState({chartConfig},()=>{
1364
+ this.setSessionData("chartConfig",chartConfig);
1365
+ this.setIsLoading(false,false);
1366
+ });
1367
+ },true)
1368
+ })
1369
+ }
1292
1370
  });
1293
1371
  }
1294
1372
  } else if(k === 'table'){
@@ -1304,7 +1382,7 @@ export default class CommonDatagridComponent extends AppComponent {
1304
1382
  right : <>
1305
1383
  {active ? <Icon color={theme.colors.primaryOnSurface} name="check"/>: null}
1306
1384
  </>,
1307
- disabled : type.isChart && !hasConfig ? true : undefined,
1385
+ disabled : isChartDisabled || type.isChart && !hasConfig ? true : undefined,
1308
1386
  onPress:()=>{
1309
1387
  this.setDisplayType(type);
1310
1388
  }
@@ -1329,105 +1407,246 @@ export default class CommonDatagridComponent extends AppComponent {
1329
1407
  getEmptyDataValue(){
1330
1408
  return "N/A";
1331
1409
  }
1410
+ /*** retourne les sectionHeaderSeries par défautt */
1411
+ getDefaultSectionHeadersSeries(){
1412
+ if(!this.isSectionList() || !this.hasFootersFields()) return [];
1413
+ const footersFields = this.getFootersFields();
1414
+ const r = [],max = this.getMaxSeriesSize();
1415
+ let counter = 0;
1416
+ for(let i in footersFields){
1417
+ const footer = footersFields[i];
1418
+ if(!isObj(footer)) continue;
1419
+ if(counter >= max) break;
1420
+ r.push(i);
1421
+ }
1422
+ return r;
1423
+ }
1424
+ formatChartDataValue({value,column,options}){
1425
+ return value;
1426
+ }
1427
+ /*****
1428
+ * retourne les paramètres à passer au chart, lorsque les données sont groupées, ie les sectionsListColumns est non nulle
1429
+ * @param {object} de la forme suivante :
1430
+ * @param {object} chartType le type de chart, l'un des types du tableau displayTypes en haut du présent fichier
1431
+ * @param {object} yAxisColumn la colonne de l'axe vertical y
1432
+ * @param {object} xAxisColumn la colonne de l'axe des x de la courbe, pris dans les configurations du chart, chartConfig
1433
+ * @param {object} la fonction d'aggrégation, l'une des fonctions issues des fonctions d'aggrégations aggregatorsFuncions, @see : dans $components/Datagrid/Footer
1434
+ * en affichage des tableaux de type sectionList, seul les colonnes de totalisation sont utilisées pour l'affichage du graphe
1435
+ * Le nombre de graphes (series) à afficher est valable pour tous les graphes sauf les graphes de type pie/donut.
1436
+ * 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)
1437
+ */
1438
+ getSectionListHeadersChartOptions({chartType,yAxisColumn,xAxisColumn,aggregatorFunction}){
1439
+ if(!this.isSectionList()) return null;
1440
+ if(!isObj(chartType) || !isObj(yAxisColumn) || !yAxisColumn.field) return null;
1441
+ if(!isObj(aggregatorFunction) || !isNonNullString(aggregatorFunction.code) || !aggregatorFunctions[aggregatorFunction.code]){
1442
+ aggregatorFunction = aggregatorFunctions.sum;
1443
+ }
1444
+ const code = aggregatorFunction.code;
1445
+ const isDonut = chartType.isDonut || chartType.isRadial;
1446
+ const config = this.getCharConfig();
1447
+ //@see : https://apexcharts.com/docs/series/
1448
+ ///lorsqu'on affiche uniquement les totaux des sections, alors la visualition se fait sur uniquement sur la base des valeurs
1449
+ ///on parcoure uniquement les entêtes des sectionLis
1450
+ const dataIndexes={},dataInexesNames = {};
1451
+ //la variable sectionListHeaderSeries, permet de récupérer les colonnes de type montant à utiliser pour le rendu du chart
1452
+ let serieName = "";
1453
+ const tableFooters = this.getFootersFields();
1454
+ const defaultSectionListHeadersSeries = this.getDefaultSectionHeadersSeries();
1455
+ let seriesConfig = isDonut ? [] : Array.isArray(config.sectionListHeadersSeries) && config.sectionListHeadersSeries.length ? config.sectionListHeadersSeries : [];
1456
+ if(!isDonut){
1457
+ if(seriesConfig.length){
1458
+ const ss = [];
1459
+ seriesConfig.map((s)=>{
1460
+ if(!isNonNullString(s) || !tableFooters[s]) return null;
1461
+ ss.push(s);
1462
+ });
1463
+ seriesConfig = ss;
1464
+ }
1465
+ if(!seriesConfig.length){
1466
+ seriesConfig = defaultSectionListHeadersSeries;
1467
+ }
1468
+ }
1469
+ /**** boucle sur chaque éléments trouvée dans le tableau des données sectionListData */
1470
+ const loopForFooter = ({column,serieName,footers,header})=>{
1471
+ if(!isObj(column) || !isObj(footers)) return null;
1472
+ if(!isObj(footers[column.field])) return null;
1473
+ const footer = footers[column.field]
1474
+ if(typeof footer[code] !== 'number') return null;
1475
+ const label = defaultStr(footer.label,footer.text,footer.field);
1476
+ if(typeof footer[code] !== 'number') return null;
1477
+ if(isDonut){
1478
+ serieName = label;
1479
+ dataIndexes[header] = footer[code];
1480
+ } else {
1481
+ dataIndexes[serieName] = defaultArray(dataIndexes[serieName]);
1482
+ dataIndexes[serieName].push({x:header,y:footer[code]});
1483
+ }
1484
+ }
1485
+ Object.map(this.sectionListData,(data,header)=>{
1486
+ if(!isObj(this.sectionListHeaderFooters[header])) return null;
1487
+ const footers = this.sectionListHeaderFooters[header];
1488
+ if(isDonut){
1489
+ loopForFooter({footers,header,column:yAxisColumn})
1490
+ } else {
1491
+ seriesConfig.map((s)=>{
1492
+ const serie = this.state.columns[s];
1493
+ const serieName = defaultStr(s.label,s.text,s);
1494
+ loopForFooter({footers,serie,serieName,header,column:tableFooters[s]})
1495
+ })
1496
+ }
1497
+ });
1498
+ if(isDonut){
1499
+ const series = [],labels = [];
1500
+ Object.map(dataIndexes,(serie,index)=>{
1501
+ series.push(serie);
1502
+ labels.push(index);
1503
+ });
1504
+ return {
1505
+ series,
1506
+ labels,
1507
+ }
1508
+ } else {
1509
+ const series = [];
1510
+ Object.map(dataIndexes,(data,name)=>{
1511
+ series.push({name,data});
1512
+ });
1513
+ return {
1514
+ series
1515
+ }
1516
+ }
1517
+ }
1332
1518
  renderChart(){
1333
1519
  if(!this.canRenderChart()) return null;
1334
1520
  if(!this.isValidChartConfig()) return null;
1335
1521
  const chartType = displayTypes[this.state.displayType];
1336
1522
  if(!isObj(chartType) || !isNonNullString(chartType.type)) return null;
1523
+ if(typeof chartType.isRendable =='function' && chartType.isRendable(this.getChartIsRendableArgs()) === false) {
1524
+ 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")
1525
+ return null;
1526
+ }
1337
1527
  const config = this.getCharConfig();
1338
1528
  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();
1529
+ const yAxisColumn = this.state.columns[config.y];
1530
+ const type = defaultStr(yAxisColumn.jsType,yAxisColumn.type).toLowerCase();
1341
1531
  if(type !== 'number'&& type !== 'decimal') return null;
1342
1532
  const isEmptyY = config.x === this.emptySectionListHeaderValue;
1343
- const seriesConfig = Array.isArray(config.series) && config.series.length ? config.series : [yaxis.field];
1344
- let xaxis = null;
1533
+ let seriesConfig = Array.isArray(config.series) && config.series.length ? config.series : [yAxisColumn.field];
1534
+ const snConfig = [];
1535
+ Object.map(seriesConfig,(s,v)=>{
1536
+ if(!isNonNullString(s) || !this.state.columns[s]) return null;
1537
+ snConfig.push(s);
1538
+ });
1539
+ seriesConfig = snConfig.length> 0 ? snConfig : [yAxisColumn.field];
1540
+ let xAxisColumn = null;
1345
1541
  if(!isEmptyY){
1346
1542
  if(!this.state.columns[config.x]){
1347
1543
  return null;
1348
1544
  }
1349
- xaxis = this.state.columns[config.x];
1545
+ xAxisColumn = this.state.columns[config.x];
1350
1546
  }
1351
1547
  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
1548
  const emptyValue = this.getEmptyDataValue();
1361
- const indexes = {}
1549
+ const indexes = {};
1550
+ let series = [],xaxis = {},customConfig = {};
1362
1551
  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,
1552
+ if(!this.isSectionList()){
1553
+ if(isObj(aggregatorFunction) && typeof aggregatorFunction.eval =='function'){
1554
+ aggregatorFunction = aggregatorFunction.eval;
1555
+ } else {
1556
+ aggregatorFunction = aggregatorFunctions.sum.eval;
1557
+ }
1558
+ this.state.data.map((data,index)=>{
1559
+ if(!isObj(data))return null;
1560
+ const txt = this.renderRowCell({
1561
+ data,
1562
+ rowData : data,
1563
+ rowCounterIndex : index,
1564
+ rowIndex : index,
1565
+ columnDef : xAxisColumn,
1566
+ renderRowCell : false,
1567
+ columnField : xAxisColumn.field,
1568
+ });
1569
+ const text = isNonNullString(txt)? txt : emptyValue;
1570
+ Object.map(seriesConfig,(s,v)=>{
1571
+ if(!isNonNullString(s) || !this.state.columns[s]) return null;
1572
+ const columnDef = this.state.columns[s];
1573
+ if(!isObj(columnDef)) return null;
1574
+ indexes[s] = defaultObj(indexes[s]);
1575
+ const current = indexes[s];
1576
+ current[text] = typeof current[text] =="number"? current[text] : 0;
1577
+ const value = getFooterColumnValue({data,columnDef,columnField:columnDef.field});
1578
+ current[text] = aggregatorFunction(value,current[text],count);
1579
+ })
1373
1580
  });
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,
1581
+ Object.map(indexes,(values,serieName)=>{
1582
+ const col = this.state.columns[serieName];
1583
+ const name = defaultStr(col?.label,col?.text,serieName),data = [];
1584
+ Object.map(values,(v,i)=>{
1585
+ data.push({
1586
+ x : i,
1587
+ y : v,
1588
+ })
1589
+ })
1590
+ series.push({
1591
+ name,
1592
+ type : chartType.type,
1593
+ data,
1394
1594
  })
1395
1595
  })
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;
1596
+ } else {
1597
+ const configs = this.getSectionListHeadersChartOptions({chartType,aggregatorFunction,xAxisColumn,yAxisColumn});
1598
+ if(isObj(configs)){
1599
+ const {series :customSeries,xaxis:customXaxis,...rest} = configs;
1600
+ series = Array.isArray(customSeries) ? customSeries : series;
1601
+ xaxis = isObj(customXaxis)? customXaxis : xaxis;
1602
+ customConfig = rest;
1409
1603
  } else {
1410
- maxHeight = diff;
1604
+ return null;
1411
1605
  }
1412
1606
  }
1413
- const chartProps = defaultObj(chartProps);
1607
+ customConfig = defaultObj(customConfig);
1608
+ customConfig.chart = defaultObj(customConfig.chart);
1609
+ const chartProps = extendObj(true,{},this.props.chartProps,customConfig);
1610
+ if(chartType.isBar){
1611
+ chartProps.chart.stacked = !!config.stacked;
1612
+ } else chartProps.chart.stacked = undefined;
1414
1613
  return <Chart
1415
1614
  options = {{
1416
- ...defaultObj(chartProps.options),
1615
+ ...chartProps,
1616
+ title : {
1617
+ text: defaultStr(config.title,chartProps.title),
1618
+ align: 'left',
1619
+ //margin: 10,
1620
+ //offsetX: 0,
1621
+ //offsetY: 0,
1622
+ //floating: false,
1623
+ style: {
1624
+ //fontSize: '14px',
1625
+ //fontWeight: 'bold',
1626
+ //fontFamily: undefined,
1627
+ color: theme.Colors.isValid(config.titleColor)?config.titleColor : undefined,
1628
+ },
1629
+ },
1417
1630
  series,
1418
1631
  chart : {
1419
1632
  height :350,
1420
- maxHeight,
1421
1633
  ...defaultObj(chartProps.chart),
1422
1634
  type : chartType.type,
1423
1635
  },
1424
1636
  xaxis: {
1425
1637
  ...defaultObj(chartProps.xaxis),
1426
- type: 'category'
1638
+ type: 'category',
1639
+ ...defaultObj(xaxis)
1427
1640
  }
1428
1641
  }}
1429
1642
  />
1430
1643
  }
1644
+ canShowFooters(){
1645
+ return this.state.showFooters || this.hasSectionListData() && this.state.displayOnlySectionListHeaders;
1646
+ }
1647
+ canShowFilters(){
1648
+ return this.state.showFilters
1649
+ }
1431
1650
  toggleDisplayOnlySectionListHeaders(){
1432
1651
  if(!this.canDisplayOnlySectionListHeaders()) return
1433
1652
  setTimeout(()=>{
@@ -1532,7 +1751,6 @@ export default class CommonDatagridComponent extends AppComponent {
1532
1751
  } = header;
1533
1752
  restCol = Object.clone(defaultObj(restCol));
1534
1753
  let colFilter = defaultVal(restCol.filter,true);
1535
- const format = defaultStr(restCol.format).toLowerCase();
1536
1754
  field = header.field = defaultStr(header.field,field,headerIndex);
1537
1755
  delete restCol.filter;
1538
1756
 
@@ -1588,9 +1806,13 @@ export default class CommonDatagridComponent extends AppComponent {
1588
1806
  sortedColumn.header = header;
1589
1807
  sortedColumn.label = restCol.label;
1590
1808
  const isDesc = currentSortedColumn.dir === "desc";
1809
+ const sortDir = isDesc ? "descending" : "ascending";
1591
1810
  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");
1811
+ sortedColumn.icon = 'sort-'+prefix+'-'+sortDir;
1593
1812
  sortedColumn.title = (isDesc ? "Trié par ordre décroissant":"Trié par ordre croissant ")+ " du champ ["+restCol.label+"]";
1813
+ if(sortType.contains('date') || sortType.contains("time")){
1814
+ 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")
1815
+ }
1594
1816
  }
1595
1817
  sortedColumns[field] = restCol.label;
1596
1818
  sortedColumnsLength++;
@@ -1660,15 +1882,15 @@ export default class CommonDatagridComponent extends AppComponent {
1660
1882
  ...header,
1661
1883
  width,
1662
1884
  type,
1885
+ ...sListColumns[field],
1663
1886
  ...defaultObj(this.configureSectionListSelectedValues[field]),
1664
- ...sListColumns[field],
1665
1887
  };///les colonnes de sections
1666
1888
  this.sectionListColumnsSize.current++;
1667
1889
  }
1668
1890
  const mItem = {
1891
+ ...header,
1669
1892
  field,
1670
1893
  type,
1671
- format,
1672
1894
  onPress : ()=>{
1673
1895
  this.toggleColumnInSectionList(field);
1674
1896
  return false;
@@ -1679,9 +1901,9 @@ export default class CommonDatagridComponent extends AppComponent {
1679
1901
  if(this.isSectionListColumnConfigurable(mItem)){
1680
1902
  mItem.right = (p)=>{
1681
1903
  return <Icon name="material-settings" {...p} onPress={(e)=>{
1682
- React.stopEventPropagation(e);
1904
+ //React.stopEventPropagation(e);
1683
1905
  this.configureSectionListColumn(mItem);
1684
- return false;
1906
+ //return false;
1685
1907
  }}/>
1686
1908
  }
1687
1909
  }
@@ -1748,11 +1970,25 @@ export default class CommonDatagridComponent extends AppComponent {
1748
1970
  let isArr = Array.isArray(data);
1749
1971
  //let push = (d,index) => isArr ? newData.push(d) : newData[index] = d;
1750
1972
  const hasLocalFilter = this.props.filters !== false && this.hasLocalFilters;
1751
- let footersColumns = this.getFooterFields(),hasFooterFields = this.hasFooterFields();
1752
- const canUpdateFooters = !!(updateFooters !== false && hasFooterFields);
1973
+ const footersColumns = this.getFootersFields(),hasFootersFields = this.hasFootersFields();
1974
+ const canUpdateFooters = !!(updateFooters !== false && hasFootersFields);
1753
1975
  this.hasFoundSectionData.current = false;
1754
1976
  this.sectionListDataSize.current = 0;
1755
1977
  const isSList = this.isSectionList(sectionListColumns);
1978
+ if(this.canAutoSort() && isNonNullString(this.sortRef.current.column)){
1979
+ if(isObj(this.state.columns) && this.state.columns[this.sortRef.current.column]){
1980
+ let field = this.state.columns[this.sortRef.current.column];
1981
+ const sortConfig = Object.assign({},this.sortRef.current);
1982
+ sortConfig.getItem = (item,columnName,{getItem})=>{
1983
+ if(isObj(item) && (field.type =='decimal' || field.type =="number")){
1984
+ const v = item[columnName];
1985
+ return isDecimal(v)? v : isNonNullString(v)? parseDecimal(v) : 0;
1986
+ }
1987
+ return getItem(item,columnName);
1988
+ }
1989
+ data = sortBy(data,sortConfig);//on trie tout d'abord les données
1990
+ }
1991
+ }
1756
1992
  if(hasLocalFilter || !isArr || canUpdateFooters || isSList) {
1757
1993
  if(canUpdateFooters){
1758
1994
  this.___evaluatedFootersValues = {}
@@ -1765,10 +2001,12 @@ export default class CommonDatagridComponent extends AppComponent {
1765
2001
  Object.map(this.sectionListData,(v,i)=>{
1766
2002
  delete this.sectionListData[i];
1767
2003
  });
1768
- //on réinnitialise tous les footes
1769
- Object.map(this.sectionListHeaderFooters,(v,i)=>{
1770
- delete this.sectionListHeaderFooters[i];
1771
- })
2004
+ if(canUpdateFooters){
2005
+ //on réinnitialise tous les footes
2006
+ Object.map(this.sectionListHeaderFooters,(v,i)=>{
2007
+ delete this.sectionListHeaderFooters[i];
2008
+ })
2009
+ }
1772
2010
  }
1773
2011
  let currentSectionListFooter = null;
1774
2012
  const sectionListData = this.sectionListData;//l'ensemble des données de sectionList
@@ -1791,6 +2029,7 @@ export default class CommonDatagridComponent extends AppComponent {
1791
2029
  }
1792
2030
  sectionListData[r].push(d);
1793
2031
  if(canUpdateFooters){
2032
+ ///garde pour chaque éléments de groue, la valeur des champs de son footer
1794
2033
  this.sectionListHeaderFooters[r] = defaultObj(this.sectionListHeaderFooters[r]);
1795
2034
  currentSectionListFooter = this.sectionListHeaderFooters[r];
1796
2035
  }
@@ -1810,20 +2049,6 @@ export default class CommonDatagridComponent extends AppComponent {
1810
2049
  });
1811
2050
  data = newData;
1812
2051
  }
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
2052
  this.INITIAL_STATE.data = data;
1828
2053
  if(this.hasFoundSectionData.current){
1829
2054
  data = [];
@@ -1905,7 +2130,7 @@ export default class CommonDatagridComponent extends AppComponent {
1905
2130
  }
1906
2131
  }
1907
2132
  let cells = null;
1908
- if(this.state.showFooters && isObj(this.sectionListHeaderFooters[key])){
2133
+ if(this.canShowFooters() && isObj(this.sectionListHeaderFooters[key])){
1909
2134
  const {visibleColumnsNames,widths} = defaultObj(this.preparedColumns);
1910
2135
  if(isObj(visibleColumnsNames) &&isObj(widths)){
1911
2136
  cells = [];
@@ -2865,6 +3090,8 @@ CommonDatagridComponent.propTypes = {
2865
3090
  x : PropTypes.string.isRequired, //l'axe horizontal
2866
3091
  y : PropTypes.string.isRequired, //l'axe des y, les colonnes de type nombre
2867
3092
  series : PropTypes.arrayOf([PropTypes.string]), //les séries, le nombre de courbe a afficher sur le graphe, en fonction du type
3093
+ /**** les series à utiliser pour l'affichage des données lorsque les colonnes sont groupées, ie les montant de totalisation sont utilisés */
3094
+ sectionListHeadersSeries : PropTypes.arrayOf([PropTypes.string]),
2868
3095
  }),
2869
3096
  displayType : chartDisplayType,
2870
3097
  /*** les types d'afichates supportés par l'application */
@@ -1,4 +1,3 @@
1
- import Button from "$ecomponents/Button";
2
1
  import Menu from "$ecomponents/BottomSheet/Menu";
3
2
  import View from "$ecomponents/View";
4
3
  import {Pressable,StyleSheet} from "react-native";
@@ -3,21 +3,27 @@ import View from "$ecomponents/View";
3
3
  import React from "$react";
4
4
  import memoize from "$react/memoize";
5
5
  export {default as FooterItem} from "./Footer";
6
- import {parseDecimal} from "$utils";
6
+ import {parseDecimal,defaultObj,defaultStr,isNonNullString} from "$utils";
7
7
 
8
- export const evalSingleValue = ({data,columnDef,field,result,withLabel,displayLabel,onlyVisible})=>{
9
- data = data || {}
8
+ export * from "./Footer";
9
+
10
+ /***évalue la valeur décimale selon les paramètres */
11
+ export const getFooterColumnValue = ({data,columnDef,field,result,columnField}) =>{
12
+ data = defaultObj(data)
13
+ columnDef = defaultObj(columnDef);
14
+ columnField = defaultStr(columnField,columnDef.field,field);
15
+ let val = data[columnField];
16
+ if(typeof columnDef.multiplicater ==='function'){
17
+ val = defaultDecimal(columnDef.multiplicater({value:val,columnField,field,columnDef,rowData:data,item:data}),val)
18
+ }
19
+ return typeof val =='number'? parseDecimal(val.toFixed(12)) : 0;
20
+ }
21
+
22
+ export const evalSingleValue = ({data,columnDef,field,columnField,withLabel,result,displayLabel,onlyVisible})=>{
10
23
  if(!isNonNullString(field) || !isObj(columnDef)) return result;
11
24
  onlyVisible = defaultBool(onlyVisible,true);
12
25
  if(onlyVisible === true && !(columnDef.visible !== false)) result;
13
- let val = data[field];
14
- if(isFunction(columnDef.multiplicater)){
15
- val = defaultDecimal(columnDef.multiplicater({value:val,columnField:field,field,columnDef,rowData:data,item:data}),val)
16
- }
17
- if(!isDecimal(val)){
18
- return result;
19
- }
20
- val = parseDecimal(val.toFixed(10));
26
+ let val = getFooterColumnValue({data,columnDef,columnField,result,field});
21
27
  (Array.isArray(result) ? result : [result]).map((currentResult)=>{
22
28
  currentResult = defaultObj(currentResult);
23
29
  if(!isObj(currentResult[field])){
@@ -53,7 +53,7 @@ const DatagridFactory = (Factory)=>{
53
53
  renderFooterCell(props){
54
54
  const {columnField,style} = props;
55
55
  let footersValues = this.getFooterValues();
56
- const footerFields = this.getFooterFields();
56
+ const footerFields = this.getFootersFields();
57
57
  if(isObj(footerFields[columnField])){
58
58
  return <Footer
59
59
  //{...footerFields[columnField]}
@@ -160,8 +160,9 @@ const DatagridFactory = (Factory)=>{
160
160
  total:pagination.rows,pages:countPages
161
161
  })*/
162
162
  const {visibleColumns} = this.preparedColumns;
163
- const hasFooterFields = this.hasFooterFields();
164
- const {columnsWidths:widths,showFilters,showFooters} = this.state;
163
+ const hasFootersFields = this.hasFootersFields();
164
+ const {columnsWidths:widths} = this.state;
165
+ const showFooters = this.canShowFooters(), showFilters = this.canShowFilters();
165
166
  const isLoading = this.isLoading();
166
167
  let _progressBar = this.getProgressBar();
167
168
  const pointerEvents = this.getPointerEvents();
@@ -219,13 +220,13 @@ const DatagridFactory = (Factory)=>{
219
220
  {showFilters?'Masquer/Filtres':'Afficher/Filtres'}
220
221
  </Button>
221
222
  )}
222
- {hasFooterFields && !canRenderChart ? <Button
223
+ {hasFootersFields && !canRenderChart ? <Button
223
224
  normal
224
225
  style={styles.paginationItem}
225
226
  onPress = {()=>{this.toggleFooters(!showFooters)} }
226
227
  icon = {showFooters?'view-column':'view-module'}
227
228
  >
228
- {showFooters?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux'}
229
+ {showFooters?'Masquer les totaux':'Afficher les totaux'}
229
230
  </Button>:null}
230
231
  {restItems.map((item,index)=>{
231
232
  return <Button
@@ -277,7 +278,7 @@ const DatagridFactory = (Factory)=>{
277
278
  ,icon : showFilters?'eye-off':'eye'
278
279
  ,text : (showFilters?'Masquer/Filtres':'Afficher/Filtres')
279
280
  } : null,
280
- isMobile && hasFooterFields?{
281
+ isMobile && hasFootersFields?{
281
282
  onPress : ()=>{this.toggleFooters(!showFooters)}
282
283
  ,icon : showFooters?'view-column':'view-module'
283
284
  ,text : (showFooters?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux')
@@ -344,7 +345,7 @@ const DatagridFactory = (Factory)=>{
344
345
  </View> : null}
345
346
  getItemType = {this.getFlashListItemType.bind(this)}
346
347
  renderItem = {this.renderFlashListItem.bind(this)}
347
- hasFooters = {hasFooterFields && !canRenderChart ? true : false}
348
+ hasFooters = {hasFootersFields && !canRenderChart ? true : false}
348
349
  showFilters = {showFilters}
349
350
  showFooters = {showFooters && !canRenderChart ? true : false}
350
351
  showHeaders = { canRenderChart ? !!showFilters : true}
@@ -79,7 +79,8 @@ export const selectDateFormatFieldProps = ({onAdd:customOnAdd,onAddCustomFormat,
79
79
  getItemValue : ({item})=>item.code,
80
80
  renderItem : dateFormatSelectorRenderItem,
81
81
  showAdd : true,
82
- ...defaultObj(props),
82
+ ...props,
83
+ defaultValue : defaultStr(props.defaultValue,props.format),
83
84
  onAdd,
84
85
  onAdd : undefined,
85
86
  showAdd : false,
@@ -889,7 +889,7 @@ export default class Field extends AppComponent {
889
889
 
890
890
  this.___formattedField = undefined;
891
891
  let _type = this.type;
892
- format = defaultStr(format).toLowerCase().trim();
892
+ format = defaultStr(format);
893
893
  tooltip = defaultVal(tooltip,title);
894
894
 
895
895
  const isEditable = rest.disabled !== true && rest.readOnly !== true && rest.editable !== false ? true : false;