@fto-consult/expo-ui 2.36.5 → 2.37.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fto-consult/expo-ui",
3
- "version": "2.36.5",
3
+ "version": "2.37.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": "^2.1.3",
64
+ "@fto-consult/common": "^2.1.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",
@@ -92,6 +92,7 @@
92
92
  "react-dom": "18.1.0",
93
93
  "react-native": "0.70.5",
94
94
  "react-native-big-list": "^1.6.0",
95
+ "react-native-blob-util": "^0.17.0",
95
96
  "react-native-gesture-handler": "~2.8.0",
96
97
  "react-native-paper": "^4.12.5",
97
98
  "react-native-paper-dates": "^0.9.2",
@@ -529,25 +529,7 @@ const DatagridFactory = (Factory)=>{
529
529
  {this.renderDisplayTypes()}
530
530
  {this.renderAggregatorFunctionsMenu()}
531
531
  {!canRenderChart ? <RenderType /> : null}
532
-
533
- {/*exportable && exportTableProps.pdf && (exportTableProps.pdf.footer) ? (
534
- <td width="40px">
535
- <ExportTable
536
- {...exportTableProps}
537
- excel = {false}
538
- ref = {(el)=>{
539
- if(el){
540
- this.exportDataInstance = el;
541
- }
542
- }}
543
- selector = {this.exportableDomId}
544
- getAllData = {()=>{
545
- return this.INITIAL_STATE.data;
546
- }}
547
- />
548
- </td>
549
- ) : null
550
- */}
532
+ {this.renderExportableMenu()}
551
533
  {/*filters !== false && <td ><LocalFilter title = {this.props.title} fields ={this.state.columns} onChange={this.onLocalFiltersChange.bind(this)}/></td>*/}
552
534
  </View>
553
535
  </ScrollView>
@@ -11,7 +11,7 @@ import {showConfirm} from "$ecomponents/Dialog";
11
11
  import Label from "$ecomponents/Label";
12
12
  import Icon,{COPY_ICON} from "$ecomponents/Icon";
13
13
  import filterUtils from "$cfilters";
14
- import {sortBy,isDecimal,defaultVal,extendObj,isObjOrArray,isObj,defaultNumber,defaultStr,isFunction,defaultBool,defaultArray,defaultObj,isNonNullString,defaultDecimal} from "$utils";
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";
16
16
  import React from "$react";
17
17
  import DateLib from "$lib/date";
@@ -37,6 +37,7 @@ import notify from "$cnotify";
37
37
  import FileSystem from "$file-system";
38
38
  import sprintf from "$cutils/sprintf";
39
39
  import { renderRowCell,formatValue } from "./utils";
40
+ import * as XLSX from "xlsx";
40
41
 
41
42
  export const TIMEOUT = 100;
42
43
 
@@ -1623,6 +1624,204 @@ export default class CommonDatagridComponent extends AppComponent {
1623
1624
  }
1624
1625
  ]
1625
1626
  }
