@fto-consult/expo-ui 2.21.3 → 2.23.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.
@@ -1,68 +1,90 @@
1
- import Button from "$ecomponents/Button";
2
1
  import Menu from "$ecomponents/BottomSheet/Menu";
3
2
  import View from "$ecomponents/View";
4
3
  import {Pressable,StyleSheet} from "react-native";
5
4
  import Label from "$ecomponents/Label";
6
- import {defaultVal,defaultObj} from "$utils";
5
+ import {defaultVal,defaultObj,isNonNullString,isObj} from "$utils";
7
6
  import React from "$react";
8
7
  import theme from "$theme"
8
+ import appConfig from "$capp/config";
9
9
 
10
- let methods = {
11
- sum : "Somme",
12
- average : "Moyenne",
13
- min : "Minimum",
14
- max : 'Maximum',
15
- count : "Nombre",
16
- }
17
10
  /*** les fonction d'aggreations */
18
11
  export const aggregatorFunctions = {
19
12
  sum : {
20
13
  code : "sum",
21
14
  label : "Somme",
22
- eval : (current,prev,count)=>{
23
- current = typeof current =='number'?current : 0;
24
- prev = typeof prev =='number'? prev : 0;
25
- return current+prev;
15
+ eval : ({value,total,count})=>{
16
+ value = typeof value =='number'?value : 0;
17
+ total = typeof total =='number'? total : 0;
18
+ return value+total;
26
19
  }
27
20
  },
28
- /*average : {
29
- code : "average",
30
- label : "Moyenne",
31
- eval : ()=>{
32
-
33
- }
34
- },*/
35
21
  min : {
36
22
  code : "min",
37
23
  label : "Minimum",
38
- eval : (current,prev,count)=>{
39
- current = typeof current =='number'?current : 0;
40
- prev = typeof prev =='number'? prev : 0;
41
- return Math.min(current,prev);
24
+ eval : ({value,total,count})=>{
25
+ value = typeof value =='number'?value : 0;
26
+ total = typeof total =='number'? total : 0;
27
+ return Math.min(value,total);
42
28
  },
43
29
  },
44
30
  max : {
45
31
  code : "max",
46
32
  label: 'Maximum',
47
- eval : (current,prev,count)=>{
48
- current = typeof current =='number'?current : 0;
49
- prev = typeof prev =='number'? prev : 0;
50
- return Math.max(current,prev);
33
+ eval : ({value,total,count})=>{
34
+ value = typeof value =='number'?value : 0;
35
+ total = typeof total =='number'? total : 0;
36
+ return Math.max(value,total);
51
37
  },
52
38
  },
53
39
  count : {
54
40
  code : "count",
55
41
  label : "Nombre",
56
- eval : (current,prev,count)=>{
42
+ eval : ({value,total,count})=>{
57
43
  return (typeof count =='number'? count : 0)+1;
58
44
  }
59
45
  },
46
+ average : {
47
+ code : "average",
48
+ label : "Moyenne",
49
+ eval : ({count,sum})=>{
50
+ return typeof count =='number' && count > 0 && typeof sum =='number'? sum/count : 0;
51
+ }
52
+ },
53
+ }
54
+ /****
55
+ * Vérifie si la fonction d'aggregation est valide
56
+ */
57
+ export const isValidAggregator = (aggregatorFunctionObject)=>{
58
+ return isObj(aggregatorFunctionObject) && isNonNullString(aggregatorFunctionObject.code) && typeof aggregatorFunctionObject.eval =='function' && true || false;
59
+ }
60
+ /*** permet d'étendre les fonction d'aggregations
61
+ * @param {object|Array} liste des functions d'aggregation supplémentaires, de la forme
62
+ * {
63
+ * code {string} le code de la fonction d'aggrégation
64
+ * label {string} le libele
65
+ * eval {function} la function a utiiser pour évaluer la données via l'aggregator
66
+
67
+ * }
68
+ */
69
+ export function extendAggreagatorFunctions(aFunctions){
70
+ const r = {...aggregatorFunctions};
71
+ loopForAggregator(appConfig.get("datagridAggregatorFunctions"),r);
72
+ loopForAggregator(aFunctions,r);
73
+ return r;
74
+ }
75
+ const loopForAggregator = (aggregatorFunctions,result)=>{
76
+ result = defaultObj(result);
77
+ Object.map(aggregatorFunctions,(aggregatorObj,key)=>{
78
+ if(!isValidAggregator(aggregatorObj)) return null;
79
+ result[aggregatorObj.code] = aggregatorObj;
80
+ });
60
81
  }
61
- const formatValue = ({value,format,method})=>{
62
- return (format === 'money' && method != 'count')? value.formatMoney():value.formatNumber();
82
+ const formatValue = ({value,format,aggregatorFunction})=>{
83
+ return (format === 'money' && aggregatorFunction != 'count')? value.formatMoney():value.formatNumber();
63
84
  }
64
85
  export default function DGGridFooterValue (props){
65
- let {label,text,displayLabel,style,format,testID,anchorProps} = props;
86
+ let {label,text,displayLabel,style,aggregatorFunctions,aggregatorFunction,format,testID,anchorProps} = props;
87
+ aggregatorFunctions = defaultObj(aggregatorFunctions);
66
88
  anchorProps = defaultObj(anchorProps);
67
89
  testID = defaultStr(testID,"RN_DatagridFooterComponent");
68
90
  label = defaultVal(label,text);
@@ -70,27 +92,28 @@ export default function DGGridFooterValue (props){
70
92
  if(displayLabel !== false){
71
93
  if(!label || !React.isValidElement(label,true)) return null;
72
94
  } else label = undefined;
73
- const [active,setActive] = React.useState(isNonNullString(props.method) && props.method in methods ? props.method : "sum")
95
+ const [active,setActive] = React.useState(isNonNullString(aggregatorFunction) && aggregatorFunction in aggregatorFunctions ? aggregatorFunction : aggregatorFunctions[Object.keys(aggregatorFunctions)[0]]?.code)
74
96
  React.useEffect(()=>{
75
- if(isNonNullString(props.method) && props.method in methods){
76
- setActive(props.method)
97
+ if(aggregatorFunction !== active && isNonNullString(aggregatorFunction) && aggregatorFunction in aggregatorFunctions){
98
+ setActive(aggregatorFunction)
77
99
  }
78
- },[props.method])
100
+ },[aggregatorFunction])
79
101
  let title = "";
80
102
  let menuItems = []
81
103
  const activeStyle = {color:theme.colors.primaryOnSurface};
82
- for(let method in methods){
83
- let val = defaultDecimal(props[method]);
104
+ for(let aggregatorFunction in aggregatorFunctions){
105
+ let val = defaultDecimal(props[aggregatorFunction]);
84
106
  if(isDecimal(val)){
85
- let fText = formatValue({value:val,format,method});
86
- title +=(title? ", ":"")+methods[method]+" : "+fText
107
+ let fText = formatValue({value:val,format,aggregatorFunction});
108
+ const mText = defaultStr(aggregatorFunctions[aggregatorFunction].label,aggregatorFunctions[aggregatorFunction].code,aggregatorFunction);
109
+ title +=(title? ", ":"")+mText +" : "+fText
87
110
  menuItems.push({
88
- text : methods[method] + " : "+fText,
89
- icon : active == method ? "check" : null,
90
- style : [{paddingHorizontal:0},active ===method ?activeStyle:null],
111
+ text : mText + " : "+fText,
112
+ icon : active == aggregatorFunction ? "check" : null,
113
+ style : [{paddingHorizontal:0},active ===aggregatorFunction ?activeStyle:null],
91
114
  onPress : (e)=>{
92
115
  React.stopEventPropagation(e);
93
- setActive(method)
116
+ setActive(aggregatorFunction)
94
117
  }
95
118
  })
96
119
  }
@@ -109,7 +132,7 @@ export default function DGGridFooterValue (props){
109
132
  </>
110
133
  : null}
111
134
  <Label testID={testID+"_LabelContent"} primary style={[styles.value]}>
112
- {formatValue({value:defaultDecimal(props[active]),method:active,format})}
135
+ {formatValue({value:defaultDecimal(props[active]),aggregatorFunction:active,format})}
113
136
  </Label>
114
137
  </Pressable>
115
138
  }}
@@ -3,21 +3,28 @@ import View from "$ecomponents/View";
3
3
  import React from "$react";
4
4
  import memoize from "$react/memoize";
5
5
  export {default as FooterItem} from "./Footer";
6
- import {parseDecimal} from "$utils";
6
+ import {parseDecimal,defaultObj,defaultStr,isNonNullString} from "$utils";
7
+ import { aggregatorFunctions as mAggregatorFunctions } from "./Footer";
8
+ export * from "./Footer";
7
9
 
8
- export const evalSingleValue = ({data,columnDef,field,result,withLabel,displayLabel,onlyVisible})=>{
9
- data = data || {}
10
- if(!isNonNullString(field) || !isObj(columnDef)) return result;
10
+ /***évalue la valeur décimale selon les paramètres */
11
+ export const getFooterColumnValue = ({data,columnDef,field,result,columnField}) =>{
12
+ data = defaultObj(data)
13
+ columnDef = defaultObj(columnDef);
14
+ columnField = defaultStr(columnField,columnDef.field,field);
15
+ let val = data[columnField];
16
+ if(typeof columnDef.multiplicater ==='function'){
17
+ val = defaultDecimal(columnDef.multiplicater({value:val,columnField,field,columnDef,rowData:data,item:data}),val)
18
+ }
19
+ return typeof val =='number'? parseDecimal(val.toFixed(12)) : 0;
20
+ }
21
+
22
+ export const evalSingleValue = ({data,columnDef,field,count,columnField,aggregatorFunctions,withLabel,result,displayLabel,onlyVisible})=>{
23
+ if(!isNonNullString(field) || !isObj(columnDef) || !isObj(data)) return result;
24
+ aggregatorFunctions = defaultObj(aggregatorFunctions,mAggregatorFunctions);
11
25
  onlyVisible = defaultBool(onlyVisible,true);
12
26
  if(onlyVisible === true && !(columnDef.visible !== false)) result;
13
- let val = data[field];
14
- if(isFunction(columnDef.multiplicater)){
15
- val = defaultDecimal(columnDef.multiplicater({value:val,columnField:field,field,columnDef,rowData:data,item:data}),val)
16
- }
17
- if(!isDecimal(val)){
18
- return result;
19
- }
20
- val = parseDecimal(val.toFixed(10));
27
+ let val = getFooterColumnValue({data,columnDef,columnField,result,field});
21
28
  (Array.isArray(result) ? result : [result]).map((currentResult)=>{
22
29
  currentResult = defaultObj(currentResult);
23
30
  if(!isObj(currentResult[field])){
@@ -30,21 +37,27 @@ export const evalSingleValue = ({data,columnDef,field,result,withLabel,displayLa
30
37
  }
31
38
  }
32
39
  const obj = currentResult[field];
33
- obj.max = isDecimal(obj.max) ? Math.max(obj.max,val) : val;
34
- obj.min = isDecimal(obj.min) ? Math.min(obj.min,val) : val;
35
- obj.count = isDecimal(obj.count) ? (obj.count = obj.count +1) : 1;
36
- obj.sum = isDecimal(obj.sum) ? (parseDecimal((obj.sum+val).toFixed(10))) : val;
37
- obj.average = obj.sum / obj.count;
40
+ //obj.max = isDecimal(obj.max) ? Math.max(obj.max,val) : val;
41
+ //obj.min = isDecimal(obj.min) ? Math.min(obj.min,val) : val;
42
+ //obj.count = isDecimal(obj.count) ? obj.count : 0;
43
+ //obj.sum = isDecimal(obj.sum) ? (parseDecimal((obj.sum+val).toFixed(10))) : val;
44
+ Object.map(aggregatorFunctions,(aggegatorFunction,key)=>{
45
+ const code = aggegatorFunction.code;
46
+ obj[code] = aggegatorFunction.eval({columnDef,columnField,data,value : val,count,...obj,total:defaultNumber(obj[code])});
47
+ });
48
+ if(typeof obj.count =='number' && obj.count >0 && typeof obj.sum =='number'){
49
+ obj.average = obj.sum / obj.count;
50
+ }
38
51
  return currentResult;
39
52
  })
40
53
  return result;
41
54
  }
42
- export const evalValues = memoize(({data,columns,onlyVisible,withLabel,displayLabel})=>{
55
+ export const evalValues = memoize(({data,columns,aggregatorFunctions,onlyVisible,withLabel,displayLabel})=>{
43
56
  let result = {};
44
57
  Object.map(data,(rowData,i)=>{
45
58
  if(!isObj(rowData)) return result;
46
59
  Object.map(columns,(columnDef,field)=>{
47
- result = evalSingleValue({data:rowData,columnDef,field,result,withLabel,displayLabel,onlyVisible})
60
+ result = evalSingleValue({data:rowData,aggregatorFunctions,columnDef,field,result,withLabel,displayLabel,onlyVisible})
48
61
  })
49
62
  })
50
63
  return result;
@@ -59,7 +72,7 @@ export const evalValues = memoize(({data,columns,onlyVisible,withLabel,displayLa
59
72
  * @param : children {func}, fonction permettant de générer le contenu du footer
60
73
  */
61
74
  export default function DGGridFooters (props){
62
- let {columns,children,displayLabel,Component,data,onlyVisible,...rest} = props;
75
+ let {columns,children,displayLabel,aggregatorFunctions,Component,data,onlyVisible,...rest} = props;
63
76
  rest = defaultObj(rest)
64
77
  if(Component === false){
65
78
  Component = React.Fragment;
@@ -77,7 +90,7 @@ export default function DGGridFooters (props){
77
90
  React.useEffect(()=>{
78
91
  setState({...state,data:props.data})
79
92
  },[props.data]);
80
- let footers = evalValues({data:state.data,columns:state.columns,onlyVisible,displayLabel});
93
+ let footers = evalValues({data:state.data,aggregatorFunctions,columns:state.columns,onlyVisible,displayLabel});
81
94
  return <Component {...rest}>
82
95
  {children({
83
96
  footers,
@@ -18,7 +18,7 @@ const DatagridMainComponent = React.forwardRef((props,ref)=>{
18
18
  const AccordionComponent = isTableDataRef.current ? TableDataAccordion : Accordion;
19
19
  let Component = TableComponent;
20
20
  const canRenderAccordion = (isFunction(props.accordion) || (isObj(props.accordionProps) && isFunction(props.accordionProps.accordion)) || props.accordion === true);
21
- let renderType = defaultStr(getRenderType(),isDesk? "fixed":'accordion').trim().toLowerCase()
21
+ let renderType = defaultStr(getRenderType(),isDesk? "table":'accordion').trim().toLowerCase()
22
22
  if(renderType == 'accordion' && canRenderAccordion){
23
23
  Component = AccordionComponent;
24
24
  } else if(renderType =='table'){
@@ -18,9 +18,9 @@ const DatagridRenderTypeComponent = (props)=>{
18
18
  let type = defaultStr(get(typeKey),isDesk? "fixed":'accordion').toLowerCase().trim();
19
19
  const rTypes = [
20
20
  {...getActiveProps(type,'accordion'),tooltip:"Les éléments de liste s'affichent de manière optimisé pour téléphone mobile",code:'accordion',icon:accordionIcon,label:'Mobile',labelText:'environnement optimisé pour téléphone mobile'},
21
- //{...getActiveProps(type,'table'),tooltip:"Les éléments de listes s'affichent dans un tableau rééel",code:'table',icon:tableIcon,label:'Tableau réel avec pagination'}
21
+ {...getActiveProps(type,'table'),tooltip:"Les éléments de listes s'affichent dans un tableau",code:'table',icon:tableIcon,label:'Tableau'}
22
22
  ]
23
- Object.map(rendersTypes,(t,i)=>{
23
+ /*Object.map(rendersTypes,(t,i)=>{
24
24
  if(isObj(t)){
25
25
  if((isDesk && t.desktop) || (!isDesk && t.mobile)){
26
26
  rTypes.push({
@@ -29,7 +29,7 @@ const DatagridRenderTypeComponent = (props)=>{
29
29
  })
30
30
  }
31
31
  }
32
- });
32
+ });*/
33
33
  let typeObj = {};
34
34
  Object.map(rTypes,(t)=>{
35
35
  if(isObj(t) && t.code == type){
@@ -53,13 +53,14 @@ const DatagridFactory = (Factory)=>{
53
53
  renderFooterCell(props){
54
54
  const {columnField,style} = props;
55
55
  let footersValues = this.getFooterValues();
56
- const footerFields = this.getFooterFields();
56
+ const footerFields = this.getFootersFields();
57
57
  if(isObj(footerFields[columnField])){
58
58
  return <Footer
59
- //{...footerFields[columnField]}
60
59
  {...defaultObj(footersValues[columnField])}
61
60
  displayLabel = {false}
62
61
  style = {[style]}
62
+ aggregatorFunction = {this.getActiveAggregatorFunction().code}
63
+ aggregatorFunctions = {this.aggregatorFunctions}
63
64
  />
64
65
  }
65
66
  return null;
@@ -160,8 +161,9 @@ const DatagridFactory = (Factory)=>{
160
161
  total:pagination.rows,pages:countPages
161
162
  })*/
162
163
  const {visibleColumns} = this.preparedColumns;
163
- const hasFooterFields = this.hasFooterFields();
164
- const {columnsWidths:widths,showFilters,showFooters} = this.state;
164
+ const hasFootersFields = this.hasFootersFields();
165
+ const {columnsWidths:widths} = this.state;
166
+ const showFooters = this.canShowFooters(), showFilters = this.canShowFilters();
165
167
  const isLoading = this.isLoading();
166
168
  let _progressBar = this.getProgressBar();
167
169
  const pointerEvents = this.getPointerEvents();
@@ -202,7 +204,7 @@ const DatagridFactory = (Factory)=>{
202
204
  <ScrollView horizontal showsHorizontalScrollIndicator={!isLoading} style={styles.paginationContainerStyle} contentContainerStyle={styles.minW100}>
203
205
  <View style={[styles.paginationContent]}>
204
206
  <View testID={testID+"_HeaderQueryLimit"}>
205
- {this.renderQueryLimit(this.state.data.length.formatNumber())}
207
+ {this.renderQueryLimit(this.getStateDataSize().formatNumber())}
206
208
  </View>
207
209
  {this.renderCustomPagination()}
208
210
  {!isMobile && <>
@@ -219,13 +221,13 @@ const DatagridFactory = (Factory)=>{
219
221
  {showFilters?'Masquer/Filtres':'Afficher/Filtres'}
220
222
  </Button>
221
223
  )}
222
- {hasFooterFields && !canRenderChart ? <Button
224
+ {hasFootersFields && !canRenderChart ? <Button
223
225
  normal
224
226
  style={styles.paginationItem}
225
227
  onPress = {()=>{this.toggleFooters(!showFooters)} }
226
228
  icon = {showFooters?'view-column':'view-module'}
227
229
  >
228
- {showFooters?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux'}
230
+ {showFooters?'Masquer les totaux':'Afficher les totaux'}
229
231
  </Button>:null}
230
232
  {restItems.map((item,index)=>{
231
233
  return <Button
@@ -277,17 +279,19 @@ const DatagridFactory = (Factory)=>{
277
279
  ,icon : showFilters?'eye-off':'eye'
278
280
  ,text : (showFilters?'Masquer/Filtres':'Afficher/Filtres')
279
281
  } : null,
280
- isMobile && hasFooterFields?{
282
+ isMobile && hasFootersFields?{
281
283
  onPress : ()=>{this.toggleFooters(!showFooters)}
282
284
  ,icon : showFooters?'view-column':'view-module'
283
285
  ,text : (showFooters?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux')
284
286
  } : null,
287
+ ...this.getAggregatorFunctionsMenuItems(),
285
288
  ...(selectableMultiple ? restItems : [])
286
289
  ] : visibleColumns}
287
290
 
288
291
  />
289
292
  {this.renderSectionListMenu()}
290
293
  {this.renderDisplayTypes()}
294
+ {!isMobile && this.renderAggregatorFunctionsMenu() || null}
291
295
  <View testID={testID+"_HeaderPagination"} style = {styles.paginationItem}>
292
296
  <BottomSheetMenu
293
297
  testID={testID+"_HeaderMenus"}
@@ -344,7 +348,7 @@ const DatagridFactory = (Factory)=>{
344
348
  </View> : null}
345
349
  getItemType = {this.getFlashListItemType.bind(this)}
346
350
  renderItem = {this.renderFlashListItem.bind(this)}
347
- hasFooters = {hasFooterFields && !canRenderChart ? true : false}
351
+ hasFooters = {hasFootersFields && !canRenderChart ? true : false}
348
352
  showFilters = {showFilters}
349
353
  showFooters = {showFooters && !canRenderChart ? true : false}
350
354
  showHeaders = { canRenderChart ? !!showFilters : true}
@@ -79,7 +79,8 @@ export const selectDateFormatFieldProps = ({onAdd:customOnAdd,onAddCustomFormat,
79
79
  getItemValue : ({item})=>item.code,
80
80
  renderItem : dateFormatSelectorRenderItem,
81
81
  showAdd : true,
82
- ...defaultObj(props),
82
+ ...props,
83
+ defaultValue : defaultStr(props.defaultValue,props.format),
83
84
  onAdd,
84
85
  onAdd : undefined,
85
86
  showAdd : false,
@@ -889,7 +889,7 @@ export default class Field extends AppComponent {
889
889
 
890
890
  this.___formattedField = undefined;
891
891
  let _type = this.type;
892
- format = defaultStr(format).toLowerCase().trim();
892
+ format = defaultStr(format);
893
893
  tooltip = defaultVal(tooltip,title);
894
894
 
895
895
  const isEditable = rest.disabled !== true && rest.readOnly !== true && rest.editable !== false ? true : false;
@@ -218,7 +218,9 @@ const styles = StyleSheet.create({
218
218
  flexDirection: 'row',
219
219
  flexWrap : "wrap",
220
220
  justifyContent: 'space-between',
221
- margin: 8,
221
+ margin: 0,
222
+ marginHorizontal : 10,
223
+ paddingRight : 10,
222
224
  borderRadius: 4,
223
225
  minHeight: 48,
224
226
  },