@fto-consult/expo-ui 2.46.4 → 2.47.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fto-consult/expo-ui",
3
- "version": "2.46.4",
3
+ "version": "2.47.1",
4
4
  "description": "Bibliothèque de composants UI Expo,react-native",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -61,7 +61,7 @@
61
61
  "@emotion/native": "^11.10.0",
62
62
  "@expo/html-elements": "^0.2.0",
63
63
  "@expo/vector-icons": "^13.0.0",
64
- "@fto-consult/common": "^2.8.3",
64
+ "@fto-consult/common": "^2.9.4",
65
65
  "@gorhom/portal": "^1.0.14",
66
66
  "@react-native-async-storage/async-storage": "~1.17.3",
67
67
  "@react-native-community/datetimepicker": "6.5.2",
@@ -9,7 +9,7 @@ import Tooltip from "$ecomponents/Tooltip";
9
9
  import setQueryLimit from "./setQueryLimit";
10
10
  import {showConfirm} from "$ecomponents/Dialog";
11
11
  import Label from "$ecomponents/Label";
12
- import Icon,{COPY_ICON} from "$ecomponents/Icon";
12
+ import Icon,{COPY_ICON,CHECKED_ICON,UNCHECKED_ICON} from "$ecomponents/Icon";
13
13
  import filterUtils from "$cfilters";
14
14
  import {sortBy,isDecimal,defaultVal,sanitizeSheetName,extendObj,isObjOrArray,isObj,defaultNumber,defaultStr,isFunction,defaultBool,defaultArray,defaultObj,isNonNullString,defaultDecimal} from "$utils";
15
15
  import {Datagrid as DatagridContentLoader} from "$ecomponents/ContentLoader";
@@ -36,10 +36,10 @@ import notify from "$cnotify";
36
36
  import FileSystem from "$file-system";
37
37
  import sprintf from "$cutils/sprintf";
38
38
  import { renderRowCell,formatValue,arrayValueSeparator } from "./utils";
39
- import {ScrollView} from "react-native";
40
39
  import Button from "$ecomponents/Button";
41
40
  import stableHash from "stable-hash";
42
41
  import * as XLSX from "xlsx";
42
+ import {convertToSQL} from "$ecomponents/Filter";
43
43
 
44
44
  export const TIMEOUT = 100;
45
45
 
@@ -264,21 +264,20 @@ export default class CommonDatagridComponent extends AppComponent {
264
264
  sectionListColumnsSize : {value : {current:0}}, //la taille du nombre d'éléments de section dans les colonnes
265
265
  chartRef : {value : {current:null}},
266
266
  layoutRef : {value : {}},
267
+ hidePreloaderOnRenderKey : {value : uniqid("hide-preloader-on-render")},
267
268
  isRenderingRef : {value : {current:false}},
268
269
  chartSeriesNamesColumnsMapping : {value : {}},//le mappage entre les index des series et les colonnes coorespondantes
269
270
  })
271
+ const config = extendObj(true,{},this.getSessionData("config"),this.props.chartConfig);
272
+ Object.map(config,(v,k)=>{
273
+ if(typeof v =='function'){
274
+ delete config[k];
275
+ }
276
+ });
277
+ this.state.fetchOnlyVisibleColumns = !!defaultVal(this.props.fetchOnlyVisibleColumns,config.fetchOnlyVisibleColumns,this.getSessionData("fetchOnlyVisibleColumns"));
270
278
  this.state.abreviateValues = "abreviateValues" in this.props? !!this.props.abreviateValues : !!this.getSessionData("abreviateValues");
271
- const sessionAggregator = defaultStr(this.getSessionData("aggregatorFunction")).trim();
272
- const aggregatorProps = defaultStr(this.props.aggregatorFunction).trim();
273
- let aggregatorFunction = null;
274
- if(aggregatorProps && isValidAggregator(this.aggregatorFunctions[aggregatorProps])){
275
- aggregatorFunction = aggregatorProps;
276
- } else if(isNonNullString(sessionAggregator) && isValidAggregator(this.aggregatorFunctions[sessionAggregator])){
277
- aggregatorFunction = sessionAggregator;
278
- } else {
279
- aggregatorFunction = Object.keys(this.aggregatorFunctions)[0];
280
- }
281
- this.state.aggregatorFunction= aggregatorFunction;
279
+ const sessionAggregator = this.getSessionData("aggregatorFunction");
280
+ this.state.aggregatorFunction= this.isValidAggregator(config.aggregatorFunction) && config.aggregatorFunction || this.isValidAggregator(this.props.aggregatorFunction) && this.props.aggregatorFunction || this.isValidAggregator(sessionAggregator) && sessionAggregator || Object.keys(this.aggregatorFunctions)[0];;
282
281
  this.isLoading = this.isLoading.bind(this);
283
282
  this.getProgressBar = this.getProgressBar.bind(this);
284
283
  this.sortRef.current.dir = defaultStr(this.sortRef.current.dir,this.sortRef.current.column == "date"?"desc":'asc')
@@ -290,6 +289,7 @@ export default class CommonDatagridComponent extends AppComponent {
290
289
  this.INITIAL_STATE = {
291
290
  data,
292
291
  }
292
+ this.prepareFetchData();
293
293
  this._datagridId = isNonNullString(this.props.id)? this.props.id : uniqid("datagrid-id")
294
294
  this.canDoFilter = true;
295
295
  this.filters = {}
@@ -306,15 +306,14 @@ export default class CommonDatagridComponent extends AppComponent {
306
306
  }
307
307
  });
308
308
  this.state.filteredColumns = defaultObj(this.getSessionData("filteredColumns"+this.getSessionNameKey()),this.props.filters);
309
- this.filtersSelectors = {selector:this.getFilters()};
310
309
  const {sectionListColumns} = this.prepareColumns();
311
310
  this.state.sectionListColumns = sectionListColumns;
312
311
  if(this.canHandleColumnResize()){
313
312
  this.state.columnsWidths = this.preparedColumns.widths;
314
313
  }
315
- this.state.chartConfig = extendObj(true,{},this.getSessionData("chartConfig"),this.props.chartConfig);
316
- if(!("sparkline" in this.state.chartConfig) && this.isDashboard()){
317
- this.state.chartConfig.sparkline = true;
314
+ this.state.config = config;
315
+ if(!("sparkline" in this.state.config) && this.isDashboard()){
316
+ this.state.config.sparkline = true;
318
317
  }
319
318
  const dType = defaultStr(this.props.displayType,this.getSessionData("displayType"),"table");
320
319
  this.state.displayType = this.displayTypes[dType] ? this.displayTypes[dType].code : "table" in this.displayTypes ? "table" : Object.keys(this.displayTypes)[0]?.code;
@@ -368,7 +367,34 @@ export default class CommonDatagridComponent extends AppComponent {
368
367
  }
369
368
  this.persistDisplayType(this.state.displayType);
370
369
  }
