@fto-consult/expo-ui 2.31.4 → 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.4",
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.17",
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",
@@ -40,7 +40,8 @@ import {styles as tableStyles} from "$ecomponents/Table";
40
40
  import {DialogProvider} from "$ecomponents/Form/FormData";
41
41
  import Chart,{getMaxSupportedSeriesSize} from "$ecomponents/Chart";
42
42
  import notify from "$cnotify";
43
- import Divider from "$ecomponents/Divider";
43
+ import FileSystem from "$file-system";
44
+ import sprintf from "$cutils/sprintf";
44
45
 
45
46
  export const donutChart = {
46
47
  isChart : true,
@@ -244,6 +245,8 @@ export default class CommonDatagridComponent extends AppComponent {
244
245
  displayTypes : {value : hasFoundDisplayTypes ? disTypes : Object.clone(displayTypes)},
245
246
  dateFields : {value : {}},
246
247
  sectionListColumnsSize : {value : {current:0}}, //la taille du nombre d'éléments de section dans les colonnes
248
+ chartRef : {value : {current:null}},
249
+ chartSeriesNamesColumnsMapping : {value : {}},//le mappage entre les index des series et les colonnes coorespondantes
247
250
  })
248
251
  const sessionAggregator = defaultStr(this.getSessionData("aggregatorFunction")).trim();
249
252
  const aggregatorProps = defaultStr(this.props.aggregatorFunction).trim();
