@fto-consult/expo-ui 2.31.5 → 2.32.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 CHANGED
@@ -11,4 +11,9 @@
11
11
 
12
12
  /***les fonctions d'aggregations du datagrid**/
13
13
  datagridAggregatorFunctions : {objectOf({code:{string},label:{string},eval:{function}})|| arrayOf({code:{string},label:{string},eval:{function}})}
14
+ /**l'ensemble des selectors à appliquer au champs de type hashtag, permettant de faire des liens vers d'autres tables**/
15
+ sprintfSelectors : {
16
+ "&libele&" : selectLabel,
17
+ }
18
+
14
19
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fto-consult/expo-ui",
3
- "version": "2.31.5",
3
+ "version": "2.32.0",
4
4
  "description": "Bibliothèque de composants UI Expo,react-native",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -61,7 +61,7 @@
61
61
  "@emotion/native": "^11.10.0",
62
62
  "@expo/html-elements": "^0.2.0",
63
63
  "@expo/vector-icons": "^13.0.0",
64
- "@fto-consult/common": "^1.25.24",
64
+ "@fto-consult/common": "^1.25.25",
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",
@@ -41,6 +41,7 @@ import {DialogProvider} from "$ecomponents/Form/FormData";
41
41
  import Chart,{getMaxSupportedSeriesSize} from "$ecomponents/Chart";
42
42
  import notify from "$cnotify";
43
43
  import FileSystem from "$file-system";
44
+ import sprintf from "$cutils/sprintf";
44
45
 
45
46
  export const donutChart = {
46
47
  isChart : true,
@@ -245,6 +246,7 @@ export default class CommonDatagridComponent extends AppComponent {
245
246
  dateFields : {value : {}},
246
247
  sectionListColumnsSize : {value : {current:0}}, //la taille du nombre d'éléments de section dans les colonnes
247
248
  chartRef : {value : {current:null}},
249
+ chartSeriesNamesColumnsMapping : {value : {}},//le mappage entre les index des series et les colonnes coorespondantes
248
250
  })
249
251
  const sessionAggregator = defaultStr(this.getSessionData("aggregatorFunction")).trim();
250
252
  const aggregatorProps = defaultStr(this.props.aggregatorFunction).trim();