371
-
370
+ /*** si l'on peut récuperer à distance, les colonnes seulement visibles */
371
+ canFetchOnlyVisibleColumns(){
372
+ return this.isTableData() && this.props.canFetchOnlyVisibleColumns !== false && true || false;
373
+ }
374
+ isFetchOnlyVisibleColumnsEnabled(){
375
+ return this.canFetchOnlyVisibleColumns() && !!this.state.fetchOnlyVisibleColumns;
376
+ }
377
+ fetchDataIfCanFetchColumnsIfVisible(){
378
+ if(!this.canFetchOnlyVisibleColumns()) return;
379
+ this.toggleHidePreloaderOnRender(false);
380
+ return this.fetchData({force:true});
381
+ }
382
+ toggleFetchOnlyVisibleColumns(){
383
+ if(!this.canFetchOnlyVisibleColumns()){
384
+ return Promise.reject({});
385
+ }
386
+ const fetchOnlyVisibleColumns = !this.isFetchOnlyVisibleColumnsEnabled();
387
+ setTimeout(()=>{
388
+ this.setIsLoading(true,()=>{
389
+ this.toggleHidePreloaderOnRender(true);
390
+ this.setSessionData("fetchOnlyVisibleColumns",fetchOnlyVisibleColumns);
391
+ this.setState({fetchOnlyVisibleColumns},this.fetchDataIfCanFetchColumnsIfVisible.bind(this))
392
+ },true)
393
+ },TIMEOUT)
394
+ }
395
+ isValidAggregator(aggregatorFunction){
396
+ return isNonNullString(aggregatorFunction) && isValidAggregator(this.aggregatorFunctions[aggregatorFunction])
397
+ }
372
398
  /*** si une ligne peut être selectionable */
