@fto-consult/expo-ui 2.36.4 → 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.4",
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>
@@ -9,11 +9,9 @@ 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 Image from "$ecomponents/Image";
13
12
  import Icon,{COPY_ICON} from "$ecomponents/Icon";
14
13
  import filterUtils from "$cfilters";
15
- import Hashtag from "$ecomponents/Hashtag";
16
- 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";
17
15
  import {Datagrid as DatagridContentLoader} from "$ecomponents/ContentLoader";
18
16
  import React from "$react";
19
17
  import DateLib from "$lib/date";
@@ -25,15 +23,11 @@ import Preloader from "$ecomponents/Preloader";
25
23
  import Checkbox from "../Checkbox";
26
24
  import { TouchableRipple } from "react-native-paper";
27
25
  import { evalSingleValue,Footer,getFooterColumnValue,isValidAggregator,extendAggreagatorFunctions} from "../Footer";
28
- import i18n from "$i18n";
29
26
  import { makePhoneCall,canMakePhoneCall as canMakeCall} from "$makePhoneCall";
30
27
  import copyToClipboard from "$capp/clipboard";
31
28
  import { Pressable } from "react-native";
32
- import TableLink from "$TableLink";
33
- import appConfig from "$capp/config";
34
29
  import stableHash from "stable-hash";
35
30
  import DatagridProgressBar from "./ProgressBar";
36
- import {Flag} from "$ecomponents/Countries"
37
31
  import View from "$ecomponents/View";
38
32
  import {Menu} from "$ecomponents/BottomSheet";
39
33
  import {styles as tableStyles} from "$ecomponents/Table";
@@ -42,6 +36,8 @@ import Chart,{getMaxSupportedSeriesSize} from "$ecomponents/Chart";
42
36
  import notify from "$cnotify";
43
37
  import FileSystem from "$file-system";
44
38
  import sprintf from "$cutils/sprintf";
39
+ import { renderRowCell,formatValue } from "./utils";
40
+ import * as XLSX from "xlsx";
45
41
 
46
42
  export const TIMEOUT = 100;
47
43
 
@@ -1417,11 +1413,7 @@ export default class CommonDatagridComponent extends AppComponent {
1417
1413
  },TIMEOUT);
1418
1414
  }
1419
1415
  formatValue(value,format){
1420
- if(typeof value !='number') return value;
1421
- if(format && typeof format =='string' && format.toLowerCase() =='money'){
1422
- return this.state.abreviateValues? value.abreviate2FormatMoney() : value.formatMoney();
1423
- }
1424
- return this.state.abreviateValues ? value.abreviate() : value.formatNumber();
1416
+ return formatValue(value,format,this.state.abreviateValues);
1425
1417
  }
1426
1418
  renderAggregatorFunctionsMenu(){
1427
1419
  const m = this.getAggregatorFunctionsMenuItems(false,false);
@@ -1632,6 +1624,204 @@ export default class CommonDatagridComponent extends AppComponent {
1632
1624
  }
1633
1625
  ]