@@ -1405,7 +1407,7 @@ export default class CommonDatagridComponent extends AppComponent {
1405
1407
  DialogProvider.open({
1406
1408
  title : 'Configuration des graphes',
1407
1409
  subtitle : false,
1408
- data : this.getCharConfig(),
1410
+ data : this.getChartConfig(),
1409
1411
  fields : {
1410
1412
  x : {
1411
1413
  text : 'Axe des x[horizontal]',
@@ -1435,6 +1437,14 @@ export default class CommonDatagridComponent extends AppComponent {
1435
1437
  items : yItems,
1436
1438
  multiple : true,
1437
1439
  },
1440
+ dataLabels : {
1441
+ type : 'switch',
1442
+ label : "Affich les valeurs",
1443
+ checkedTooltip : "Les étiquettes de valeurs seront affichées sur le graphe",
1444
+ checkedValue : true,
1445
+ uncheckedValue : false,
1446
+ defaultValue : this.isDashboard()? true : false,
1447
+ },
1438
1448
  stacked : stackSettings,
1439
1449
  sparkline : {
1440
1450
  type : 'switch',
@@ -1451,6 +1461,12 @@ export default class CommonDatagridComponent extends AppComponent {
1451
1461
  titleColor: {
1452
1462
  text : "Couleur de titre",
1453
1463
  type :"color",
1464
+ format : "hashtag"
1465
+ },
1466
+ fileName : {
1467
+ text : "Nom fichier en téléchargement",
1468
+ format : "hashtag",
1469
+ tooltip : "Veuillez spécifier le nom du fichier du graphe qui sera utilisée lors du téléchargement"
1454
1470
  },
1455
1471
  },
1456
1472
  actions : [
@@ -1473,7 +1489,7 @@ export default class CommonDatagridComponent extends AppComponent {
1473
1489
  })
1474
1490
  })
1475
1491
  }
1476
- getCharConfig(){
1492
+ getChartConfig(){
1477
1493
  return defaultObj(this.state.chartConfig);
1478
1494
  }
1479
1495
  getChartIsRendableArgs(){
@@ -1490,7 +1506,9 @@ export default class CommonDatagridComponent extends AppComponent {
1490
1506
  downloadChart(){
1491
1507
  if(!this.chartRef.current || !this.chartRef.current.dataURI) return Promise.reject({message:'Référence du graphique non valide'});
1492
1508
  return this.chartRef.current.dataURI().then(({ imgURI, blob })=>{
1493
- FileSystem.write({content:imgURI,fileName:"graphe.png",contentType:"image/png"})
1509
+ const config = this.getChartConfig();
1510
+ const fileName = sprintf(defaultStr(config.fileName,config.title,"graphe"));
1511
+ FileSystem.write({content:imgURI,fileName})
1494
1512
  });
1495
1513
  }
1496
1514
  ///reoturne les options de menus à appliquer sur le char
@@ -1622,7 +1640,7 @@ export default class CommonDatagridComponent extends AppComponent {
1622
1640
 
1623
1641
  const code = aggregatorFunction.code;
1624
1642
  const isDonut = chartType.isDonut || chartType.isRadial;
1625
- const config = this.getCharConfig();
1643
+ const config = this.getChartConfig();
1626
1644
  //@see : https://apexcharts.com/docs/series/
1627
1645
  ///lorsqu'on affiche uniquement les totaux des sections, alors la visualition se fait sur uniquement sur la base des valeurs
1628
1646
  ///on parcoure uniquement les entêtes des sectionLis
@@ -1645,6 +1663,7 @@ export default class CommonDatagridComponent extends AppComponent {
1645
1663
  seriesConfig = defaultSectionListHeadersSeries;
1646
1664
  }
1647
1665
  }
1666
+ const seriesNamesMapping = {};
1648
1667
  /**** boucle sur chaque éléments trouvée dans le tableau des données sectionListData */
1649
1668
  const loopForFooter = ({column,serieName,footers,header})=>{
1650
1669
  if(!isObj(column) || !isObj(footers)) return null;
@@ -1663,12 +1682,13 @@ export default class CommonDatagridComponent extends AppComponent {
1663
1682
  if(!isObj(this.sectionListHeaderFooters[header])) return null;
1664
1683
  const footers = this.sectionListHeaderFooters[header];
1665
1684
  if(isDonut){
1666
- loopForFooter({footers,header,column:yAxisColumn})
1685
+ loopForFooter({footers,header,column:yAxisColumn,columnField:yAxisColumn.field})
1667
1686
  } else {
1668
1687
  seriesConfig.map((s)=>{
1669
1688
  const serie = this.state.columns[s];
1670
1689
  const serieName = defaultStr(serie.label,serie.text,s);
1671
- loopForFooter({footers,serie,serieName,header,column:tableFooters[s]})
1690
+ this.chartSeriesNamesColumnsMapping[serieName] = serie;
1691
+ loopForFooter({footers,serie,columnField:s,serieName,header,column:tableFooters[s]})
1672
1692
  })
1673
1693
  }
1674
1694
  });
@@ -1707,7 +1727,7 @@ export default class CommonDatagridComponent extends AppComponent {
1707
1727
  return null;
1708
1728
  }
1709
1729
  const isDonut = chartType.isDonut || chartType.isRadial;
1710
- const config = this.getCharConfig();
1730
+ const config = this.getChartConfig();
1711
1731
  if(!this.state.columns[config.y]) return null;
1712
1732
  const yAxisColumn = this.state.columns[config.y];
1713
1733
  const type = defaultStr(yAxisColumn.jsType,yAxisColumn.type).toLowerCase();
@@ -1732,6 +1752,10 @@ export default class CommonDatagridComponent extends AppComponent {
1732
1752
  const indexes = {};
1733
1753
  let series = [],xaxis = {},customConfig = {},seriesNamesToColumns={};
1734
1754
  let count = 0;
1755
+ //on réinitialise le mappage entre les index
1756
+ Object.map(this.chartSeriesNamesColumnsMapping,(_,k)=>{
1757
+ delete this.chartSeriesNamesColumnsMapping[k];
1758
+ });
1735
1759
  if(!this.isSectionList()){
1736
1760
  this.state.data.map((data,index)=>{
1737
1761
  if(!isObj(data))return null;
@@ -1771,6 +1795,7 @@ export default class CommonDatagridComponent extends AppComponent {
1771
1795
  type : chartType.type,
1772
1796
  data,
1773
1797
  })
1798
+ this.chartSeriesNamesColumnsMapping[name] = col;
1774
1799
  })
1775
1800
  } else {
1776
1801
  const configs = this.getSectionListHeadersChartOptions({chartType,aggregatorFunction,xAxisColumn,yAxisColumn});
@@ -1793,8 +1818,25 @@ export default class CommonDatagridComponent extends AppComponent {
1793
1818
  chartProps[settingKey] = defaultObj(chartProps[settingKey]);
1794
1819
  chartProps[settingKey][key] = config[key];
1795
1820
  });
1821
+ const dataLabelFormatter = typeof chartProps.dataLabels?.formatter =="function"? chartProps.dataLabels.formatter : undefined;
1796
1822
  const chartOptions = {
1797
1823
  ...chartProps,
1824
+ dataLabels : extendObj(true,{enabled:!this.isDashboard()},chartProps.dataLabels,{
1825
+ formatter : (value, { seriesIndex, dataPointIndex, w })=> {
1826
+ const serie = w.config.series[seriesIndex];
1827
+ const serieName = serie.name;
1828
+ const column = isDonut ? defaultObj((this.state.columns[yAxisColumn.field],yAxisColumn)): defaultObj(this.chartSeriesNamesColumnsMapping[serieName]);
1829
+ const columnField = defaultStr(column.field, isDonut? config.y : undefined);
1830
+ if(dataLabelFormatter){
1831
+ return dataLabelFormatter({value,column,columnDef:column,columnField,serie,serieName,seriesIndex})
1832
+ }
1833
+ if(typeof value !=='number') return value;
1834
+ if(defaultStr(column.format).toLowerCase() ==='money'){
1835
+ return value.formatMoney();
1836
+ }
1837
+ return value.formatNumber();
1838
+ }
1839
+ }),
1798
1840
  title :extendObj(true,{}, {
1799
1841
  text: defaultStr(config.title,chartProps.title),
1800
1842
  align: 'left',
@@ -1830,9 +1872,31 @@ export default class CommonDatagridComponent extends AppComponent {
1830
1872
  const yLabels = chartOptions.yaxis.labels = defaultObj(chartOptions.yaxis.labels);
1831
1873
  yLabels.style = defaultObj(yLabels.style)
1832
1874
  yLabels.style.colors = (Array.isArray(yLabels.style.colors) && yLabels.style.colors.length || theme.Colors.isValid(yLabels.style.colors)) ? yLabels.style.colors : labelColor;
1875
+ const yLabelsSerieName = series?.length == 1 && series[0] && series[0].name ? series[0].name : undefined;
1876
+ const yLabelsColumn = yLabelsSerieName ? this.chartSeriesNamesColumnsMapping[yLabelsSerieName] : undefined;
1877
+ let yLabelFormat = null;
1878
+ if(!isDonut && !yLabelFormat){
1879
+ for(let i in series){
1880
+ const v = series[i];
1881
+ if(!isObj(v) || !isNonNullString(v.name) || !this.chartSeriesNamesColumnsMapping[v.name]) break;
1882
+ if(yLabelFormat && typeof this.chartSeriesNamesColumnsMapping[v.name]?.format !=yLabelFormat) {
1883
+ yLabelFormat = null;
1884
+ break;
1885
+ }
1886
+ if(!yLabelFormat){
1887
+ yLabelFormat = this.chartSeriesNamesColumnsMapping[v.name]?.format;
1888
+ }
1889
+ }
1890
+ }
1833
1891
  yLabels.formatter = (value)=>{
1834
- if(typeof value =="number") return value.formatNumber();
1835
- return value;
1892
+ if(typeof value !='number') return value;
1893
+ if(yLabelFormat =='money' || (isDonut && yAxisColumn.format =="money")){
1894
+ return value.formatMoney();
1895
+ }
1896
+ if(yLabelsColumn && yLabelsColumn.format =='money'){
1897
+ return value.formatMoney();
1898
+ }
1899
+ return value.formatNumber();
1836
1900
  }
1837
1901
  chartOptions.chart.id = this.chartIdPrefix+"-"+defaultStr(chartType.key,"no-key");
1838
1902
  if(!chartType.isDonut){
@@ -1855,6 +1919,9 @@ export default class CommonDatagridComponent extends AppComponent {
1855
1919
  chartOptions.legend = defaultObj(chartOptions.legend);
1856
1920
  chartOptions.legend.show = false;
1857
1921
  }
1922
+ if("dataLabels" in config){
1923
+ chartOptions.dataLabels.enabled = !!config.dataLabels;
1924
+ }
1858
1925
  return <Chart
1859
1926
  options = {chartOptions}
1860
1927
  ref = {this.chartRef}
@@ -505,9 +505,9 @@ export default class Field extends AppComponent {
505
505
  }
506
506
  sprintf(){
507
507
  if(this.isDisabled() || this.isReadOnly()) return this;
508
- sprintf({value:this.getValue(),cb:(val)=>{
508
+ sprintf({context:this,value:this.getValue(),formatter:this.props.sprintfResultFormatter}).then((val)=>{
509
509
  this.setValue(APP.sprintf(val),true);
510
- }});
510
+ });
511
511
  }
512
512
  /**** met le focus sur le prochain cham */
513
513
  focusNextField (){
@@ -915,7 +915,7 @@ export default class Field extends AppComponent {
915
915
  right = <Icon color={theme.colors.primary} {...props} tooltip = {tooltip} style={[styles.icon,props.style]} icon="information"/>;
916
916
  }
917
917
  if(canRenderHashtag && !rest.disabled && !rest.readOnly){
918
- const nRight = <Icon {...props} icon={"format-header-pound"} style={[right||cRight?{marginLeft:-5}:null,props.style]} title={"Cliquer insérer un hashtag, ie faire référence à une données de ventes, d'achas, de stocks, de règlement..."} onPress={this.sprintf.bind(this)}/>;
918
+ const nRight = <Icon {...props} icon={"format-header-pound"} style={[right||cRight?{marginLeft:-5}:null,props.style]} title={"Cliquer insérer un hashtag, ie faire référence à une données externe"} onPress={this.sprintf.bind(this)}/>;
919
919
  right = right ? <>{right}{nRight}</> : nRight;
920
920
  }
921
921
  return (cRight)? <>{cRight}{right}</> : right;
@@ -988,6 +988,8 @@ Field.propTypes = {
988
988
  PropTypes.node,
989
989
  PropTypes.func,
990
990
  ]),
991
+ /**** la fonction utilisée pour le formattage des valeurs de type sprintf */
992
+ sprintfResultFormatter : PropTypes.func,
991
993
  jsType : PropTypes.string,
992
994
  usePlaceholderWhenEmpty : PropTypes.bool,//si la valeur du placeholder sera utilée, lorsque la valeur du champ de type formatable est nulle ou égale à la valeur vide
993
995
  responsive : PropTypes.bool,
@@ -1,7 +1,7 @@
1
1
  import selectors from "./sprintfSelectors";
2
- import {isNonNullString,defaultFunc} from "$utils";
2
+ import {isNonNullString,defaultFunc,isObj,isPromise} from "$utils";
3
3
 
4
- export default ({value,val,cb,success})=>{
4
+ export default ({value,val,formatter,cb,success})=>{
5
5
  val = defaultStr(value,val)
6
6
  if(!isNonNullString(val)){
7
7
  return null;
@@ -16,27 +16,59 @@ export default ({value,val,cb,success})=>{
16
16
  keys.push(i.toUpperCase())
17
17
  }
18
18
  }
19
- let results = {};
19
+ const results = {};
20
20
  let length = keys.length;
21
21
  let index = length;
22
22
  let key = undefined
23
- const next = ()=>{
24
- index -=1;
25
- if(index < 0){
26
- for(let i in results){
27
- let t = selectors[i.toLowerCase()];
28
- let replace = results[i];
29
- let dbName = defaultStr(t.dbName).toLowerCase(), type = defaultStr(t.type).toLowerCase();
30
- if( dbName !== 'struct_data' && type !== 'field' && type !== 'form'){
31
- replace = " #"+defaultStr(t.dbName)+"["+i.ltrim("&").rtrim("&")+"-"+replace+"]".toUpperCase();
23
+ const sKeys = {};
24
+ return new Promise((resolve)=>{
25
+ const next = ()=>{
26
+ index -=1;
27
+ if(index < 0){
28
+ for(let i in results){
29
+ let indexName = i.toLowerCase();
30
+ let selector = selectors[indexName];
31
+ if(!selector){
32
+ indexName = i.toUpperCase();
33
+ selector = selectors[indexName];
34
+ }
35
+ const replace = results[i];
36
+ if(typeof formatter =='function'){
37
+ val = formatter({selectors,selector,value:val,replace,result:results[i],results,indexName,selectorIndex:indexName,selectorName:indexName})
38
+ }
39
+ if(typeof val =='string' && typeof replace =='string'){
40
+ val = val.replaceAll(i,replace);
41
+ }
32
42
  }
33
- val = val.replaceAll(i,replace.toUpperCase());
43
+ val = defaultStr(val);
44
+ cb(val);
45
+ resolve(val);
46
+ } else {
47
+ key = keys[index];
48
+ if(!isNonNullString(key)) {return next();}
49
+ let indexName = i.toLowerCase();
50
+ let selector = selectors[indexName];
51
+ if(!selector){
52
+ indexName = i.toUpperCase();
53
+ selector = selectors[indexName];
54
+ }
55
+ sKeys[i] = indexName;
56
+ const select = isObj(selector)? selector.select : selector;
57
+ if(typeof cb =='function'){
58
+ const v = select({...rest,selector,selectors,indexName,selectorIndex:indexName,selectorName:indexName,results,index:key,keys,key,value:val});
59
+ if(isPromise(v)){
60
+ return v.then((r)=>{
61
+ if(typeof r ==='string'){
62
+ results[key] = r;
63
+ }
64
+ }).finally(next);
65
+ } else if(typeof v =='string' || typeof v =='number'){
66
+ results[key] = v.toString();
67
+ }
68
+ }
69
+ next();
34
70
  }
35
- cb(val);
36
- } else {
37
- key = keys[index];
38
- if(!isNonNullString(key)) {return next();}
39
71
  }
40
- }
41
- next();
72
+ next();
73
+ })
42
74
  }
@@ -1,116 +1,19 @@
1
1
  import Date from "$lib/date";
2
- const selectLabel = {
3
- title : 'Sélectionner un libelé',
4
- table : 'labels',
5
- dbName : 'struct_data',
6
- itemValue : ({item})=>defaultStr(item.label,item.code)
7
- }
2
+ import {defaultObj} from "$utils";
3
+ import appConfig from "$capp/config";
4
+ const sprintfSelectors = defaultObj(appConfig.get("sprintfSelectors"));
8
5
  export default {
9
- "&libele&" : selectLabel,
10
- "&vente&" : {
11
- title : 'Sélectionner une vente',
12
- table : 'sales',
13
- },
14
- "&achat&" : {
15
- title : 'Sélectionner un achat',
16
- table : 'purchases',
17
- },
18
- "&stock&" : {
19
- title : 'Sélectionner stock',
20
- table : 'stocks',
21
- },
22
- "&transfert&" : {
23
- title : 'Sélectionner mouvement de transfert',
24
- table : 'stocks_transfers',
25
- },
26
- "&reglement&" : {
27
- title : 'Sélectionner un règlement',
28
- table : 'payments',
29
- },
30
- "&tiers&" : {
31
- title : 'Sélectionner un tier',
32
- table : 'third_parties',
33
- dbName : 'third_parties',
34
- },
35
- "&projet&" : {
36
- title : 'Sélectionner un projet',
37
- table : 'projects',
38
- dbName : 'projects',
39
- },
40
- "&tache&" : {
41
- title : 'Sélectionner une tâche',
42
- table : 'tasks',
43
- dbName : 'projects',
44
- },
45
- "&commercial&" : {
46
- title : 'Sélectionner commercial',
47
- table : 'users',
48
- dbName : 'users',
49
- fetchDataOpts:{
50
- selector : {$and:[{isSeller:1}]}
51
- }
52
- },
53
- "&client&" : {
54
- title : 'Sélectionner client',
55
- table : 'third_parties',
56
- dbName : 'third_parties',
57
- fetchDataOpts:{
58
- selector : {$and:[{type:{$in:["customer"]}}]}
59
- }
60
- },
61
- "&fournisseur&" : {
62
- title : 'Sélectionner Fournisseur',
63
- table : 'third_parties',
64
- dbName : 'third_parties',
65
- fetchDataOpts:{
66
- selector : {$and:[{type:{$in:["provider"]}}]}
67
- }
68
- },
69
- "&utilisateur&" : {
70
- title : 'Sélectionner un utilisateur',
71
- table : 'users',
72
- dbName : 'users',
73
- },
74
- "&caisse&" : {
75
- title : 'Sélectionner une caisse',
76
- table : 'checkouts',
77
- dbName : 'common',
78
- },
79
- "&produit&" : {
80
- title : 'Sélectionner un produit/article',
81
- table : 'products',
82
- dbName : 'products',
83
- },
84
- "&categorie&" : {
85
- title : 'Sélectionner une catégorie de produit',
86
- table : 'products_categories',
87
- dbName : 'products',
88
- },
89
- "&taxe&" : {
90
- title : 'Sélectionner une taxe',
91
- table : 'taxes',
92
- dbName : 'common',
93
- },
94
- "&depot&" : {
95
- title : 'Sélectionner un dépôt',
96
- table : 'warehouses',
97
- dbName : 'common',
98
- },
99
- "&contact&" : {
100
- title : 'Sélectionner un contact',
101
- table : 'contacts',
102
- dbName : 'common',
103
- },
6
+ ...sprintfSelectors,
104
7
  "&sdate&" : {
105
8
  title : 'Sélectionner une date',
106
9
  type : 'field',
107
10
  field : {
108
- type : 'date',
109
- text : 'Sélectionner une date',
110
- format : Date.defaultDateFormat
11
+ type : 'date',
12
+ text : 'Sélectionner une date',
13
+ format : Date.defaultDateFormat
111
14
  },
112
15
  desc : "remplace le motif par la date qui sera sélectionnée par l'utilisateur au format jj/mm/aaaa",
113
- },
16
+ },
114
17
  "&date&" : "remplace le motif par la date actuelle au format : jj/mm/aaaa",
115
18
  "&heure&" : "remplace le motif par l'heure actuelle au format hh:mm:ss",
116
19
  "&dateheure&" : "remplace le motif par la date et l'heure actuelle au format : jj/mm/aaaa hh:mm:ss",