1627
+ getExportableFields(){
1628
+ return {
1629
+ fileName : {
1630
+ text : "Nom du fichier",
1631
+ format : "hashtag",
1632
+ required : true,
1633
+ },
1634
+ displayTotals : {
1635
+ type : "switch",
1636
+ label : "Exporter les entêtes des groupes",
1637
+ defaultValue :1,
1638
+ },
1639
+ /*abreviateValues : {
1640
+ text : "Abréger les valeurs numériques",
1641
+ type : "switch",
1642
+ },*/
1643
+ dateFormat : {
1644
+ text : "Format des dates",
1645
+ type : "dateFormat",
1646
+ required : true,
1647
+ },
1648
+ }
1649
+ }
1650
+ /***@see : https://docs.sheetjs.com/docs/api/utilities */
1651
+ exportToExcel(){
1652
+ const skey = "export-to-excel";
1653
+ const isOnlytotal = this.state.displayOnlySectionListHeaders;
1654
+ let displayOnlyHeader = this.canDisplayOnlySectionListHeaders() && isOnlytotal;
1655
+ const sData = defaultObj(this.getSessionData(skey));
1656
+ return DialogProvider.open({
1657
+ title : "Paramètre d'export excel",
1658
+ data : sData,
1659
+ fields : {
1660
+ ...this.getExportableFields(),
1661
+ sheetName : {
1662
+ label : 'Nom de la feuille excel',
1663
+ format : "hashtag",
1664
+ required : true,
1665
+ },
1666
+ exportOnlyTotal : displayOnlyHeader && {
1667
+ label : "Exporter uniquemnt les totaux",
1668
+ defaultValue : 0,
1669
+ type : "switch",
1670
+ },
1671
+ aggregatedValues : displayOnlyHeader ? {
1672
+ label : "Exporter uniquement les totaux aggrégées",
1673
+ defaultValue : 0,
1674
+ type : "switch",
1675
+ } : null,
1676
+ addEmptyRowAfterTotal : {
1677
+
1678
+ },
1679
+ },
1680
+ actions : [{text:'Exporter',icon : "check"}],
1681
+ onSuccess:({data:config})=>{
1682
+ this.setSessionData(skey,config);
1683
+ config.fileName = sprintf(config.fileName);
1684
+ config.sheetName = sanitizeSheetName(config.sheetName);
1685
+ const data = [];
1686
+ let totalColumns = 0;
1687
+ const cols = {};
1688
+ DialogProvider.close();
1689
+ Preloader.open("préparation des données...");
1690
+ const footers = this.getFooters();
1691
+ if(displayOnlyHeader && config.aggregatedValues){
1692
+ const fValues = this.getFooterValues();
1693
+ const headers = ["Fonction d'agrégation"];
1694
+ const aggregatorFunctions = this.aggregatorFunctions;
1695
+ Object.map(footers,(f,i)=>{
1696
+ const col = this.state.columns[i];
1697
+ if(!col || col.visible === false) return;
1698
+ headers.push(defaultStr(col.label,col.text));
1699
+ cols[i] = col;
1700
+ });
1701
+ data.push(headers);
1702
+ Object.map(aggregatorFunctions,(ag,i)=>{
1703
+ const d = [defaultStr(ag.label,ag.text,i)];
1704
+ Object.map(fValues,(footer,field)=>{
1705
+ if(!cols[field]) return;
1706
+ d.push(defaultNumber(footer[i]))
1707
+ });
1708
+ data.push(d);
1709
+ })
1710
+ } else {
1711
+ const headers = [];
1712
+ const hFooters = defaultObj(this.sectionListHeaderFooters);
1713
+ const agFunc = this.state.aggregatorFunction;
1714
+ const canExportOnlyTotal = isOnlytotal || (config.exportOnlyTotal && displayOnlyHeader);
1715
+ if(canExportOnlyTotal){
1716
+ headers.push("");
1717
+ }
1718
+ Object.map(this.state.columns,(col,i)=>{
1719
+ if(!isObj(col) || col.visible === false || this.isSelectableColumn(col,i) || i === this.getIndexColumnName()) return;
1720
+ if(canExportOnlyTotal && !(i in footers)) return;
1721
+ cols[i] = col;
1722
+ headers.push(defaultStr(col.label,col.text));
1723
+ totalColumns++;
1724
+ })
1725
+ data.push(headers);
1726
+ Object.map(this.state.data,(dat,index)=>{
1727
+ ///si l'on a a faire à une colonne de type entete
1728
+ const d = [];
1729
+ if(dat.isSectionListHeader){
1730
+ if(!config.displayTotals && !canExportOnlyTotal) return;
1731
+ const {sectionListHeaderKey:key} = dat;
1732
+ const val = key === this.emptySectionListHeaderValue ? this.getEmptySectionListHeaderValue() : key;
1733
+ d.push(val);
1734
+ if(!canExportOnlyTotal){
1735
+ for(let i = 1;i<totalColumns;i++){
1736
+ d.push(null);
1737
+ }
1738
+ data.push(d);
1739
+ }
1740
+ const hF = hFooters[key];
1741
+ if(isObj(hF) && isNonNullString(agFunc)){
1742
+ const dd = [];
1743
+ Object.map(cols,(col,i)=>{
1744
+ if(i in hF){
1745
+ const ff = hF[i];
1746
+ dd.push(defaultNumber(ff[agFunc]));
1747
+ } else {
1748
+ dd.push(null);
1749
+ }
1750
+ });
1751
+ if(canExportOnlyTotal){
1752
+ dd.unshift(val);
1753
+ data.push(dd);
1754
+ } else {
1755
+ data.push(d);
1756
+ data.push(dd);
1757
+ }
1758
+ } else {
1759
+ data.push(d);
1760
+ }
1761
+
1762
+ } else if(!canExportOnlyTotal) {
1763
+ Object.map(cols,(col,i)=>{
1764
+ const isDateField = defaultStr(col.type).toLowerCase().contains("date");
1765
+ d.push(this.renderRowCell({
1766
+ data : dat,
1767
+ rowData : dat,
1768
+ rowCounterIndex : index,
1769
+ rowIndex : index,
1770
+ formatValue : false,
1771
+ renderRowCell : false,
1772
+ columnField : defaultStr(col.field,i),
1773
+ columnDef :{
1774
+ ...col,
1775
+ ...(isDateField?{format:config.dateFormat}:{})
1776
+ }
1777
+ }));
1778
+ })
1779
+ data.push(d);
1780
+ }
1781
+ });
1782
+ }
1783
+ const wb = XLSX.utils.book_new();
1784
+ const ws = XLSX.utils.aoa_to_sheet(data);
1785
+ XLSX.utils.book_append_sheet(wb, ws, config.sheetName);
1786
+ FileSystem.writeExcel({...config,workbook:wb}).then(({path})=>{
1787
+ if(isNonNullString(path)){
1788
+ notify.success("Fichier enregistré dans le répertoire {0}".sprintf(path))
1789
+ }
1790
+ }).finally(()=>{
1791
+ Preloader.close();
1792
+ })
1793
+ }
1794
+ })
1795
+ }
1796
+ exportToPdf(){
1797
+
1798
+ }
1799
+ renderExportableMenu(){
1800
+ if(this.isDashboard() || !defaultStr(this.state.displayType).toLowerCase().contains("table")) return null;
1801
+ const items = [];
1802
+ if(this.canExportToExcel()){
1803
+ items.push({
1804
+ text : "Exporter au format excel",
1805
+ icon : "file-excel",
1806
+ onPress: this.exportToExcel.bind(this),
1807
+ })
1808
+ }
1809
+ if(false && this.canExportToPDF()){
1810
+ items.push({
1811
+ text : "Exporter au format pdf",
1812
+ icon : "file-pdf-box",
1813
+ onPress : this.exportToPdf.bind(this)
1814
+ })
1815
+ }
1816
+ if(!items.length) return null;
1817
+ return <Menu
1818
+ items = {items}
1819
+ title ="Export des données du tableau"
1820
+ anchor = {(a)=>{
1821
+ return <Icon title = {"Exporter les données du tableau"} {...a} name={"export"}/>
1822
+ }}
1823
+ />
1824
+ }
1626
1825
  renderDisplayTypes(){
1627
1826
  const m = [];
1628
1827
  let activeType = null,hasFoundChart = false,hasFoundTable = false;
@@ -1830,7 +2029,7 @@ export default class CommonDatagridComponent extends AppComponent {
1830
2029
  const chartType = displayTypes[this.state.displayType];
1831
2030
  if(!isObj(chartType) || !isNonNullString(chartType.type)) return null;
1832
2031
  if(typeof chartType.isRendable =='function' && chartType.isRendable(this.getChartIsRendableArgs()) === false) {
1833
- console.warn("impossible d'afficher le graphe de type ",chartType.label," car le type de données requis pour le rendu de ce graphe est invalide")
2032
+ //console.warn("impossible d'afficher le graphe de type ",chartType.label," car le type de données requis pour le rendu de ce graphe est invalide")
1834
2033
  return null;
1835
2034
  }
1836
2035
  const isDonut = chartType.isDonut || chartType.isRadial;
@@ -3172,7 +3371,7 @@ export default class CommonDatagridComponent extends AppComponent {
3172
3371
  différent du td d'un table et ne doit pas être un TableColumn de md
3173
3372
  */
3174
3373
  renderRowCell (arg){
3175
- let {rowData,rowKey,rowIndex,handleSelectableColumn,rowCounterIndex,renderRowCell:customRenderRowCell,isSectionListHeader,columnDef,columnField} = arg;
3374
+ let {rowData,rowKey,rowIndex,handleSelectableColumn,formatValue,rowCounterIndex,renderRowCell:customRenderRowCell,isSectionListHeader,columnDef,columnField} = arg;
3176
3375
  const renderText = isSectionListHeader === true || customRenderRowCell === false ? true : false;
3177
3376
  if(!isObj(rowData)) return renderText ? null : {render:null,extra:{}};
3178
3377
  rowIndex = isDecimal(rowIndex)? rowIndex : isDecimal(index)? index : undefined;
@@ -3196,6 +3395,7 @@ export default class CommonDatagridComponent extends AppComponent {
3196
3395
  }
3197
3396
  return renderRowCell({
3198
3397
  ...arg,
3398
+ formatValue,
3199
3399
  context : this,
3200
3400
  getRowKey : this.getRowKey.bind(this),
3201
3401
  abreviateValues : this.state.abreviateValues,
@@ -12,8 +12,9 @@ import {Flag} from "$ecomponents/Countries";
12
12
  import { StyleSheet } from "react-native";
13
13
 
14
14
  export const renderRowCell = (arg)=>{
15
- let {rowData,getRowKey,context,renderRowCell:customRenderRowCell,abreviateValues,isSectionListHeader,rowIndex,index,rowCounterIndex,columnDef,columnField} = arg;
15
+ let {rowData,getRowKey,context,formatValue:customFormatValue,renderRowCell:customRenderRowCell,abreviateValues,isSectionListHeader,rowIndex,index,rowCounterIndex,columnDef,columnField} = arg;
16
16
  context = context || this;
17
+ const canFormatValue = customFormatValue !== false ? true : false;
17
18
  const renderText = isSectionListHeader === true || customRenderRowCell === false ? true : false;
18
19
  rowIndex = isDecimal(rowIndex)? rowIndex : isDecimal(index)? index : undefined;
19
20
  rowCounterIndex = isDecimal(rowCounterIndex) ? rowCounterIndex : isDecimal(rowIndex)? rowIndex+1 : defaultDecimal(rowCounterIndex);
@@ -120,16 +121,18 @@ export const renderRowCell = (arg)=>{
120
121
  if(isFunction(renderProps)){
121
122
  renderProps = renderProps.call(context,renderArgs);
122
123
  }
123
- _render = formatValue(_render,columnDef.format,abreviateValues);
124
+ if(canFormatValue){
125
+ _render = formatValue(_render,columnDef.format,abreviateValues);
126
+ }
124
127
  if(!renderText && _render && isObj(renderProps)){
125
128
  let Component = defaultVal(renderProps.Component,Label);
126
129
  delete renderProps.Component;
127
130
  _render = <Component {...renderProps}>{_render}</Component>
128
131
  }
129
- if(typeof _render =='boolean'){
130
- _render = _render ? "Oui" : "Non";
131
- }
132
132
  if(renderText){
133
+ if(typeof _render =='number' || typeof _render =='boolean' || typeof _render =="string"){
134
+ return _render;
135
+ }
133
136
  return React.getTextContent(_render);
134
137
  }
135
138
  if((typeof _render ==='string' || typeof _render =='number')){
@@ -181,6 +184,9 @@ export const renderSelectFieldCell= ({rowData,columnDef,columnField})=>{
181
184
 
182
185
  export const formatValue = (value,format,abreviateValues)=>{
183
186
  if(typeof value !='number') return value;
187
+ if(typeof value =='boolean'){
188
+ return value ? "Oui" : "Non";
189
+ }
184
190
  if(format && typeof format =='string' && format.toLowerCase() =='money'){
185
191
  return abreviateValues? value.abreviate2FormatMoney() : value.formatMoney();
186
192
  }
@@ -295,6 +295,7 @@ const DatagridFactory = (Factory)=>{
295
295
  {this.renderSectionListMenu()}
296
296
  {this.renderDisplayTypes()}
297
297
  {this.renderAggregatorFunctionsMenu()}
298
+ {this.renderExportableMenu()}
298
299
  <View pointerEvents={pointerEvents} testID={testID+"_HeaderPagination"} style = {styles.paginationItem}>
299
300
  <BottomSheetMenu
300
301
  testID={testID+"_HeaderMenus"}
@@ -55,7 +55,7 @@ const Provider = React.forwardRef((props,innerRef)=>{
55
55
  },
56
56
  close : (props)=>{
57
57
  if(!state.visible) return;
58
- return setState({...state,...props,visible:false});
58
+ return setState({...state,...defaultObj(props),visible:false});
59
59
  },
60
60
  };
61
61
  React.setRef(ref,context);
@@ -26,7 +26,7 @@ const FormDataDialogProvider = React.forwardRef((props,innerRef)=>{
26
26
  if(formRef.current && formRef.current.formDataContext && formRef.current.formDataContext.getData){
27
27
  sData.data = formRef.current.formDataContext.getData();
28
28
  }
29
- return setState({...state,...sData,onDismiss:undefined,...defaultObj(props),visible:true})
29
+ return setState({...sData,onDismiss:undefined,...defaultObj(props),visible:true})
30
30
  },
31
31
  close : (props)=>{
32
32
  if(!state.visible) return;