1634
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
+ }
1635
1825
  renderDisplayTypes(){
1636
1826
  const m = [];
1637
1827
  let activeType = null,hasFoundChart = false,hasFoundTable = false;
@@ -1839,7 +2029,7 @@ export default class CommonDatagridComponent extends AppComponent {
1839
2029
  const chartType = displayTypes[this.state.displayType];
1840
2030
  if(!isObj(chartType) || !isNonNullString(chartType.type)) return null;
1841
2031
  if(typeof chartType.isRendable =='function' && chartType.isRendable(this.getChartIsRendableArgs()) === false) {
1842
- 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")
1843
2033
  return null;
1844
2034
  }
1845
2035
  const isDonut = chartType.isDonut || chartType.isRadial;
@@ -3165,44 +3355,8 @@ export default class CommonDatagridComponent extends AppComponent {
3165
3355
  key = {props.rowKey}
3166
3356
  />
3167
3357
  }
3168
- renderSelectFieldCell({rowData,columnDef,columnField}){
3169
- let v1 = rowData[columnField],_render = v1;
3170
- if(isObjOrArray(columnDef.items)){
3171
- if(columnDef.multiple){
3172
- v1 = Object.toArray(v1);
3173
- _render = "";
3174
- v1.map((v)=>{
3175
- for(let i in columnDef.items){
3176
- let it = columnDef.items[i];
3177
- if(isObj(it) && defaultVal(it.code,i) == v){
3178
- _render+=(_render?arrayValueSeparator:"")+defaultStr(it.label,it.text,v);
3179
- } else if(isNonNullString(it) && i == v){
3180
- _render+=(_render?arrayValueSeparator:"")+it;
3181
- }
3182
- }
3183
- })
3184
- if(!_render){
3185
- return v1.join(arrayValueSeparator);
3186
- }
3187
- return _render;
3188
- } else {
3189
- for(let i in columnDef.items){
3190
- let it = columnDef.items[i];
3191
- if(isObj(it) && defaultVal(it.code,i) == v1){
3192
- return defaultStr(it.label,it.text,v1);
3193
- } else if(isNonNullString(it) && i == v1){
3194
- return it;
3195
- }
3196
- }
3197
- if(_render === undefined || _render === null) return v1;
3198
- if(isArray(_render)){
3199
- return _render.join(arrayValueSeparator)
3200
- } else if(isObj(_render)){
3201
- return "";
3202
- }
3203
- }
3204
- }
3205
- return _render
3358
+ renderSelectFieldCell(args){
3359
+ return this.renderSelectFieldCell(args);
3206
3360
  }
3207
3361
  /*** retourne le rendu d'une cellule de la ligne du tableau
3208
3362
  @parm, rowData, object, la ligne à afficher le rendu du contenu
@@ -3217,157 +3371,35 @@ export default class CommonDatagridComponent extends AppComponent {
3217
3371
  différent du td d'un table et ne doit pas être un TableColumn de md
3218
3372
  */
3219
3373
  renderRowCell (arg){
3220
- let {rowData,renderRowCell:customRenderRowCell,isSectionListHeader,rowKey,handleSelectableColumn,rowIndex,index,rowCounterIndex,columnDef,columnField} = arg;
3374
+ let {rowData,rowKey,rowIndex,handleSelectableColumn,formatValue,rowCounterIndex,renderRowCell:customRenderRowCell,isSectionListHeader,columnDef,columnField} = arg;
3221
3375
  const renderText = isSectionListHeader === true || customRenderRowCell === false ? true : false;
3222
- rowIndex = isDecimal(rowIndex)? rowIndex : isDecimal(index)? index : undefined;
3223
- rowCounterIndex = isDecimal(rowCounterIndex) ? rowCounterIndex : isDecimal(rowIndex)? rowIndex+1 : defaultDecimal(rowCounterIndex);
3224
- const abreviate = this.state.abreviateValues;
3225
3376
  if(!isObj(rowData)) return renderText ? null : {render:null,extra:{}};
3226
- let _render = null;
3227
- columnDef = defaultObj(columnDef);
3228
- let _type = defaultStr(columnDef.jsType,columnDef.type).trim().toLowerCase();
3229
- if(this.isSelectableColumn(columnDef,columnField)){
3230
- if(renderText) return null;
3231
- rowKey = rowKey ? rowKey : this.getRowKey(rowData,rowIndex);
3232
- return {render :handleSelectableColumn === false ? null : this.renderSelectableCheckboxCell({
3233
- ...arg,
3234
- rowKey,
3235
- rowData,
3236
- checked : this.isRowSelected(rowKey,rowIndex),
3237
- rowsRefs : this.selectedRowsRefs,
3238
- onChange : ({checked})=>{
3239
- this.handleRowToggle({row:rowData,rowData,rowIndex,rowKey,selected:checked})
3240
- }
3241
- }),style:{},extra:{style:{}}};
3242
- } else if((columnField == this.getIndexColumnName())){
3377
+ rowIndex = isDecimal(rowIndex)? rowIndex : isDecimal(index)? index : undefined;
3378
+ rowCounterIndex = isDecimal(rowCounterIndex) ? rowCounterIndex : isDecimal(rowIndex)? rowIndex+1 : defaultDecimal(rowCounterIndex);
3379
+ if(this.isSelectableColumn(columnDef,columnField)){
3243
3380
  if(renderText) return null;
3381
+ rowKey = rowKey ? rowKey : this.getRowKey(rowData,rowIndex);
3382
+ return {render :handleSelectableColumn === false ? null : this.renderSelectableCheckboxCell({
3383
+ ...arg,
3384
+ rowKey,
3385
+ rowData,
3386
+ checked : this.isRowSelected(rowKey,rowIndex),
3387
+ rowsRefs : this.selectedRowsRefs,
3388
+ onChange : ({checked})=>{
3389
+ this.handleRowToggle({row:rowData,rowData,rowIndex,rowKey,selected:checked})
3390
+ }
3391
+ }),style:{},extra:{style:{}}};
3392
+ } else if((columnField == this.getIndexColumnName())){
3393
+ if(renderText) return null;
3244
3394
  return {render : rowCounterIndex.formatNumber(),style:{},extra:{}};
3245
- }
3246
- let renderProps = undefined;
3247
- if(isObj(columnDef.datagrid)){
3248
- renderProps = columnDef.datagrid.renderProps;
3249
- }
3250
- const style = Object.assign({},StyleSheet.flatten(columnDef.style));
3251
- if(!renderText && columnDef.visible === false){
3252
- style.display = "none";
3253
- }
3254
- let extra = {style},renderArgs = arg;
3255
-
3256
- renderArgs.extra = extra;
3257
- renderArgs.item = rowData;
3258
- const defaultValue = renderArgs.defaultValue = renderArgs.value = rowData[columnField];
3259
- let key = this.getRowKey(rowData,rowIndex)+"-"+columnField,isTagRender = defaultStr(columnDef.table).toLowerCase().contains("tags");
3260
- if(isObj(columnDef.datagrid) && isFunction(columnDef.datagrid.render)){
3261
- _render = columnDef.datagrid.render.call(this,renderArgs);
3262
- } else if(isFunction(columnDef.multiplicater)){
3263
- _render = defaultDecimal(columnDef.multiplicater({...renderArgs,value:rowData[columnField]}),rowData[columnField]);
3264
- } else {
3265
- _render = defaultValue;
3266
- if(!renderText && defaultStr(columnDef.format).toLowerCase() === 'hashtag'){
3267
- _render = <Hashtag>{_render}</Hashtag>
3268
- } else if(typeof columnDef.render === "function"){
3269
- _render = columnDef.render.call(this,renderArgs);
3270
- } else if(arrayValueExists( _type,["date","datetime","time"])){
3271
- let _dd =DateLib.parse(rowData[columnField],_type === 'time'?DateLib.isoTimeFormat:DateLib.SQLDateFormat);
3272
- if(DateLib.isDateObj(_dd)){
3273
- _render = DateLib.format(_dd,defaultStr(columnDef.format,(_type === 'time'?DateLib.defaultTimeFormat:DateLib.masks.defaultDate)));
3274
- }
3275
- if(!_render) _render = rowData[columnField]
3276
- } else if(arrayValueExists(_type,['switch','checkbox'])){
3277
- let {checkedLabel,checkedTooltip,uncheckedTooltip,checkedValue,uncheckedLabel,uncheckedValue} = columnDef;
3278
- checkedLabel = defaultStr(checkedLabel,'Oui')
3279
- uncheckedLabel = defaultStr(uncheckedLabel,'Non')
3280
- checkedValue = defaultVal(checkedValue,1); uncheckedValue = defaultVal(uncheckedValue,0)
3281
- let val = defaultVal(rowData[columnField],columnDef.defaultValue,columnDef.value)
3282
- if(val === checkedValue){
3283
- _render = checkedLabel;
3284
- } else _render = uncheckedLabel;
3285
- }
3286
- else if(!renderText && (_type =='select_country' || _type =='selectcountry')){
3287
- _render = <Flag withCode {...columnDef} length={undefined} width={undefined} height={undefined} code={defaultValue}/>
3288
- }
3289
- ///le lien vers le table data se fait via la colonne ayant la propriété foreignKeyTable de type chaine de caractère non nulle
3290
- else if(!renderText && (isNonNullString(columnDef.foreignKeyTable) || columnDef.primaryKey === true || arrayValueExists(['id','piece'],_type))){
3291
- const id = rowData[columnField]?.toString();
3292
- if(isNonNullString(id)){
3293
- _render = <TableLink
3294
- id = {id}
3295
- foreignKeyTable = {defaultStr(columnDef.foreignKeyTable,columnDef.table,columnDef.tableName)}
3296
- foreignKeyColumn = {defaultStr(columnDef.foreignKeyColumn,columnDef.field)}
3297
- {...columnDef}
3298
- data = {rowData}
3299
- columnField = {columnField}
3300
- >
3301
- {this.renderSelectFieldCell({columnDef,columnField,rowData})}
3302
- </TableLink>
3303
- }
3304
- } else if((_type.contains('select'))){
3305
- _render= this.renderSelectFieldCell({columnDef,columnField,rowData,data:rowData})
3306
- } else if(_type == 'image'){
3307
- if(renderText) return null;
3308
- columnDef = defaultObj(columnDef)
3309
- columnDef = {...columnDef,...defaultObj(columnDef.datagrid)};
3310
- columnDef.size = defaultDecimal(columnDef.size,50);
3311
- columnDef.editable = defaultBool(columnDef.editable,false)
3312
- columnDef.rounded = defaultBool(columnDef.rounded,columnDef.round,true);
3313
- columnDef.src = rowData[columnField];
3314
- _render = <Image {...columnDef}/>
3315
- }
3316
- if(_render === undefined || _render ===null){
3317
- _render = rowData[columnField];
3318
- }
3319
- if(columnDef.type =="password" && isNonNullString(_render)){
3320
- let l = Math.max(_render.length,20);
3321
- _render = "";
3322
- for(let i=0;i<l;i++){
3323
- _render+=".";
3324
- }
3325
- }
3326
- if(isArray(_render)){
3327
- if(isTagRender){
3328
- let nV = "";
3329
- _render.map((v1)=>{
3330
- nV +=(nV?arrayValueSeparator:"")+i18n.lang(v1,v1);
3331
- })
3332
- _render = nV;
3333
- } else {
3334
- _render = _render.join(arrayValueSeparator);
3335
- }
3336
- }
3337
- }
3338
- if(_render ===undefined){
3339
- _render = rowData[columnField];
3340
- }
3341
- if(isArray(_render)){
3342
- _render = _render.join(arrayValueSeparator);
3343
- } else if(!React.isValidElement(_render) && isPlainObj(_render)){
3344
- let __r = "";
3345
- for(let i in _render){
3346
- __r+= (isObj(_render[i]) && _render[i]? (_render[i].code? _render[i].code:defaultStr(_render[i].label)):(_render[i]))
3347
- }
3348
- _render = __r;
3349
- }
3350
- if(isFunction(renderProps)){
3351
- renderProps = renderProps.call(this,renderArgs);
3352
- }
3353
- _render = this.formatValue(_render,columnDef.format);
3354
- if(!renderText && _render && isObj(renderProps)){
3355
- let Component = defaultVal(renderProps.Component,Label);
3356
- delete renderProps.Component;
3357
- _render = <Component {...renderProps}>{_render}</Component>
3358
- }
3359
- if(typeof _render =='boolean'){
3360
- _render = _render ? "Oui" : "Non";
3361
- }
3362
- if(renderText){
3363
- return React.getTextContent(_render);
3364
- }
3365
- if((typeof _render ==='string' || typeof _render =='number')){
3366
- _render = <Label selectable>{_render}</Label>
3367
- }
3368
-
3369
- _render = React.isValidElement(_render)|| Array.isArray(_render)?_render:null;
3370
- return {render:_render,style,extra,key};
3395
+ }
3396
+ return renderRowCell({
3397
+ ...arg,
3398
+ formatValue,
3399
+ context : this,
3400
+ getRowKey : this.getRowKey.bind(this),
3401
+ abreviateValues : this.state.abreviateValues,
3402
+ })
3371
3403
  }
3372
3404
  static LinesProgressBar (props){
3373
3405
  return <DatagridContentLoader {...props}/>
@@ -4,4 +4,5 @@ export {default as TableData} from "./TableData";
4
4
 
5
5
  export {default as factory} from "./factory";
6
6
 
7
- export {default as session} from "./session";
7
+ export {default as session} from "./session";
8
+ export * from "./utils";
@@ -0,0 +1,194 @@
1
+ // Copyright 2023 @fto-consult/Boris Fouomene. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style
3
+ // license that can be found in the LICENSE file.
4
+ import {isNonNullString,isArray,isObjOrArray,defaultStr,defaultArray,defaultObj,isObj} from "$utils";
5
+ import Hashtag from "$ecomponents/Hashtag";
6
+ import DateLib from "$date";
7
+ import Image from "$ecomponents/Image";
8
+ import React from "$react";
9
+ import Label from "$ecomponents/Label";
10
+ import TableLink from "$TableLink";
11
+ import {Flag} from "$ecomponents/Countries";
12
+ import { StyleSheet } from "react-native";
13
+
14
+ export const renderRowCell = (arg)=>{
15
+ let {rowData,getRowKey,context,formatValue:customFormatValue,renderRowCell:customRenderRowCell,abreviateValues,isSectionListHeader,rowIndex,index,rowCounterIndex,columnDef,columnField} = arg;
16
+ context = context || this;
17
+ const canFormatValue = customFormatValue !== false ? true : false;
18
+ const renderText = isSectionListHeader === true || customRenderRowCell === false ? true : false;
19
+ rowIndex = isDecimal(rowIndex)? rowIndex : isDecimal(index)? index : undefined;
20
+ rowCounterIndex = isDecimal(rowCounterIndex) ? rowCounterIndex : isDecimal(rowIndex)? rowIndex+1 : defaultDecimal(rowCounterIndex);
21
+ if(!isObj(rowData)) return renderText ? null : {render:null,extra:{}};
22
+ let _render = null;
23
+ getRowKey = typeof getRowKey =='function'? getRowKey : React.getKey;
24
+ columnDef = defaultObj(columnDef);
25
+ let _type = defaultStr(columnDef.jsType,columnDef.type).trim().toLowerCase().replaceAll("_","");
26
+ let renderProps = undefined;
27
+ if(isObj(columnDef.datagrid)){
28
+ renderProps = columnDef.datagrid.renderProps;
29
+ }
30
+ const style = Object.assign({},StyleSheet.flatten(columnDef.style));
31
+ if(!renderText && columnDef.visible === false){
32
+ style.display = "none";
33
+ }
34
+ const extra = {style},renderArgs = arg;
35
+ renderArgs.extra = extra;
36
+ renderArgs.item = rowData;
37
+ const defaultValue = renderArgs.defaultValue = renderArgs.value = rowData[columnField];
38
+ let key = getRowKey.call(context,rowData,rowIndex)+"-"+columnField;
39
+ if(isObj(columnDef.datagrid) && isFunction(columnDef.datagrid.render)){
40
+ _render = columnDef.datagrid.render.call(context,renderArgs);
41
+ } else if(isFunction(columnDef.multiplicater)){
42
+ _render = defaultDecimal(columnDef.multiplicater({...renderArgs,value:rowData[columnField]}),rowData[columnField]);
43
+ } else {
44
+ _render = defaultValue;
45
+ if(!renderText && defaultStr(columnDef.format).toLowerCase() === 'hashtag'){
46
+ _render = <Hashtag>{_render}</Hashtag>
47
+ } else if(typeof columnDef.render === "function"){
48
+ _render = columnDef.render.call(context,renderArgs);
49
+ } else if(arrayValueExists( _type,["date","datetime","time"])){
50
+ let _dd =DateLib.parse(rowData[columnField],_type === 'time'?DateLib.isoTimeFormat:DateLib.SQLDateFormat);
51
+ if(DateLib.isDateObj(_dd)){
52
+ _render = DateLib.format(_dd,defaultStr(columnDef.format,(_type === 'time'?DateLib.defaultTimeFormat:DateLib.masks.defaultDate)));
53
+ }
54
+ if(!_render) _render = rowData[columnField]
55
+ } else if(arrayValueExists(_type,['switch','checkbox'])){
56
+ let {checkedLabel,checkedValue,uncheckedLabel,uncheckedValue} = columnDef;
57
+ checkedLabel = defaultStr(checkedLabel,'Oui')
58
+ uncheckedLabel = defaultStr(uncheckedLabel,'Non')
59
+ checkedValue = defaultVal(checkedValue,1); uncheckedValue = defaultVal(uncheckedValue,0)
60
+ let val = defaultVal(rowData[columnField],columnDef.defaultValue,columnDef.value)
61
+ if(val === checkedValue){
62
+ _render = checkedLabel;
63
+ } else _render = uncheckedLabel;
64
+ }
65
+ else if(!renderText && (_type =='selectcountry')){
66
+ _render = <Flag withCode {...columnDef} length={undefined} width={undefined} height={undefined} code={defaultValue}/>
67
+ }
68
+ ///le lien vers le table data se fait via la colonne ayant la propriété foreignKeyTable de type chaine de caractère non nulle
69
+ else if(!renderText && (isNonNullString(columnDef.foreignKeyTable) || columnDef.primaryKey === true || arrayValueExists(['id','piece'],_type))){
70
+ const id = rowData[columnField]?.toString();
71
+ if(isNonNullString(id)){
72
+ _render = <TableLink
73
+ id = {id}
74
+ foreignKeyTable = {defaultStr(columnDef.foreignKeyTable,columnDef.table,columnDef.tableName)}
75
+ foreignKeyColumn = {defaultStr(columnDef.foreignKeyColumn,columnDef.field)}
76
+ {...columnDef}
77
+ data = {rowData}
78
+ columnField = {columnField}
79
+ >
80
+ {renderSelectFieldCell({columnDef,columnField,rowData})}
81
+ </TableLink>
82
+ }
83
+ } else if((_type.contains('select'))){
84
+ _render= renderSelectFieldCell({columnDef,columnField,rowData,data:rowData})
85
+ } else if(_type == 'image'){
86
+ if(renderText) return null;
87
+ columnDef = defaultObj(columnDef)
88
+ columnDef = {...columnDef,...defaultObj(columnDef.datagrid)};
89
+ columnDef.size = defaultDecimal(columnDef.size,50);
90
+ columnDef.editable = defaultBool(columnDef.editable,false)
91
+ columnDef.rounded = defaultBool(columnDef.rounded,columnDef.round,true);
92
+ columnDef.src = rowData[columnField];
93
+ _render = <Image {...columnDef}/>
94
+ }
95
+ if(_render === undefined || _render ===null){
96
+ _render = rowData[columnField];
97
+ }
98
+ if(columnDef.type =="password" && isNonNullString(_render)){
99
+ let l = Math.max(_render.length,20);
100
+ _render = "";
101
+ for(let i=0;i<l;i++){
102
+ _render+=".";
103
+ }
104
+ }
105
+ if(isArray(_render)){
106
+ _render = _render.join(arrayValueSeparator);
107
+ }
108
+ }
109
+ if(_render ===undefined){
110
+ _render = rowData[columnField];
111
+ }
112
+ if(isArray(_render)){
113
+ _render = _render.join(arrayValueSeparator);
114
+ } else if(!React.isValidElement(_render) && isPlainObj(_render)){
115
+ let __r = "";
116
+ for(let i in _render){
117
+ __r+= (isObj(_render[i]) && _render[i]? (_render[i].code? _render[i].code:defaultStr(_render[i].label)):(_render[i]))
118
+ }
119
+ _render = __r;
120
+ }
121
+ if(isFunction(renderProps)){
122
+ renderProps = renderProps.call(context,renderArgs);
123
+ }
124
+ if(canFormatValue){
125
+ _render = formatValue(_render,columnDef.format,abreviateValues);
126
+ }
127
+ if(!renderText && _render && isObj(renderProps)){
128
+ let Component = defaultVal(renderProps.Component,Label);
129
+ delete renderProps.Component;
130
+ _render = <Component {...renderProps}>{_render}</Component>
131
+ }
132
+ if(renderText){
133
+ if(typeof _render =='number' || typeof _render =='boolean' || typeof _render =="string"){
134
+ return _render;
135
+ }
136
+ return React.getTextContent(_render);
137
+ }
138
+ if((typeof _render ==='string' || typeof _render =='number')){
139
+ _render = <Label selectable>{_render}</Label>
140
+ }
141
+ _render = React.isValidElement(_render)|| Array.isArray(_render)?_render:null;
142
+ return {render:_render,style,extra,key};
143
+ }
144
+
145
+ export const renderSelectFieldCell= ({rowData,columnDef,columnField})=>{
146
+ let v1 = rowData[columnField],_render = v1;
147
+ if(isObjOrArray(columnDef.items)){
148
+ if(columnDef.multiple){
149
+ v1 = Object.toArray(v1);
150
+ _render = "";
151
+ v1.map((v)=>{
152
+ for(let i in columnDef.items){
153
+ let it = columnDef.items[i];
154
+ if(isObj(it) && defaultVal(it.code,i) == v){
155
+ _render+=(_render?arrayValueSeparator:"")+defaultStr(it.label,it.text,v);
156
+ } else if(isNonNullString(it) && i == v){
157
+ _render+=(_render?arrayValueSeparator:"")+it;
158
+ }
159
+ }
160
+ })
161
+ if(!_render){
162
+ return v1.join(arrayValueSeparator);
163
+ }
164
+ return _render;
165
+ } else {
166
+ for(let i in columnDef.items){
167
+ let it = columnDef.items[i];
168
+ if(isObj(it) && defaultVal(it.code,i) == v1){
169
+ return defaultStr(it.label,it.text,v1);
170
+ } else if(isNonNullString(it) && i == v1){
171
+ return it;
172
+ }
173
+ }
174
+ if(_render === undefined || _render === null) return v1;
175
+ if(isArray(_render)){
176
+ return _render.join(arrayValueSeparator)
177
+ } else if(isObj(_render)){
178
+ return "";
179
+ }
180
+ }
181
+ }
182
+ return _render
183
+ }
184
+
185
+ export const formatValue = (value,format,abreviateValues)=>{
186
+ if(typeof value !='number') return value;
187
+ if(typeof value =='boolean'){
188
+ return value ? "Oui" : "Non";
189
+ }
190
+ if(format && typeof format =='string' && format.toLowerCase() =='money'){
191
+ return abreviateValues? value.abreviate2FormatMoney() : value.formatMoney();
192
+ }
193
+ return abreviateValues ? value.abreviate() : value.formatNumber();
194
+ }
@@ -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);
@@ -996,7 +996,7 @@ class DropdownComponent extends AppComponent {
996
996
  contentStyle = {[{paddingVertical:0},restProps.contentStyle]}
997
997
  anchor={anchor}
998
998
  {...dialogProps}
999
- title = {defaultStr(dialogProps.title,label,text)+"[ "+self.state.data.length.formatNumber()+" ]"}
999
+ title = {defaultStr(dialogProps.title,label,text)+" [ "+self.state.data.length.formatNumber()+" ]"}
1000
1000
  subtitle = {selectedText}
1001
1001
  style = {[restProps.style]}
1002
1002
  contentProps = {{style:{flex:1}}}
@@ -513,6 +513,10 @@ export default class Filter extends AppComponent {
513
513
  isLoading = !!isLoading;
514
514
  rest.pointerEvents = !isLoading ? "auto" : "none";
515
515
  if(withLabel ===false){
516
+ if(type.contains("select")){
517
+ rest.dialogProps = defaultObj(rest.dialogProps);
518
+ rest.dialogProps.title = defaultStr(rest.dialogProps.title,label);
519
+ }
516
520
  delete rest.label;
517
521
  delete rest.text;
518
522
  delete rest.title;
@@ -2,7 +2,6 @@ import Field from "./Field";
2
2
  import Dropdown from "$ecomponents/Dropdown";
3
3
  import PropTypes from "prop-types";
4
4
  import {isObj} from "$utils";
5
- import appConfig from "$capp/config";
6
5
 
7
6
  export default class FormSelectField extends Field{
8
7
  constructor(props) {
@@ -110,16 +109,11 @@ export default class FormSelectField extends Field{
110
109
  _render (componentProps){
111
110
  let {
112
111
  getInstance,
113
- _defaultValue,
114
112
  ...props
115
113
  } = componentProps;
116
114
  delete props.format;
117
115
  delete props.import;
118
116
  delete props.exports;
119
- const isCountry = this.type.contains('country');
120
- if( isCountry && props.multiple !== true){
121
- props.defaultValue = defaultStr(props.defaultValue,appConfig.countryCode,appConfig.get("countryCode","country"));
122
- }
123
117
  return <Dropdown
124
118
  {...props}
125
119
  ref = {(el)=>{
@@ -195,7 +195,13 @@ const TableDataSelectField = React.forwardRef((_props,ref)=>{
195
195
  return (itemLabel !== itemCode ? ((isNonNullString(itemCode)?("["+itemCode+"] "):"")+itemLabel):itemLabel);
196
196
  }
197
197
  const dialogProps = defaultObj(props.dialogProps);
198
- dialogProps.title = defaultStr(dialogProps.title,fKeyTable.text,fKeyTable.label)
198
+ let ttitle = defaultStr(dialogProps.title);
199
+ if(!ttitle){
200
+ const txt = defaultStr(props.label,props.text);
201
+ const tt = defaultStr(fKeyTable.text,fKeyTable.label);
202
+ ttitle = txt && tt ? "{0} [1]".sprintf(txt,tt) : tt || txt;
203
+ }
204
+ dialogProps.title = ttitle;
199
205
  return <Dropdown
200
206
  {...props}
201
207
  isFilter = {isFilter}
@@ -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;
@@ -71,20 +71,24 @@ export const getFilterComponentProps = (_props)=>{
71
71
  type = defaultStr(jsType,type,'text').toLowerCase().replaceAll("_","").replaceAll("-","").trim();
72
72
  const sanitizedType = type.replaceAll("_","").toLowerCase().trim();
73
73
  props = defaultObj(props);
74
+ props.label = defaultStr(label,text);
74
75
  if(type.startsWith("select")){
75
76
  props.inputProps = Object.assign({},props.inputProps);
76
77
  props.inputProps.placeholder = defaultStr(props.inputProps.placeholder,i18n.lang("search.."))
77
- props.label = label;
78
78
  component = Fields.SelectField;
79
79
  if(type =='select_country' || type =='selectcountry'){
80
80
  component = Fields.SelectCountry;
81
81
  } else if(type =='select_tabledata' || type =='selecttabledata'){
82
82
  component = Fields.SelectTableData;
83
+ } else if(React.isComponent(componentTypes[type])){
84
+ component = componentTypes[type];
85
+ } else if(React.isComponent(componentTypes[sanitizedType])){
86
+ component = componentTypes[sanitizedType];
83
87
  }
84
88
  type = "select";
85
89
  } else if(type == 'switch' || type =='radio' || type ==='checkbox') {
86
90
  type = 'select';
87
- let {checkedLabel,checkedTooltip,uncheckedTooltip,checkedValue,uncheckedLabel,uncheckedValue,label,text,...pR} = props;
91
+ let {checkedLabel,checkedTooltip,uncheckedTooltip,checkedValue,uncheckedLabel,uncheckedValue,...pR} = props;
88
92
  checkedLabel = defaultVal(checkedLabel,'Non')
89
93
  uncheckedLabel = defaultVal(uncheckedLabel,'Oui')
90
94
  checkedValue = defaultVal(checkedValue,1); uncheckedValue = defaultVal(uncheckedValue,0)
@@ -101,7 +105,6 @@ export const getFilterComponentProps = (_props)=>{
101
105
  component = componentTypes[type];
102
106
  } else if(isNonNullString(props.foreignKeyColumn) && isNonNullString(props.foreignKeyTable)) {
103
107
  component = Fields.SelectTableData;
104
- props.multiple = true;
105
108
  type = "select";
106
109
  }else {
107
110
  if(React.isComponent(componentTypes[sanitizedType])){
@@ -109,8 +112,7 @@ export const getFilterComponentProps = (_props)=>{
109
112
  }
110
113
  delete props.dbName;
111
114
  delete props.tableName;
112
- props.label = label;
113
- delete props.fieldName;
115
+ delete props.fieldName;
114
116
  }
115
117
  type = type || "text"
116
118
  if(type =='select'){