@@ -1404,7 +1407,7 @@ export default class CommonDatagridComponent extends AppComponent {
1404
1407
  DialogProvider.open({
1405
1408
  title : 'Configuration des graphes',
1406
1409
  subtitle : false,
1407
- data : this.getCharConfig(),
1410
+ data : this.getChartConfig(),
1408
1411
  fields : {
1409
1412
  x : {
1410
1413
  text : 'Axe des x[horizontal]',
@@ -1434,6 +1437,14 @@ export default class CommonDatagridComponent extends AppComponent {
1434
1437
  items : yItems,
1435
1438
  multiple : true,
1436
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
+ },
1437
1448
  stacked : stackSettings,
1438
1449
  sparkline : {
1439
1450
  type : 'switch',
@@ -1450,6 +1461,12 @@ export default class CommonDatagridComponent extends AppComponent {
1450
1461
  titleColor: {
1451
1462
  text : "Couleur de titre",
1452
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"
1453
1470
  },
1454
1471
  },
1455
1472
  actions : [
@@ -1472,7 +1489,7 @@ export default class CommonDatagridComponent extends AppComponent {
1472
1489
  })
1473
1490
  })
1474
1491
  }
1475
- getCharConfig(){
1492
+ getChartConfig(){
1476
1493
  return defaultObj(this.state.chartConfig);
1477
1494
  }
1478
1495
  getChartIsRendableArgs(){
@@ -1485,6 +1502,30 @@ export default class CommonDatagridComponent extends AppComponent {
1485
1502
  isDashboard(){
1486
1503
  return false;
1487
1504
  }
1505
+ /*** télécharge le chart actif */
1506
+ downloadChart(){
1507
+ if(!this.chartRef.current || !this.chartRef.current.dataURI) return Promise.reject({message:'Référence du graphique non valide'});
1508
+ return this.chartRef.current.dataURI().then(({ imgURI, blob })=>{
1509
+ const config = this.getChartConfig();
1510
+ const fileName = sprintf(defaultStr(config.fileName,config.title,"graphe"));
1511
+ FileSystem.write({content:imgURI,fileName})
1512
+ });
1513
+ }
1514
+ ///reoturne les options de menus à appliquer sur le char
1515
+ getChartMenus(){
1516
+ return [
1517
+ {
1518
+ text : "Options du graphe",
1519
+ textBold : true,
1520
+ divider:true,
1521
+ },
1522
+ {
1523
+ text :"Télécharger",
1524
+ icon : "download",
1525
+ onPress : this.downloadChart.bind(this),
1526
+ }
1527
+ ]
1528
+ }
1488
1529
  renderDisplayTypes(){
1489
1530
  const m = [];
1490
1531
  let activeType = null,hasFoundChart = false,hasFoundTable = false;
@@ -1539,6 +1580,11 @@ export default class CommonDatagridComponent extends AppComponent {
1539
1580
  if(!isMobileOrTabletMedia()){
1540
1581
  m.unshift({text:"Type d'affichage des données",divider:true,textBold:true});
1541
1582
  }
1583
+ if(hasFoundChart){
1584
+ Object.map(this.getChartMenus(),(c,i)=>{
1585
+ m.push(c);
1586
+ })
1587
+ }
1542
1588
  return <Menu
1543
1589
  title = "Type d'affichage"
1544
1590
  items = {m}
@@ -1594,7 +1640,7 @@ export default class CommonDatagridComponent extends AppComponent {
1594
1640
 
1595
1641
  const code = aggregatorFunction.code;
1596
1642
  const isDonut = chartType.isDonut || chartType.isRadial;
1597
- const config = this.getCharConfig();
1643
+ const config = this.getChartConfig();
1598
1644
  //@see : https://apexcharts.com/docs/series/
1599
1645
  ///lorsqu'on affiche uniquement les totaux des sections, alors la visualition se fait sur uniquement sur la base des valeurs
1600
1646
  ///on parcoure uniquement les entêtes des sectionLis
@@ -1617,6 +1663,7 @@ export default class CommonDatagridComponent extends AppComponent {
1617
1663
  seriesConfig = defaultSectionListHeadersSeries;
1618
1664
  }
1619
1665
  }
1666
+ const seriesNamesMapping = {};
1620
1667
  /**** boucle sur chaque éléments trouvée dans le tableau des données sectionListData */
1621
1668
  const loopForFooter = ({column,serieName,footers,header})=>{
1622
1669
  if(!isObj(column) || !isObj(footers)) return null;
@@ -1635,12 +1682,13 @@ export default class CommonDatagridComponent extends AppComponent {
1635
1682
  if(!isObj(this.sectionListHeaderFooters[header])) return null;
1636
1683
  const footers = this.sectionListHeaderFooters[header];
1637
1684
  if(isDonut){
1638
- loopForFooter({footers,header,column:yAxisColumn})
1685
+ loopForFooter({footers,header,column:yAxisColumn,columnField:yAxisColumn.field})
1639
1686
  } else {
1640
1687
  seriesConfig.map((s)=>{
1641
1688
  const serie = this.state.columns[s];
1642
1689
  const serieName = defaultStr(serie.label,serie.text,s);
1643
- loopForFooter({footers,serie,serieName,header,column:tableFooters[s]})
1690
+ this.chartSeriesNamesColumnsMapping[serieName] = serie;
1691
+ loopForFooter({footers,serie,columnField:s,serieName,header,column:tableFooters[s]})
1644
1692
  })
1645
1693
  }
1646
1694
  });
@@ -1679,7 +1727,7 @@ export default class CommonDatagridComponent extends AppComponent {
1679
1727
  return null;
1680
1728
  }
1681
1729
  const isDonut = chartType.isDonut || chartType.isRadial;
1682
- const config = this.getCharConfig();
1730
+ const config = this.getChartConfig();
1683
1731
  if(!this.state.columns[config.y]) return null;
1684
1732
  const yAxisColumn = this.state.columns[config.y];
1685
1733
  const type = defaultStr(yAxisColumn.jsType,yAxisColumn.type).toLowerCase();
@@ -1704,6 +1752,10 @@ export default class CommonDatagridComponent extends AppComponent {
1704
1752
  const indexes = {};
1705
1753
  let series = [],xaxis = {},customConfig = {},seriesNamesToColumns={};
1706
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
+ });
1707
1759
  if(!this.isSectionList()){
1708
1760
  this.state.data.map((data,index)=>{
1709
1761
  if(!isObj(data))return null;
@@ -1743,6 +1795,7 @@ export default class CommonDatagridComponent extends AppComponent {
1743
1795
  type : chartType.type,
1744
1796
  data,
1745
1797
  })
1798
+ this.chartSeriesNamesColumnsMapping[name] = col;
1746
1799
  })
1747
1800
  } else {
1748
1801
  const configs = this.getSectionListHeadersChartOptions({chartType,aggregatorFunction,xAxisColumn,yAxisColumn});
@@ -1765,8 +1818,25 @@ export default class CommonDatagridComponent extends AppComponent {
1765
1818
  chartProps[settingKey] = defaultObj(chartProps[settingKey]);
1766
1819
  chartProps[settingKey][key] = config[key];
1767
1820
  });
1821
+ const dataLabelFormatter = typeof chartProps.dataLabels?.formatter =="function"? chartProps.dataLabels.formatter : undefined;
1768
1822
  const chartOptions = {
1769
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
+ }),
1770
1840
  title :extendObj(true,{}, {
1771
1841
  text: defaultStr(config.title,chartProps.title),
1772
1842
  align: 'left',
@@ -1782,9 +1852,11 @@ export default class CommonDatagridComponent extends AppComponent {
1782
1852
  },
1783
1853
  },chartProps.title),
1784
1854
  series,
1785
- chart : extendObj(true,{},{height :this.isDashboard()?80:350},chartProps.chart,{
1786
- type : chartType.type,
1787
- })
1855
+ chart : extendObj(true,{},
1856
+ {toolbar : {show : false}},
1857
+ {height :this.isDashboard()?80:350},chartProps.chart,
1858
+ {type : chartType.type}
1859
+ )
1788
1860
  }
1789
1861
  const labelColor = theme.Colors.isValid(config.labelColor)? config.labelColor : theme.colors.text;
1790
1862
  if(!isDonut){
@@ -1800,9 +1872,31 @@ export default class CommonDatagridComponent extends AppComponent {
1800
1872
  const yLabels = chartOptions.yaxis.labels = defaultObj(chartOptions.yaxis.labels);
1801
1873
  yLabels.style = defaultObj(yLabels.style)
1802
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
+ }
1803
1891
  yLabels.formatter = (value)=>{
1804
- if(typeof value =="number") return value.formatNumber();
1805
- 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();
1806
1900
  }
1807
1901
  chartOptions.chart.id = this.chartIdPrefix+"-"+defaultStr(chartType.key,"no-key");
1808
1902
  if(!chartType.isDonut){
@@ -1825,8 +1919,12 @@ export default class CommonDatagridComponent extends AppComponent {
1825
1919
  chartOptions.legend = defaultObj(chartOptions.legend);
1826
1920
  chartOptions.legend.show = false;
1827
1921
  }
1922
+ if("dataLabels" in config){
1923
+ chartOptions.dataLabels.enabled = !!config.dataLabels;
1924
+ }
1828
1925
  return <Chart
1829
1926
  options = {chartOptions}
1927
+ ref = {this.chartRef}
1830
1928
  key = {chartOptions.chart.id+"-"+this.state.displayType}
1831
1929
  />
1832
1930
  }
@@ -2101,7 +2199,7 @@ export default class CommonDatagridComponent extends AppComponent {
2101
2199
  mItem.right = (p)=>{
2102
2200
  return <Icon name="material-settings" {...p} onPress={(e)=>{
2103
2201
  //React.stopEventPropagation(e);
2104
- this.configureSectionListColumn(mItem);
2202
+ this.configureSectionListColumn({...mItem,...defaultObj(sectionListColumns[field])});
2105
2203
  //return false;
2106
2204
  }}/>
2107
2205
  }
@@ -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",
@@ -1,7 +1,7 @@
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 {defaultStr,base64toBlob,isNonNullString,getFileName,getFileExtension,defaultNumber,defaultBool,dataURLToBase64,isBlob,isBase64,isDataURL} from "$utils";
4
+ import {defaultStr,base64toBlob,dataURLToBlob,getTypeFromDataURL,isNonNullString,getFileName,getFileExtension,defaultNumber,defaultBool,dataURLToBase64,isBlob,isBase64,isDataURL} from "$utils";
5
5
  const FileSaver = require('file-saver');
6
6
  const mime = require('mime-types')
7
7
  const XLSX = require("xlsx");
@@ -29,6 +29,13 @@ import Preloader from "$preloader";
29
29
  reject({status:false,msg:'Nom de fichier invalide'});
30
30
  return;
31
31
  }
32
+ if(isDataURL(content)){
33
+ const type = getTypeFromDataURL(content);
34
+ content = dataURLToBlob(content);
35
+ if(isNonNullString(type)){
36
+ contentType = type;
37
+ }
38
+ }
32
39
  content = isBlob(content)? content : new Blob([content], { type: content?.type||contentType})
33
40
  try {
34
41
  FileSaver.saveAs(content, fileName);
@@ -55,10 +62,6 @@ function s2ab(s) {
55
62
  * .xls : application/vnd.ms-excel
56
63
  */
57
64
  export const writeExcel = ({workbook,content,contentType,fileName,...rest})=>{
58
- if(!isNonNullString(contentType) || !contentType.contains("application/vnd.")){
59
- //contentType = "application/vnd.ms-excel";
60
- contentType : "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
61
- }
62
65
  let ext = defaultStr(getFileExtension(fileName,true),"xlsx");
63
66
  fileName = sanitizeFileName(getFileName(fileName,true))+"."+ext;
64
67
  if(!isNonNullString(fileName)){
@@ -70,11 +73,31 @@ export const writeExcel = ({workbook,content,contentType,fileName,...rest})=>{
70
73
  if(isBlob(content)){
71
74
  return write({...rest,content,fileName,contentType})
72
75
  }
73
- Preloader.open("génération du fichier excel "+fileName);
74
- XLSX.writeFile(workbook, fileName);
75
- setTimeout(()=>{
76
- Preloader.close();
77
- },1000);
76
+ return new Promise((resolve,reject)=>{
77
+ Preloader.open("génération du fichier excel "+fileName);
78
+ try {
79
+ XLSX.writeFile(workbook, fileName);
80
+ setTimeout(()=>{
81
+ Preloader.close();
82
+ resolve({fileName});
83
+ },1000);
84
+ } catch(e){
85
+ reject(e);
86
+ }
87
+ })
88
+ }
89
+
90
+ /***
91
+ * @see https://ourtechroom.com/tech/mime-type-for-excel/ for excel mimesTypes
92
+ * .xls : application/vnd.ms-excel
93
+ */
94
+ export const writeImage = ({content,fileName,...rest})=>{
95
+ let ext = defaultStr(getFileExtension(fileName,true),"png");
96
+ fileName = sanitizeFileName(getFileName(fileName,true))+"."+ext;
97
+ if(!isNonNullString(fileName)){
98
+ return Promise.reject({status:false,message:'Nom de fichier invalide pour le contenu excel à créer'});
99
+ }
100
+
78
101
  }
79
102
 
80
103