373
399
  canSelectRow(row){
374
400
  return isObj(row) && row.isSectionListHeader !== true ? true : false;
@@ -957,7 +983,7 @@ export default class CommonDatagridComponent extends AppComponent {
957
983
  this._previousSortObj = Object.clone(this.sortRef.current);
958
984
  this.sortRef.current = sort;
959
985
  this.prepareColumns({sortedColumn : this.sortRef.current});
960
- if(typeof this.props.onSort =='function' && this.props.onSort({...sort,context:this,sort,data:this.INITIAL_STATE.data,fields:this.state.columns,columns:this.state.columns}) === false){
986
+ if(typeof this.props.onSort =='function' && this.props.onSort({context:this,sort,data:this.INITIAL_STATE.data,fields:this.state.columns,columns:this.state.columns}) === false){
961
987
  return;
962
988
  }
963
989
  this.prepareData({data:this.INITIAL_STATE.data,updateFooters:false},(state)=>{
@@ -1005,6 +1031,17 @@ export default class CommonDatagridComponent extends AppComponent {
1005
1031
  */
1006
1032
  renderCustomMenu(){
1007
1033
  const customMenu = []
1034
+ if(this.canFetchOnlyVisibleColumns()){
1035
+ const isFE = this.isFetchOnlyVisibleColumnsEnabled();
1036
+ customMenu.push({
1037
+ text : "Valeurs Cols visibles",
1038
+ tooltip : "Récupérer uniquement les valeurs des colonnes visibles",
1039
+ icon : "material-radio-button-{0}".sprintf(isFE?"on":"off"),
1040
+ onPress : (e)=>{
1041
+ this.toggleFetchOnlyVisibleColumns();
1042
+ }
1043
+ })
1044
+ }
1008
1045
  Object.map(this.props.customMenu,(menu,i)=>{
1009
1046
  if(isObj(menu)){
1010
1047
  const {onPress,label,text,children,...rest} = menu;
@@ -1177,7 +1214,8 @@ export default class CommonDatagridComponent extends AppComponent {
1177
1214
  }
1178
1215
  this.setIsLoading(true,()=>{
1179
1216
  this.prepareColumns({columns});
1180
- this.setState({columns});
1217
+ this.toggleHidePreloaderOnRender(this.canFetchOnlyVisibleColumns());
1218
+ this.setState({columns},this.fetchDataIfCanFetchColumnsIfVisible.bind(this));
1181
1219
  })
1182
1220
  },TIMEOUT)
1183
1221
  }
@@ -1301,7 +1339,7 @@ export default class CommonDatagridComponent extends AppComponent {
1301
1339
  return !this.isPivotDatagrid() && this.hasFootersFields();
1302
1340
  }
1303
1341
  isValidChartConfig(){
1304
- const config = this.state.chartConfig;
1342
+ const config = this.state.config;
1305
1343
  return isNonNullString(config.x) && isNonNullString(config.y);
1306
1344
  }
1307
1345
  canRenderChart(){
@@ -1323,21 +1361,21 @@ export default class CommonDatagridComponent extends AppComponent {
1323
1361
  });
1324
1362
  },true)
1325
1363
  } else {
1326
- const cb = (chartConfig)=>{
1364
+ const cb = (config)=>{
1327
1365
  setTimeout(()=>{
1328
1366
  this.setIsLoading(true,()=>{
1329
- this.setState({chartConfig,displayType},()=>{
1367
+ this.setState({config,displayType},()=>{
1330
1368
  this.persistDisplayType(displayType);
1331
1369
  })
1332
1370
  },true);
1333
1371
  },200);
1334
1372
  }
1335
1373
  if(!this.isValidChartConfig()){
1336
- return this.configureChart(false).then((chartConfig)=>{
1337
- cb(chartConfig);
1374
+ return this.configureChart(false).then((config)=>{
1375
+ cb(config);
1338
1376
  });
1339
1377
  }
1340
- cb({...this.state.chartConfig});
1378
+ cb({...this.state.config});
1341
1379
  }
1342
1380
  }
1343
1381
  getActiveAggregatorFunction(){
@@ -1421,7 +1459,7 @@ export default class CommonDatagridComponent extends AppComponent {
1421
1459
  />
1422
1460
  }
1423
1461
  configureSectionLists(){
1424
- const config = this.getChartConfig();
1462
+ const config = this.getConfig();
1425
1463
  return new Promise((resolve)=>{
1426
1464
  DialogProvider.open({
1427
1465
  title : "Paramètres de groupe",
@@ -1445,13 +1483,13 @@ export default class CommonDatagridComponent extends AppComponent {
1445
1483
  text : "Configurer",
1446
1484
  icon : "check",
1447
1485
  onPress : ({data})=>{
1448
- const chartConfig = {...config,...data};
1449
- this.setSessionData("chartConfig",chartConfig);
1486
+ const nConfig = {...config,...data};
1487
+ this.setSessionData("config",nConfig);
1450
1488
  DialogProvider.close();
1451
1489
  this.setIsLoading(true,true);
1452
- this.prepareData({data:this.INITIAL_STATE.data,config:chartConfig},(state)=>{
1453
- this.setState({...state,chartConfig},()=>{
1454
- resolve(chartConfig)
1490
+ this.prepareData({data:this.INITIAL_STATE.data,config:nConfig},(state)=>{
1491
+ this.setState({...state,config:nConfig},()=>{
1492
+ resolve(nConfig)
1455
1493
  })
1456
1494
  })
1457
1495
  }
@@ -1464,7 +1502,7 @@ export default class CommonDatagridComponent extends AppComponent {
1464
1502
  if(!this.isChartRendable()){
1465
1503
  return Promise.reject({message:'Impossible de configurer le graphe car le type de données ne permet pas de rendu de type graphe'});
1466
1504
  }
1467
- const xItems = {},yItems = {},config = defaultObj(this.state.chartConfig);
1505
+ const xItems = {},yItems = {},config = defaultObj(this.state.config);
1468
1506
  const series = {},seriesGroups = {};
1469
1507
  const footers = this.getFootersFields();
1470
1508
  const isValidConfig = this.isValidChartConfig();
@@ -1496,7 +1534,7 @@ export default class CommonDatagridComponent extends AppComponent {
1496
1534
  }
1497
1535
  }
1498
1536
  return new Promise((resolve,reject)=>{
1499
- const data = this.getChartConfig();
1537
+ const data = this.getConfig();
1500
1538
  DialogProvider.open({
1501
1539
  title : 'Configuration des graphes',
1502
1540
  subtitle : false,
@@ -1608,23 +1646,23 @@ export default class CommonDatagridComponent extends AppComponent {
1608
1646
  text : "Configurer",
1609
1647
  icon : "check",
1610
1648
  onPress : ({data})=>{
1611
- const chartConfig = {...config,...data};
1612
- this.setSessionData("chartConfig",chartConfig);
1649
+ const nConfig = {...config,...data};
1650
+ this.setSessionData("config",nConfig);
1613
1651
  DialogProvider.close();
1614
1652
  if(!isValidConfig && refreshChart !== false){
1615
- return this.setState({chartConfig},()=>{
1616
- resolve(chartConfig)
1653
+ return this.setState({config:nConfig},()=>{
1654
+ resolve(nConfig)
1617
1655
  })
1618
1656
  }
1619
- resolve(chartConfig);
1657
+ resolve(nConfig);
1620
1658
  }
1621
1659
  }
1622
1660
  ]
1623
1661
  })
1624
1662
  })
1625
1663
  }
1626
- getChartConfig(){
1627
- return defaultObj(this.state.chartConfig);
1664
+ getConfig(){
1665
+ return defaultObj(this.state.config);
1628
1666
  }
1629
1667
  getChartIsRendableArgs(){
1630
1668
  return {
@@ -1640,7 +1678,7 @@ export default class CommonDatagridComponent extends AppComponent {
1640
1678
  downloadChart(){
1641
1679
  if(!this.chartRef.current || !this.chartRef.current.dataURI) return Promise.reject({message:'Référence du graphique non valide'});
1642
1680
  return this.chartRef.current.dataURI().then(({ imgURI, blob })=>{
1643
- const config = this.getChartConfig();
1681
+ const config = this.getConfig();
1644
1682
  const fileName = sprintf(defaultStr(config.fileName,config.title,"graphe"));
1645
1683
  FileSystem.write({content:imgURI,fileName})
1646
1684
  });
@@ -1890,9 +1928,9 @@ export default class CommonDatagridComponent extends AppComponent {
1890
1928
  text : "Configurer les graphes",
1891
1929
  icon :"material-settings",
1892
1930
  onPress : ()=>{
1893
- this.configureChart(false).then((chartConfig)=>{
1931
+ this.configureChart(false).then((config)=>{
1894
1932
  this.setIsLoading(true,()=>{
1895
- this.setState({chartConfig});
1933
+ this.setState({config});
1896
1934
  },true)
1897
1935
  })
1898
1936
  }
@@ -1968,7 +2006,7 @@ export default class CommonDatagridComponent extends AppComponent {
1968
2006
  * @param {object} de la forme suivante :
1969
2007
  * @param {object} chartType le type de chart, l'un des types du tableau displayTypes en haut du présent fichier
1970
2008
  * @param {object} yAxisColumn la colonne de l'axe vertical y
1971
- * @param {object} xAxisColumn la colonne de l'axe des x de la courbe, pris dans les configurations du chart, chartConfig
2009
+ * @param {object} xAxisColumn la colonne de l'axe des x de la courbe, pris dans les configurations du chart, config
1972
2010
  * @param {object} la fonction d'aggrégation, l'une des fonctions issues des fonctions d'aggrégations aggregatorsFuncions, @see : dans $components/Datagrid/Footer
1973
2011
  * en affichage des tableaux de type sectionList, seul les colonnes de totalisation sont utilisées pour l'affichage du graphe
1974
2012
  * Le nombre de graphes (series) à afficher est valable pour tous les graphes sauf les graphes de type pie/donut.
@@ -1977,12 +2015,12 @@ export default class CommonDatagridComponent extends AppComponent {
1977
2015
  getSectionListHeadersChartOptions({chartType,yAxisColumn,xAxisColumn,aggregatorFunction}){
1978
2016
  if(!this.isSectionList()) return null;
1979
2017
  if(!isObj(chartType) || !isObj(yAxisColumn) || !yAxisColumn.field) return null;
1980
- if(!isValidAggregator(aggregatorFunction)){
2018
+ if(!this.isValidAggregator(aggregatorFunction)){
1981
2019
  aggregatorFunction = this.getActiveAggregatorFunction();
1982
2020
  }
1983
2021
  const code = aggregatorFunction.code;
1984
2022
  const isDonut = chartType.isDonut || chartType.isRadial;
1985
- const config = this.getChartConfig();
2023
+ const config = this.getConfig();
1986
2024
  //@see : https://apexcharts.com/docs/series/
1987
2025
  ///lorsqu'on affiche uniquement les totaux des sections, alors la visualition se fait sur uniquement sur la base des valeurs
1988
2026
  ///on parcoure uniquement les entêtes des sectionLis
@@ -2076,7 +2114,7 @@ export default class CommonDatagridComponent extends AppComponent {
2076
2114
  return null;
2077
2115
  }
2078
2116
  const isDonut = chartType.isDonut || chartType.isRadial;
2079
- const config = this.getChartConfig();
2117
+ const config = this.getConfig();
2080
2118
  if(!this.state.columns[config.y]) return null;
2081
2119
  const yAxisColumn = this.state.columns[config.y];
2082
2120
  const type = defaultStr(yAxisColumn.jsType,yAxisColumn.type).toLowerCase();
@@ -2453,6 +2491,9 @@ export default class CommonDatagridComponent extends AppComponent {
2453
2491
  },
2454
2492
  title : title,
2455
2493
  icon : visible?CHECKED_ICON_NAME : null,
2494
+ right : false ? (p)=><Icon {...p} icon="material-gear"
2495
+ onPress = {(e)=>{console.log(e," is pressed toddd config")}}
2496
+ /> : undefined
2456
2497
  });
2457
2498
  restCol.field = header.field;
2458
2499
 
@@ -2636,7 +2677,7 @@ export default class CommonDatagridComponent extends AppComponent {
2636
2677
  prepareData(args,cb){
2637
2678
  let {pagination,config,aggregatorFunction:customAggregatorFunction,displayOnlySectionListHeaders:cdisplayOnlySectionListHeaders,data,force,sectionListColumns,updateFooters} = defaultObj(args);
2638
2679
  cb = typeof cb ==='function'? cb : typeof args.cb == 'function'? args.cb : undefined;
2639
- config = isObj(config) && Object.size(config,true)? config : this.getChartConfig();
2680
+ config = isObj(config) && Object.size(config,true)? config : this.getConfig();
2640
2681
  const aggregatorFunction = isNonNullString(customAggregatorFunction) && customAggregatorFunction in this.aggregatorFunctions ? this.aggregatorFunctions[customAggregatorFunction] : this.getActiveAggregatorFunction();
2641
2682
  sectionListColumns = isObj(sectionListColumns) ? sectionListColumns : this.state.sectionListColumns;
2642
2683
  const displayOnlySectionListHeaders = typeof cdisplayOnlySectionListHeaders == 'boolean'?cdisplayOnlySectionListHeaders : this.state.displayOnlySectionListHeaders;
@@ -2766,9 +2807,9 @@ export default class CommonDatagridComponent extends AppComponent {
2766
2807
  if(this.getSectionListHeaderProp){
2767
2808
  return this.getSectionListHeaderProp(args);
2768
2809
  }
2769
- const chartConfig = isObj(args.config) && Object.size(args.config,true)? args.config : this.getChartConfig();
2770
- const displayGroupLabels = "displayGroupLabels" in chartConfig? chartConfig.displayGroupLabels : true;
2771
- const displayGroupLabelsSeparator = typeof chartConfig.displayGroupLabelsSeparator =='string'? chartConfig.displayGroupLabelsSeparator : arrayValueSeparator;
2810
+ const config = isObj(args.config) && Object.size(args.config,true)? args.config : this.getConfig();
2811
+ const displayGroupLabels = "displayGroupLabels" in config? config.displayGroupLabels : true;
2812
+ const displayGroupLabelsSeparator = typeof config.displayGroupLabelsSeparator =='string'? config.displayGroupLabelsSeparator : arrayValueSeparator;
2772
2813
  const {fields,sectionListColumnsSize} = args;
2773
2814
  const d = [];
2774
2815
  Object.map(fields,(field,i)=>{
@@ -2780,7 +2821,7 @@ export default class CommonDatagridComponent extends AppComponent {
2780
2821
  columnField : defaultStr(field.field,i),
2781
2822
  });
2782
2823
  if(!isNonNullString(txt) && typeof txt !=='number') return;
2783
- if(sectionListColumnsSize == 1 || !displayGroupLabels){
2824
+ if(!displayGroupLabels){
2784
2825
  d.push(txt);
2785
2826
  } else {
2786
2827
  d.push("{0} : {1}".sprintf(defaultStr(field.label,field.txt),txt))
@@ -3069,23 +3110,117 @@ export default class CommonDatagridComponent extends AppComponent {
3069
3110
  this.previousDataSources = dataSources;
3070
3111
  this.previousServer = server;
3071
3112
  }
3113
+ prepareFetchData(fetchData){
3114
+ this.INITIAL_STATE.fetchData = defaultVal(fetchData,this.props.fetchData);
3115
+ }
3072
3116
  beforeFetchData(){}
3073
- fetchData({fetchOptions}){
3074
- if(typeof this.props.fetchData =='function'){
3075
- const r = this.props.fetchData(fetchOptions);
3076
- if(isPromise(r)){
3077
- return r.then((data)=>{
3078
- if(isObjOrArray(data)){
3079
- this.setIsLoading(true,()=>{
3080
- this.prepareData({data},(state)=>{
3081
- this.setState(state)
3082
- })
3083
- },true)
3084
- }
3085
- })
3117
+ /**** retourne la liste des items, utile lorsqu'une s'agit d'une fonction
3118
+ Lorsque data est une chaine de caractère, alors elle doit être sous la forme recommandée par la function
3119
+ getDataFunc de l'object dataSource
3120
+ la props data, peut être une chaine de caractère contenant le nom de la base et de la bd de travail de l'application
3121
+ example common[articles], dans ce cas, la fonction fetchData, aura pour rôle de chercher toutes les données qui match
3122
+ la table dans la base common.
3123
+ Elle pourra éventuellement passer directement la limite et les filtres à la fonction fetchdata
3124
+ */
3125
+ fetchData ({cb,callback,force,fetchOptions,...rest}){
3126
+ if(!this._isMounted()) return Promise.resolve(this.state.data);
3127
+ if(this.isFetchingData) {
3128
+ if(!isPromise(this.fetchingPromiseData)){
3129
+ this.fetchingPromiseData = Promise.resolve(this.state.data)
3086
3130
  }
3087
- }
3088
- return Promise.resolve(this.state.data);
3131
+ return this.fetchingPromiseData;
3132
+ };
3133
+ this.isFetchingData = true;
3134
+ cb = typeof cb =='function'? cb : typeof callback =='function'? callback : undefined;
3135
+ this.fetchingPromiseData = new Promise((resolve,reject)=>{
3136
+ setTimeout(()=>{
3137
+ if(typeof cb === 'boolean'){
3138
+ force = cb;
3139
+ cb = undefined;
3140
+ }
3141
+ fetchOptions = this.getFetchOptions({fetchOptions,convertToSQL:false});
3142
+ if(typeof this.props.fetchOptionsMutator =='function' && this.props.fetchOptionsMutator(fetchOptions) === false){
3143
+ this.isFetchingData = false;
3144
+ return resolve(this.state.data);
3145
+ }
3146
+ if(this.beforeFetchData(fetchOptions) === false) return resolve(this.state.data);
3147
+ if(this.willConvertFiltersToSQL()){
3148
+ fetchOptions.selector = convertToSQL(fetchOptions.selector);
3149
+ }
3150
+ if(typeof this.props.beforeFetchData =='function' && this.props.beforeFetchData({...rest,context:this,force,fetchOptions,options:fetchOptions}) === false){
3151
+ this.isFetchingData = false;
3152
+ return resolve(this.state.data);
3153
+ }
3154
+ if(force !== true && isArray(this.INITIAL_STATE.data)) {
3155
+ return this.resolveFetchedDataPromise({cb,data:this.INITIAL_STATE.data}).then(resolve).catch(reject)
3156
+ }
3157
+ let fetchData = this.INITIAL_STATE.fetchData;
3158
+ if(isFunction(this.props.fetchData)){
3159
+ /**** l'on peut définir la props fetchData, qui est la fonction appelée pour la recherche des données */
3160
+ fetchData = this.props.fetchData.call(this,fetchOptions);
3161
+ }
3162
+ fetchData = isFunction(fetchData)? fetchData.call(this,fetchOptions) : fetchData;
3163
+ this.setIsLoading(true,()=>{
3164
+ if(isPromise(fetchData)){
3165
+ return fetchData.then(data=>{
3166
+ return this.resolveFetchedDataPromise({cb,data,force}).then((data)=>{
3167
+ resolve(data);
3168
+ }).catch((e)=>{
3169
+ console.log(e,' fetching datagrid data')
3170
+ reject(e);
3171
+ });
3172
+ }).catch((e)=>{
3173
+ console.log(e," resolve fetching data for datagrid");
3174
+ return this.resolveFetchedDataPromise({cb,data:[],force}).then((data)=>{
3175
+ resolve(data);
3176
+ }).catch((e)=>{
3177
+ console.log(e,' fetching datagrid data')
3178
+ reject(e);
3179
+ });;
3180
+ })
3181
+ } else {
3182
+ let data = !isPromise(fetchData)? Object.toArray(fetchData) : [];
3183
+ ///if(data.length <=0) data = this.state.data;
3184
+ return this.resolveFetchedDataPromise({cb,data,force}).then((data)=>{
3185
+ resolve(data);
3186
+ }).catch((e)=>{
3187
+ console.log(e,' fetching datagrid data')
3188
+ reject(e);
3189
+ });
3190
+ }
3191
+ },true);
3192
+ },1);
3193
+ })
3194
+ return this.fetchingPromiseData;
3195
+ }
3196
+ resolveFetchedDataPromise(arg){
3197
+ arg = defaultObj(arg);
3198
+ const d = arg.data;
3199
+ if(isObj(d) && d.data && typeof d.data ==='object'){
3200
+ arg.data = d.data;
3201
+ arg.total = defaultNumber(arg.total,arg.data.total);
3202
+ }
3203
+ /*if(typeof arg.total != 'number'){
3204
+ arg.total = Object.size(arg.data);
3205
+ }*/
3206
+ const {cb,total,data} = arg;
3207
+ return new Promise((resolve)=>{
3208
+ this.prepareData(arg,(state)=>{
3209
+ state.data = Array.isArray(state.data)? state.data : [];
3210
+ if(typeof this.props.onFetchData =='function'){
3211
+ this.props.onFetchData({...arg,allData:data,context:this,props:this.props,data:state.data})
3212
+ }
3213
+ this.setState(state,()=>{
3214
+ if(isFunction(cb)) {
3215
+ cb(data)
3216
+ }
3217
+ resolve(data);
3218
+ this.isRenderingRef.current = false;
3219
+ this.isFetchingData = undefined;
3220
+ this.setIsLoading(false,false);
3221
+ })
3222
+ });
3223
+ })
3089
3224
  }
3090
3225
  /**** Filtre le tableau */
3091
3226
  doFilter ({value,field,selector,event,force}){
@@ -3115,8 +3250,7 @@ export default class CommonDatagridComponent extends AppComponent {
3115
3250
  this._pagination.page = 1;
3116
3251
  this._pagination.start = 0;
3117
3252
  }
3118
- this.filtersSelectors = {selector:this.getFilters()};
3119
- return this.fetchData({force:true,isFiltering : true,fetchOptions:this.filtersSelectors});
3253
+ return this.fetchData({force:true});
3120
3254
  }
3121
3255
  onSetQueryLimit(){
3122
3256
  if(!this.canSetQueryLimit()) return;
@@ -3169,6 +3303,35 @@ export default class CommonDatagridComponent extends AppComponent {
3169
3303
  </Pressable>
3170
3304
  </Tooltip>
3171
3305
  }
3306
+ /*** récupère les fetchOptions du datagrid */
3307
+ getFetchOptions({fetchOptions,convertToSQL}){
3308
+ const fetchFilters = this.getFilters({convertToSQL:typeof convertToSQL =='boolean'? convertToSQL : false});
3309
+ fetchOptions = Object.clone(isObj(fetchOptions)? fetchOptions : {});
3310
+ fetchOptions.selector = defaultObj(fetchOptions.selector);
3311
+ fetchOptions.dataSources = this.currentDataSources;
3312
+ fetchOptions.selector = fetchFilters;
3313
+ fetchOptions.sort = this.getSort();
3314
+ const ff = this.getFilterableColumnsNames();
3315
+ let fields = ff;
3316
+ if(this.isFetchOnlyVisibleColumnsEnabled()){
3317
+ fields = [];
3318
+ Object.map(ff,(field)=>{
3319
+ if(isNonNullString(field) && isObj(this.state.columns[field]) && this.state.columns[field].visible !== false){
3320
+ fields.push(field);
3321
+ }
3322
+ });
3323
+ }
3324
+ fetchOptions.fields = fields;
3325
+ let limit = this.getQueryLimit();
3326
+ if(limit > 0 && !this.isPivotDatagrid()){
3327
+ fetchOptions.limit = limit;
3328
+ } else {
3329
+ if(!isDecimal(fetchOptions.limit) || fetchOptions.limit <=0){
3330
+ delete fetchOptions.limit
3331
+ }
3332
+ }
3333
+ return fetchOptions;
3334
+ }
3172
3335
  getFetchDataOpts(){
3173
3336
  return this.props.fetchDataOpts;
3174
3337
  }
@@ -3272,16 +3435,20 @@ export default class CommonDatagridComponent extends AppComponent {
3272
3435
  canSetIsLoading(){
3273
3436
  return isObj(this.progressBarRef.current) && typeof this.progressBarRef.current.setIsLoading =='function' ? true : false;
3274
3437
  }
3438
+ canHidePreloaderOnRender(){
3439
+ const cH = this[this.hidePreloaderOnRenderKey];
3440
+ return typeof cH =='boolean'? cH : true;
3441
+ }
3442
+ toggleHidePreloaderOnRender(toggle){
3443
+ this[this.hidePreloaderOnRenderKey] = !!toggle;
3444
+ }
3275
3445
  onRender(){
3276
- if(typeof this.props.onRender ==='function'){
3277
- this.props.onRender({context:this});
3278
- }
3279
- if(this.isRenderingRef.current === true){
3280
- //setTimeout(()=>{
3281
- this.isRenderingRef.current = false;
3282
- return this.setIsLoading(false,undefined,undefined,"yes mannnnaaaaaa");
3283
- //},500);
3446
+ if(!this.canHidePreloaderOnRender()) return ;
3447
+ if(typeof this.props.onRender ==='function' && this.props.onRender({context:this}) === false){
3448
+ return ;
3284
3449
  }
3450
+ this.isRenderingRef.current = false;
3451
+ return this.setIsLoading(false,undefined,undefined);
3285
3452
  }
3286
3453
  /***
3287
3454
  * @param {boolean} loading
@@ -3749,6 +3916,10 @@ CommonDatagridComponent.propTypes = {
3749
3916
  renderCustomPagination : PropTypes.func,
3750
3917
  getActionsArgs : PropTypes.func,//fonction permettant de récupérer les props supplémentaires à passer aux actions du datagrid
3751
3918
  displayOnlySectionListHeaders : PropTypes.bool,// si uniquement les entêtes des sections seront affichés, valides uniquement en affichage des sectionHeader
3919
+ /***les props de configuration du chart, */
3920
+ config : PropTypes.shape({
3921
+ aggregatorFunction : PropTypes.string, //la fonction d'aggrégation à utiliser
3922
+ }),
3752
3923
  /*** les options de configuration du graphe */
3753
3924
  chartConfig : PropTypes.shape({
3754
3925
  //type : PropTypes.oneOfType(chartDisplayType).isRequired,//le type de graphe : l'une des valeurs parmis les éléments cités plus haut
@@ -3771,6 +3942,9 @@ CommonDatagridComponent.propTypes = {
3771
3942
  aggregatorFunction : PropTypes.string,
3772
3943
  /*** permet de faire une mutation sur les options de la recherche, immédiatement avant le lancement de la recherche */
3773
3944
  fetchOptionsMutator : PropTypes.func,
3945
+ /*** si les données à récupérer à distance seront */
3946
+ fetchOnlyVisibleColumns : PropTypes.bool,
3947
+ canFetchOnlyVisibleColumns : PropTypes.bool,//si l'on peut modifier le type d'affichage lié à la possibilité de récupérer uniquement les données reletives aux colonnes visibles
3774
3948
  useLinesProgressBar : PropTypes.bool,//si le progress bar lignes horizontale seront utilisés
3775
3949
  abreviateValues : PropTypes.bool, //si les valeurs numériques seront abregées
3776
3950
  }
@@ -1,7 +1,6 @@
1
1
  import CommonDatagrid from "./Common";
2
2
  import {defaultObj,extendObj,defaultStr,isNonNullString,isFunction,isPromise} from "$utils";
3
3
  import PropTypes from "prop-types";
4
- import {convertToSQL} from "$ecomponents/Filter";
5
4
  import actions from "$actions";
6
5
  /****
7
6
  * la fonction fetchOptionsMutator permet éventuellemnt de faire une mutations sur les options fetchOptions avant qu'elle ne soit appliquée pour la recherche. elle
@@ -18,16 +17,13 @@ export default class CommonTableDatagrid extends CommonDatagrid{
18
17
  } = props;
19
18
  dataSource = CommonDatagrid.getDataSource({...props,dataSource,context:this});
20
19
  tableName = defaultStr(tableName,table).toUpperCase();
21
- this.prepareFetchData();
22
20
  if(tableName){
23
21
  Object.defineProperties(this,{
24
22
  tableName : {value:tableName,override:false,writable:false}
25
23
  })
26
24
  }
27
25
  }
28
- prepareFetchData(fetchData){
29
- this.INITIAL_STATE.fetchData = defaultVal(fetchData,this.props.fetchData);
30
- }
26
+
31
27
  /*** lorsque la données est modifiée */
32
28
  onUpsertData =(arg) =>{
33
29
  if(!this._isMounted()) return;
@@ -61,127 +57,10 @@ export default class CommonTableDatagrid extends CommonDatagrid{
61
57
  isTableData(){
62
58
  return true;
63
59
  }
64
-
65
60
  getProgressBar(props){
66
61
  if(this.isDataJustComeToUpsert) return null;
67
62
  return super.getProgressBar(props);
68
63
  }
69
- /**** retourne la liste des items, utile lorsqu'une s'agit d'une fonction
70
- Lorsque data est une chaine de caractère, alors elle doit être sous la forme recommandée par la function
71
- getDataFunc de l'object dataSource
72
- la props data, peut être une chaine de caractère contenant le nom de la base et de la bd de travail de l'application
73
- example common[articles], dans ce cas, la fonction fetchData, aura pour rôle de chercher toutes les données qui match
74
- la table dans la base common.
75
- Elle pourra éventuellement passer directement la limite et les filtres à la fonction fetchdata
76
- */
77
- fetchData ({cb,callback,force,fetchOptions,...rest}){
78
- if(!this._isMounted()) return Promise.resolve(this.state.data);
79
- if(this.isFetchingData) {
80
- if(!isPromise(this.fetchingPromiseData)){
81
- this.fetchingPromiseData = Promise.resolve(this.state.data)
82
- }
83
- return this.fetchingPromiseData;
84
- };
85
- this.isFetchingData = true;
86
- cb = typeof cb =='function'? cb : typeof callback =='function'? callback : undefined;
87
- this.fetchingPromiseData = new Promise((resolve,reject)=>{
88
- setTimeout(()=>{
89
- if(typeof cb === 'boolean'){
90
- force = cb;
91
- cb = undefined;
92
- }
93
- const fetchFilters = this.getFilters({convertToSQL:false});
94
- fetchOptions = defaultObj(fetchOptions);
95
- fetchOptions = Object.clone(fetchOptions);
96
- fetchOptions.selector = defaultObj(fetchOptions.selector);
97
- fetchOptions.dataSources = this.currentDataSources;
98
- fetchOptions = extendObj(true,true,{},fetchOptions,{selector : fetchFilters});
99
- fetchOptions.dataSources = this.currentDataSources;
100
- fetchOptions.sort = this.getSort();
101
- fetchOptions.fields = this.getFilterableColumnsNames();
102
- let limit = this.getQueryLimit();
103
- if(limit > 0 && !this.isPivotDatagrid()){
104
- fetchOptions.limit = limit;
105
- } else {
106
- if(!isDecimal(fetchOptions.limit) || fetchOptions.limit <=0){
107
- delete fetchOptions.limit
108
- }
109
- }
110
- if(typeof this.props.fetchOptionsMutator =='function' && this.props.fetchOptionsMutator(fetchOptions) === false){
111
- this.isFetchingData = false;
112
- return resolve(this.state.data);
113
- }
114
- this.beforeFetchData(fetchOptions);
115
- if(this.willConvertFiltersToSQL()){
116
- fetchOptions.selector = convertToSQL(fetchOptions.selector);
117
- }
118
- if(typeof this.props.beforeFetchData =='function' && this.props.beforeFetchData({...rest,context:this,force,fetchOptions,options:fetchOptions}) === false){
119
- this.isFetchingData = false;
120
- return resolve(this.state.data);
121
- }
122
- if(force !== true && isArray(this.INITIAL_STATE.data)) {
123
- return this.resolveFetchedDataPromise({cb,data:this.INITIAL_STATE.data}).then(resolve).catch(reject)
124
- }
125
- let fetchData = this.INITIAL_STATE.fetchData;
126
- if(isFunction(this.props.fetchData)){
127
- /**** l'on peut définir la props fetchData, qui est la fonction appelée pour la recherche des données */
128
- fetchData = this.props.fetchData.call(this,fetchOptions);
129
- }
130
- fetchData = isFunction(fetchData)? fetchData.call(this,fetchOptions) : fetchData;
131
- this.setIsLoading(true,()=>{
132
- if(isPromise(fetchData)){
133
- return fetchData.then(data=>{
134
- return this.resolveFetchedDataPromise({cb,data,force}).then((data)=>{
135
- resolve(data);
136
- }).catch((e)=>{
137
- console.log(e,' fetching datagrid data')
138
- reject(e);
139
- });
140
- }).catch((e)=>{
141
- console.log(e," resolve fetching data for datagrid");
142
- return this.resolveFetchedDataPromise({cb,data:[],force}).then((data)=>{
143
- resolve(data);
144
- }).catch((e)=>{
145
- console.log(e,' fetching datagrid data')
146
- reject(e);
147
- });;
148
- })
149
- } else {
150
- let data = !isPromise(fetchData)? Object.toArray(fetchData) : [];
151
- ///if(data.length <=0) data = this.state.data;
152
- return this.resolveFetchedDataPromise({cb,data,force}).then((data)=>{
153
- resolve(data);
154
- }).catch((e)=>{
155
- console.log(e,' fetching datagrid data')
156
- reject(e);
157
- });
158
- }
159
- },true);
160
- },1);
161
- })
162
- return this.fetchingPromiseData;
163
- }
164
- resolveFetchedDataPromise(arg){
165
- arg = defaultObj(arg);
166
- const {cb,data} = arg;
167
- return new Promise((resolve)=>{
168
- this.prepareData(arg,(state)=>{
169
- state.data = Array.isArray(state.data)? state.data : [];
170
- if(typeof this.props.onFetchData =='function'){
171
- this.props.onFetchData({...arg,allData:data,context:this,props:this.props,data:state.data})
172
- }
173
- this.setState(state,()=>{
174
- if(isFunction(cb)) {
175
- cb(data)
176
- }
177
- resolve(data);
178
- this.isRenderingRef.current = false;
179
- this.isFetchingData = undefined;
180
- this.setIsLoading(false,false);
181
- })
182
- });
183
- })
184
- }
185
64
  }
186
65
 
187
66
  CommonTableDatagrid.propTypes = {
@@ -10,6 +10,7 @@ import Label from "$ecomponents/Label";
10
10
  import TableLink from "$TableLink";
11
11
  import {Flag} from "$ecomponents/Countries";
12
12
  import { StyleSheet } from "react-native";
13
+ import {isDesktopMedia} from "$dimensions";
13
14
 
14
15
  export const renderRowCell = (arg)=>{
15
16
  let {rowData,getRowKey,context,formatValue:customFormatValue,renderRowCell:customRenderRowCell,abreviateValues,isSectionListHeader,rowIndex,index,rowCounterIndex,columnDef,columnField} = arg;
@@ -198,5 +199,9 @@ export const formatValue = (value,format,abreviateValues,formatter)=>{
198
199
  return abreviateValues ? value.abreviate() : value.formatNumber();
199
200
  }
200
201
 
202
+ export const getRowsPerPagesLimits = ()=>{
203
+ return [5,10,15,20,25,30,40,50,60,80,100,500,1000,1500,2000,2500,3000,3500,4000,4500,5000,...(isDesktopMedia() ? [6000,7000,8000,9000,10000]:[])];
204
+ }
205
+
201
206
 
202
207
  export const arrayValueSeparator = ", ";
@@ -8,7 +8,6 @@
8
8
  import Datagrid from "./IndexComponent";
9
9
  import {defaultStr,defaultObj,defaultVal,isNonNullString,defaultNumber,isObjOrArray,isObj,extendObj} from "$utils";
10
10
  import {Pressable} from "react-native";
11
- import SimpleSelect from "$ecomponents/SimpleSelect";
12
11
  import React from "$react";
13
12
  import Auth from "$cauth";
14
13
  import DateLib from "$lib/date";
@@ -20,13 +19,10 @@ import Icon from "$ecomponents/Icon";
20
19
  import Label from "$ecomponents/Label";
21
20
  import { StyleSheet,View } from "react-native";
22
21
  import theme from "$theme";
23
- import useSWR,{useInfinite} from "$swr";
22
+ import useSWR from "$swr";
24
23
  import appConfig from "$capp/config";
25
- import APP from "$capp/instance";
26
-
24
+ import {getRowsPerPagesLimits} from "./Common/utils";
27
25
  import PropTypes from "prop-types";
28
- import {isDesktopMedia} from "$dimensions";
29
- import ActivityIndicator from "$ecomponents/ActivityIndicator";
30
26
  import {Menu} from "$ecomponents/BottomSheet";
31
27
  import session from "$session";
32
28
 
@@ -69,9 +65,7 @@ export const getSWROptions = ()=>{
69
65
  }
70
66
  }
71
67
 
72
- const getDefaultPaginationRowsPerPageItems = ()=>{
73
- return [5,10,15,20,25,30,40,50,60,80,100,500,1000,...(isDesktopMedia() ? [1500,2000,2500,3000,3500,4000,4500,5000,10000]:[])];
74
- }
68
+
75
69
  /****la fonction fetcher doit toujours retourner :
76
70
  * 1. la liste des éléments fetchés dans la props data
77
71
  * 2. le nombre total d'éléments de la liste obtenue en escluant les clause limit et offset correspondant à la même requête
@@ -160,6 +154,7 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
160
154
  if(fetchPath){
161
155
  fetchPath = setQueryParams(fetchPath,"SWRFetchPathKey",fPathRef.current)
162
156
  }
157
+ const sortRef = React.useRef({});
163
158
  const innerRef = React.useRef(null);
164
159
  const showProgressRef = React.useRef(true);
165
160
  const dataRef = React.useRef([]);
@@ -181,9 +176,8 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
181
176
  }
182
177
  opts = defaultObj(opts);
183
178
  opts.fetchOptions = isObj(opts.fetchOptions)? Object.clone(opts.fetchOptions) : {};
184
- opts.fetchOptions.withTotal = true;
185
- opts.fetchOptions.fields = fetchFields;
186
179
  extendObj(true,opts.fetchOptions,fetchOptionsRef.current?.fetchOptions);
180
+ opts.fetchOptions.sort = sortRef.current;
187
181
  if(canHandleLimit){
188
182
  opts.fetchOptions.limit = limitRef.current;
189
183
  opts.fetchOptions.page = pageRef.current -1;
@@ -194,7 +188,6 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
194
188
  delete opts.page;
195
189
  delete opts.offset;
196
190
  }
197
-
198
191
  const fetchCB = ({data,total})=>{
199
192
  totalRef.current = total;
200
193
  dataRef.current = data;
@@ -291,7 +284,7 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
291
284
  text : "Limite nbre elts par page",
292
285
  divider : true,
293
286
  }]
294
- getDefaultPaginationRowsPerPageItems().map((item)=>{
287
+ getRowsPerPagesLimits().map((item)=>{
295
288
  itLimits.push({
296
289
  text : item.formatNumber(),
297
290
  icon : limitRef.current == item ? 'check' : null,
@@ -314,8 +307,8 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
314
307
  {...rest}
315
308
  sort = {sort}
316
309
  onSort = {({sort})=>{
310
+ sortRef.current = sort;
317
311
  if(!canSortRemotely()) return;
318
- fetchOptionsRef.current.sort = sort;
319
312
  pageRef.current = firstPage;
320
313
  doRefresh(true);
321
314
  return false;
@@ -380,7 +373,7 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
380
373
  {...iconProp}
381
374
  title = {"Aller à la page suivante {0}".sprintf(nextPage && nextPage.formatNumber()||undefined)}
382
375
  name="material-keyboard-arrow-right"
383
- disabled = {nextPage >= totalPages || getNextPage() === false ? true : false}
376
+ disabled = {nextPage > totalPages || getNextPage() === false ? true : false}
384
377
  onPress = {()=>{
385
378
  const page = getNextPage();
386
379
  if(page === false) return;
@@ -416,6 +409,7 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
416
409
  opts = getFetchOptions({showError:showProgressRef.current,...opts});
417
410
  isInitializedRef.current = true;
418
411
  fetchOptionsRef.current = opts;
412
+ sortRef.current = opts.fetchOptions.sort;
419
413
  if(force){
420
414
  pageRef.current = firstPage;
421
415
  }
@@ -167,26 +167,26 @@ const DatagridFactory = (Factory)=>{
167
167
  const progressBar = this.getProgressBar();
168
168
  const pointerEvents = this.getPointerEvents();
169
169
 
170
- let restItems = [...this.renderCustomMenu()];
170
+ const restItems = [...this.renderCustomMenu()];
171
171
  let max = this.getMaxSelectableRows();
172
172
  if(selectableMultiple && max){
173
173
  max = max.formatNumber();
174
- restItems = [
175
- ...(selectableMultiple && !canRenderChart ? [{
174
+ if(selectableMultiple && !canRenderChart){
175
+ restItems.push({
176
176
  label : "Sélectionner "+max.formatNumber(),
177
177
  icon : "select-all",
178
178
  onPress : (x,event)=>{
179
179
  this.handleAllRowsToggle(true);
180
180
  }
181
- },
182
- {
181
+ });
182
+ restItems.push({
183
183
  label : "Tout désélectionner",
184
184
  onPress : (x,event)=>{
185
185
  this.handleAllRowsToggle(false);
186
186
  },
187
187
  icon : "select"
188
- }] : [])
189
- ]
188
+ });
189
+ }
190
190
  }
191
191
  const maxHeight = this.getMaxListHeight();
192
192
  const rPagination = showPagination ? <View style={[styles.paginationContainer]}>
@@ -923,7 +923,7 @@ class DropdownComponent extends AppComponent {
923
923
  }}
924
924
  containerProps = {{...containerProps,style:[containerProps.style,styles.mbO]}}
925
925
  error = {!!error}
926
- right = {loadingElement ? (loadingElement) : disabled? null : (props)=>{
926
+ right = {loadingElement ? loadingElement : disabled? null : (props)=>{
927
927
  let r = React.isValidElement(defRight)?<>{defRight}</> : <></>;
928
928
  if(typeof defRight =='function'){
929
929
  const t = defRight(props);
@@ -1,15 +1,15 @@
1
1
  import {defaultObj,defaultStr} from "$utils";
2
2
  import PropTypes from "prop-types";
3
3
  import theme,{Colors} from "$theme";
4
- import {isIos} from "$cplatform";
5
4
  import React from "$react";
6
5
  import Icon from "./Icon";
6
+ import {CHECKED_ICON,UNCHECKED_ICON} from "$ecomponents/Icon/utils";
7
7
 
8
8
  /****** icon de type checkbox variant en fonction de l'environnement ou la plateforme */
9
9
  const Checkbox = React.forwardRef((props,ref)=>{
10
10
  const {checked:customChecked,color:customColor,primary,secondary,primaryOnCheck,secondaryOnCheck,onChange,checkedIcon:customCheckedIcon,uncheckedIcon:customUncheckedIcon,onPress,...rest} = props;
11
- const checkedIcon = defaultStr(checkedIcon,isIos()? 'check' : "checkbox-marked");
12
- const uncheckedIcon = defaultStr(uncheckedIcon,"checkbox-blank-outline");
11
+ const checkedIcon = defaultStr(checkedIcon,CHECKED_ICON);
12
+ const uncheckedIcon = defaultStr(uncheckedIcon,UNCHECKED_ICON);
13
13
  const [checked,setIsChecked] = React.useStateIfMounted(!!checked);
14
14
  const isMounted = React.useIsMounted();
15
15
  const prevChecked = React.usePrevious(checked);
@@ -1,4 +1,3 @@
1
- //import MaterialIcon from "./MaterialCommunityIcon";
2
1
  import {defaultVal,defaultObj,defaultStr,isNonNullString} from "$utils";
3
2
  import PropTypes from "prop-types";
4
3
  import Tooltip from "$ecomponents/Tooltip";
@@ -12,3 +12,7 @@ export const PRINT_ICON = "printer";
12
12
 
13
13
  export const ICON_SIZE = 24;
14
14
  export const ICON_OFFSET = 12;
15
+
16
+ export const CHECKED_ICON = isIos()? 'check' : "checkbox-marked";
17
+
18
+ export const UNCHECKED_ICON = "checkbox-blank-outline";
@@ -9,9 +9,23 @@ const ScrollViewComponent = React.forwardRef(({virtualized,contentProps,containe
9
9
  const isKeyboardOpenRef = React.useRef(false);
10
10
  const testID = defaultStr(customTestID,'RN_ScrollViewComponent');
11
11
  containerProps = defaultObj(containerProps)
12
+ const layoutRef = React.useRef(null);
12
13
  const [layout,setLayout] = React.useState(Dimensions.get("window"));
13
14
  const {height} = layout;
15
+ const hasInitializedRef = React.useRef(false);
14
16
  const children = React.useStableMemo(()=>cChildren,[cChildren]);
17
+ const updateLayout = ()=>{
18
+ return new Promise((resolve)=>{
19
+ if(layoutRef.current && layoutRef.current.measureInWindow){
20
+ layoutRef.current.measureInWindow((x, y, width, height) => {
21
+ const r = { x, y, width, height };
22
+ setLayout({...Dimensions.get("window"),layout:r});
23
+ hasInitializedRef.current = true;
24
+ resolve(r);
25
+ });
26
+ }
27
+ })
28
+ }
15
29
  React.useEffect(()=>{
16
30
  const onKeyboardToggle = ({visible})=>{
17
31
  isKeyboardOpenRef.current = visible;
@@ -19,7 +33,7 @@ const ScrollViewComponent = React.forwardRef(({virtualized,contentProps,containe
19
33
  const onResizePage = ()=>{
20
34
  setTimeout(()=>{
21
35
  if(isKeyboardOpenRef.current) return;
22
- setLayout(Dimensions.get("window"))
36
+ updateLayout();
23
37
  },300);
24
38
  }
25
39
  APP.on(APP.EVENTS.RESIZE_PAGE,onResizePage);
@@ -29,8 +43,21 @@ const ScrollViewComponent = React.forwardRef(({virtualized,contentProps,containe
29
43
  APP.off(APP.EVENTS.KEYBOARD_DID_TOGGLE,onKeyboardToggle);
30
44
  }
31
45
  },[]);
32
- const contentContainerStyle = [{maxHeight:Math.max(height-100,250),width:'100%'},rest.contentContainerStyle];
33
- return <View {...containerProps} style={[theme.styles.w100,containerProps.style]} testID={testID+"_ScrollViewContainer"}>
46
+
47
+ const cStyle = {maxHeight:Math.max(height-100,250),width:'100%'};
48
+ if(isObj(layout.layout) && typeof layout.layout.y =='number'){
49
+ const {layout : {x,y}} = layout;
50
+ const minHeight = height - y;
51
+ if(minHeight> 0){
52
+ cStyle.minHeight = minHeight;
53
+ }
54
+ }
55
+ const contentContainerStyle = [cStyle,rest.contentContainerStyle];
56
+ return <View ref={layoutRef} onLayout={()=>{
57
+ if(!hasInitializedRef.current){
58
+ updateLayout();
59
+ }
60
+ }} {...containerProps} style={[theme.styles.w100,containerProps.style]} testID={testID+"_ScrollViewContainer"}>
34
61
  <ScrollView
35
62
  ref={ref} {...rest}
36
63
  testID={testID}