@fto-consult/expo-ui 2.20.1 → 2.21.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.20.1",
3
+ "version": "2.21.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.23.4",
64
+ "@fto-consult/common": "^1.23.9",
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",
@@ -38,6 +38,40 @@ import View from "$ecomponents/View";
38
38
  import {Menu} from "$ecomponents/BottomSheet";
39
39
  import {styles as tableStyles} from "$ecomponents/Table";
40
40
  import {DialogProvider} from "$ecomponents/Form/FormData";
41
+ import Chart from "$ecomponents/Chart";
42
+
43
+ export const displayTypes = {
44
+ table : {
45
+ code : "table",
46
+ label : 'Tableau',
47
+ icon : "table",
48
+ type : 'table',
49
+ },
50
+ /*lineChart : {
51
+ code : 'lineChart',
52
+ label : 'Graphique|Linéaire',
53
+ icon : "chart-areaspline",
54
+ type : 'line',
55
+ },
56
+ areaChart : {
57
+ code : 'areaChart',
58
+ label : 'Graphique|Surface',
59
+ icon : "chart-areaspline-variant",
60
+ type: 'area',
61
+ },
62
+ barChart : {
63
+ code : 'barChart',
64
+ label : 'Graphique|Barres',
65
+ icon : "chart-bar",
66
+ type: 'bar'
67
+ },
68
+ donutChar : {
69
+ code : 'donutChart',
70
+ label : 'Graphique|Circulaire',
71
+ icon : "chart-donut",
72
+ type: 'donut'
73
+ },*/
74
+ }
41
75
 
42
76
  export const arrayValueSeparator = ", ";
43
77
 
@@ -85,6 +119,17 @@ export default class CommonDatagridComponent extends AppComponent {
85
119
  showFooters : defaultBool(props.showFooters,(sData.showFooters? true : false)),
86
120
  fixedTable : sData.fixedTable
87
121
  });
122
+ const disTypes = {};
123
+ let hasFoundDisplayTypes = false;
124
+ Object.map(this.props.displayTypes,(dType,v)=>{
125
+ if(isNonNullString(dType)){
126
+ dType = dType.toLowerCase().trim();
127
+ if(displayTypes[dType]){
128
+ disTypes[dType] = Object.clone(displayTypes[dType]);
129
+ }
130
+ hasFoundDisplayTypes = true;
131
+ }
132
+ });
88
133
  Object.defineProperties(this,{
89
134
  layoutRef : {
90
135
  value : React.createRef(null),
@@ -119,6 +164,8 @@ export default class CommonDatagridComponent extends AppComponent {
119
164
  sectionListDataSize : {value : {current : 0}},
120
165
  enablePointerEventsRef : {value : {current:false}},
121
166
  configureSectionListSelectedValues : {value : {}},
167
+ ///les types d'affichage
168
+ displayTypes : {value : hasFoundDisplayTypes ? disTypes : Object.clone(displayTypes)},
122
169
  sectionListColumnsSize : {value : {current:0}}, //la taille du nombre d'éléments de section dans les colonnes
123
170
  })
124
171
  this.isLoading = this.isLoading.bind(this);
@@ -150,10 +197,11 @@ export default class CommonDatagridComponent extends AppComponent {
150
197
  this.state.filteredColumns = defaultObj(this.getSessionData("filteredColumns"+this.getSessionNameKey()),this.props.filters);
151
198
  this.filtersSelectors = {selector:this.getFilters()};
152
199
  const {sectionListColumns} = this.prepareColumns();
153
- this.state.sectionListColumns = sectionListColumns;
154
200
  if(this.canHandleColumnResize()){
155
201
  this.state.columnsWidths = this.preparedColumns.widths;
156
202
  }
203
+ const dType = defaultStr(this.props.displayType,this.getSessionData("diplayType"),"table");
204
+ this.state.displayType = this.displayTypes[dType] ? this.displayTypes[dType].code : "table" in this.displayTypes ? "table" : Object.keys(this.displayTypes)[0];
157
205
  extendObj(this.state,this.prepareData({data}));
158
206
  const {width:windowWidth,height:windowHeight} = Dimensions.get("window");
159
207
  this.state.layout = {
@@ -198,7 +246,10 @@ export default class CommonDatagridComponent extends AppComponent {
198
246
  } else {
199
247
  this.currentDataSources = Object.toArray(this.currentDataSources);
200
248
  }
249
+ this.state.sectionListColumns = sectionListColumns;
250
+ this.state.displayOnlySectionListHeaders = defaultBool(this.getSessionData("displayOnlySectionListHeaders"),this.props.displayOnlySectionListHeaders,false)
201
251
  }
252
+
202
253
  /*** si une ligne peut être selectionable */
203
254
  canSelectRow(row){
204
255
  return isObj(row) && row.isSectionListHeader !== true ? true : false;
@@ -206,7 +257,7 @@ export default class CommonDatagridComponent extends AppComponent {
206
257
  prepareSectionListColumns(props){
207
258
  props = defaultObj(props,this.props);
208
259
  const l = {};
209
- (Array.isArray(props.sectionListColumns) ? props.sectionListColumns : []).map((col)=>{
260
+ (Array.isArray(props.sectionListColumns) ? props.sectionListColumns : defaultArray(this.getSessionData("sectionListColumns"))).map((col)=>{
210
261
  if(isNonNullString(col)){
211
262
  l[col.trim()] = {};
212
263
  }
@@ -1052,6 +1103,7 @@ export default class CommonDatagridComponent extends AppComponent {
1052
1103
  this.prepareData({data:this.INITIAL_STATE.data,sectionListColumns:pSListColumns},(state)=>{
1053
1104
  this.setState({...state,sectionListColumns:pSListColumns},()=>{
1054
1105
  this.setIsLoading(false,false);
1106
+ this.setSessionData("sectionListColumns",Object.keys(pSListColumns));
1055
1107
  });
1056
1108
  });
1057
1109
  },true);
@@ -1069,16 +1121,106 @@ export default class CommonDatagridComponent extends AppComponent {
1069
1121
  this.prepareData({data:this.INITIAL_STATE.data,sectionListColumns},(state)=>{
1070
1122
  this.setState({...state,sectionListColumns},()=>{
1071
1123
  this.setIsLoading(false,false);
1124
+ this.setSessionData("sectionListColumns",null);
1072
1125
  });
1073
1126
  });
1074
1127
  },true);
1075
1128
  }
1129
+ canDisplayOnlySectionListHeaders(){
1130
+ return this.hasFooterFields() && this.isSectionList() && this.hasSectionListData();
1131
+ }
1132
+ /*** si l'on peut rendre le contenu de type graphique */
1133
+ isChartRendable(){
1134
+ return !this.isPivotDatagrid() && this.hasFooterFields() && this.isSectionList();
1135
+ }
1136
+ canRenderChart(){
1137
+ return this.isChartRendable() && this.state.displayType.toLowerCase().contains("chart");
1138
+ }
1139
+ /***modifie le type de données d'affichage du tableau */
1140
+ setDisplay(type){
1141
+ if(!isObj(type) || !isNonNullString(type.code) || !displayTypes[type.code]) return;
1142
+ const tt = displayTypes[type.code];
1143
+ if(tt.code == 'table'){
1144
+ if(this.state.displayType == 'table') return;
1145
+ return this.setIsLoading(true,()=>{
1146
+ this.setState({displayTypes:'table'},()=>{
1147
+ this.setIsLoading(false,false);
1148
+ });
1149
+ },true)
1150
+ } else {
1151
+ DialogProvider.open({
1152
+ title : 'Configuration de la courbe'
1153
+ })
1154
+ }
1155
+ }
1156
+ renderDisplayTypes(){
1157
+ const m = [];
1158
+ let activeType = null;
1159
+ Object.map(this.displayTypes,(type,k)=>{
1160
+ let c = k.toLowerCase();
1161
+ if(c.contains('chart') && !this.isChartRendable()) return null;
1162
+ const active = this.state.displayType === k;
1163
+ if(active){
1164
+ activeType = type;
1165
+ }
1166
+ m.push({
1167
+ ...type,
1168
+ labelStyle : active && {fontWeight:'bold',color:theme.colors.primaryOnSurface} || null,
1169
+ onPress:()=>{
1170
+ this.setDisplay(type);
1171
+ }
1172
+ })
1173
+ });
1174
+ if(m.length <= 1 || !activeType) return null;
1175
+ if(!isMobileOrTabletMedia()){
1176
+ m.unshift({text:"Type d'affichage des données",divider:true});
1177
+ }
1178
+ return <Menu
1179
+ title = "Type d'affichage"
1180
+ items = {m}
1181
+ anchor = {(p)=>{
1182
+ return <Icon
1183
+ {...p}
1184
+ name = {activeType.icon}
1185
+ title = {"Les données s'affichent actuellement en {0}. Cliquez pour modifier le type d'affichage".sprintf(activeType.label)}
1186
+ />
1187
+ }}
1188
+ />
1189
+ }
1190
+ renderChart(){
1191
+ if(!this.canRenderChart()) return null;
1192
+ }
1193
+ toggleDisplayOnlySectionListHeaders(){
1194
+ if(!this.canDisplayOnlySectionListHeaders()) return
1195
+ setTimeout(()=>{
1196
+ const displayOnlySectionListHeaders = !!!this.state.displayOnlySectionListHeaders;
1197
+ this.setSessionData("displayOnlySectionListHeaders",displayOnlySectionListHeaders);
1198
+ if(!displayOnlySectionListHeaders){
1199
+ return this.prepareData({data:this.INITIAL_STATE.data,displayOnlySectionListHeaders},(state)=>{
1200
+ this.setState(state)
1201
+ })
1202
+ } else {
1203
+ this.setIsLoading(true,()=>{
1204
+ const data = [];
1205
+ this.state.data.map((d)=>{
1206
+ if(isObj(d) && d.isSectionListHeader === true){
1207
+ data.push(d);
1208
+ }
1209
+ });
1210
+ this.setState({data,displayOnlySectionListHeaders},()=>{
1211
+ this.setIsLoading(false),false;
1212
+ });
1213
+ },true)
1214
+ }
1215
+ },100);
1216
+ }
1076
1217
  /*** permet d'effectuer le rendu des colonnes groupable dans le menu item */
1077
1218
  renderSectionListMenu(){
1078
1219
  const m = Array.isArray(this.preparedColumns?.sectionListColumnsMenuItems)? this.preparedColumns?.sectionListColumnsMenuItems : [];
1079
1220
  if(!m.length){
1080
1221
  return null;
1081
1222
  }
1223
+ const hasList = this.sectionListColumnsSize.current;
1082
1224
  const isMobile = isMobileOrTabletMedia();
1083
1225
  return <Menu
1084
1226
  title = {"Grouper les données du tableau"}
@@ -1093,7 +1235,12 @@ export default class CommonDatagridComponent extends AppComponent {
1093
1235
  closeOnPress : false,
1094
1236
  divider : true,
1095
1237
  },
1096
- this.sectionListColumnsSize.current && {
1238
+ this.canDisplayOnlySectionListHeaders() && {
1239
+ text : "Afficher uniquement totaux",
1240
+ icon : this.state.displayOnlySectionListHeaders?"check":null,
1241
+ onPress : this.toggleDisplayOnlySectionListHeaders.bind(this)
1242
+ },
1243
+ hasList && {
1097
1244
  text : "Supprimer les groupes",
1098
1245
  icon: "ungroup",
1099
1246
  divider : true,
@@ -1314,9 +1461,9 @@ export default class CommonDatagridComponent extends AppComponent {
1314
1461
  getFooterValues(){
1315
1462
  return defaultObj(this.___evaluatedFootersValues);
1316
1463
  }
1317
-
1464
+
1318
1465
  /**** s'il s'agit d'une section list */
1319
- isSectionList(sectionListColumns){
1466
+ isSectionList (sectionListColumns){
1320
1467
  sectionListColumns = isObj(sectionListColumns) ? sectionListColumns : this.state.sectionListColumns;
1321
1468
  return !this.isPivotDatagrid() && isObj(sectionListColumns) && Object.size(sectionListColumns,true) ? true : false;
1322
1469
  }
@@ -1341,13 +1488,14 @@ export default class CommonDatagridComponent extends AppComponent {
1341
1488
  return defaultNumber(this.sectionListDataSize.current)
1342
1489
  }
1343
1490
  prepareData(args,cb){
1344
- let {pagination,data,force,sectionListColumns,updateFooters} = defaultObj(args);
1491
+ let {pagination,displayOnlySectionListHeaders:cdisplayOnlySectionListHeaders,data,force,sectionListColumns,updateFooters} = defaultObj(args);
1345
1492
  cb = typeof cb ==='function'? cb : typeof args.cb == 'function'? args.cb : undefined;
1346
1493
  sectionListColumns = isObj(sectionListColumns) ? sectionListColumns : this.state.sectionListColumns;
1494
+ const displayOnlySectionListHeaders = typeof cdisplayOnlySectionListHeaders == 'boolean'?cdisplayOnlySectionListHeaders : this.state.displayOnlySectionListHeaders;
1347
1495
  let isArr = Array.isArray(data);
1348
1496
  //let push = (d,index) => isArr ? newData.push(d) : newData[index] = d;
1349
1497
  const hasLocalFilter = this.props.filters !== false && this.hasLocalFilters;
1350
- let footersColumns = this.getFooterFields(),hasFooterFields = Object.size(footersColumns,true);
1498
+ let footersColumns = this.getFooterFields(),hasFooterFields = this.hasFooterFields();
1351
1499
  const canUpdateFooters = !!(updateFooters !== false && hasFooterFields);
1352
1500
  this.hasFoundSectionData.current = false;
1353
1501
  this.sectionListDataSize.current = 0;
@@ -1430,9 +1578,11 @@ export default class CommonDatagridComponent extends AppComponent {
1430
1578
  //this.sectionListData[i] = sortConfig ? sortBy(this.sectionListData[i],sortConfig):this.sectionListData[i];
1431
1579
  //const v = i;// === this.emptySectionListHeaderValue ? "" : i;
1432
1580
  data.push({isSectionListHeader:true,sectionListHeaderKey:i});
1433
- this.sectionListData[i].map((d)=>{
1434
- data.push(d);
1435
- })
1581
+ if(!displayOnlySectionListHeaders){
1582
+ this.sectionListData[i].map((d)=>{
1583
+ data.push(d);
1584
+ })
1585
+ }
1436
1586
  }
1437
1587
  }
1438
1588
  if(!this.hasSectionListData() && this.canPaginateData()){
@@ -1444,7 +1594,7 @@ export default class CommonDatagridComponent extends AppComponent {
1444
1594
  } else if(force){
1445
1595
  this.setSelectedRows();
1446
1596
  }
1447
- const state = {data};
1597
+ const state = {data,displayOnlySectionListHeaders};
1448
1598
  if((cb)){
1449
1599
  cb(state);
1450
1600
  }
@@ -2274,7 +2424,7 @@ export const ProgressBar = CommonDatagridComponent.LinesProgressBar;
2274
2424
  CommonDatagridComponent.getDefaultPreloader = (props)=>{
2275
2425
  return <Preloader {...defaultObj(props)}/>
2276
2426
  }
2277
-
2427
+ const allDTypes = Object.keys(displayTypes);
2278
2428
  CommonDatagridComponent.propTypes = {
2279
2429
  canMakePhoneCall : PropTypes.bool,//si l'on peut faire un appel sur la données sélectionnées
2280
2430
  makePhoneCallProps : PropTypes.oneOfType([
@@ -2451,6 +2601,13 @@ CommonDatagridComponent.propTypes = {
2451
2601
  /**** la fonction permettant de faire le rendu dun contenu paginé, personalisé */
2452
2602
  renderCustomPagination : PropTypes.func,
2453
2603
  getActionsArgs : PropTypes.func,//fonction permettant de récupérer les props supplémentaires à passer aux actions du datagrid
2604
+ displayOnlySectionListHeaders : PropTypes.bool,// si uniquement les entêtes des sections seront affichés, valides uniquement en affichage des sectionHeader
2605
+
2606
+ /*** types d'affichage displayTypes */
2607
+ /** les types d'affichage supportés */
2608
+ displayType : PropTypes.oneOf(allDTypes),
2609
+ /*** les types d'afichates supportés par l'application */
2610
+ displayTypes : PropTypes.arrayOf(PropTypes.oneOf(allDTypes))
2454
2611
  }
2455
2612
 
2456
2613
  const styles = StyleSheet.create({
@@ -0,0 +1,12 @@
1
+ // Copyright 2022 @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
+
5
+ Les propriétés d'états :
6
+ displayType, displayOnlySectionListHeaders, sectionListHeaderColumns sont persistées
7
+ ça sous entend que lorsqu'on charge la table data, ces valeurs sont restaurées
8
+
9
+ - Pour faire un affichage en graphique, il faut :
10
+ 1. grouper les éléments du tableau avec au moins une colonne
11
+ 2. disposer dans le tableau, des colonnes de totalisation (de type montant et somme)
12
+ 3.
@@ -284,6 +284,7 @@ const DatagridFactory = (Factory)=>{
284
284
 
285
285
  />
286
286
  {this.renderSectionListMenu()}
287
+ {this.renderDisplayTypes()}
287
288
  <View testID={testID+"_HeaderPagination"} style = {styles.paginationItem}>
288
289
  <BottomSheetMenu
289
290
  testID={testID+"_HeaderMenus"}
@@ -85,7 +85,7 @@ export const selectDateFormatFieldProps = ({onAdd:customOnAdd,onAddCustomFormat,
85
85
  showAdd : false,
86
86
  }
87
87
  }
88
- export const getDateFormatSelectorItems = x=> Object.map(DateLib.formats,(format)=>{
88
+ export const getDateFormatSelectorItems = x=> Object.map(DateLib.sortedFormats,(format)=>{
89
89
  return {code : format,label:new Date().toFormat(format)}
90
90
  });
91
91
 
@@ -2,6 +2,7 @@ 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";
5
6
 
6
7
  export default class FormSelectField extends Field{
7
8
  constructor(props) {
@@ -115,8 +116,9 @@ export default class FormSelectField extends Field{
115
116
  delete props.format;
116
117
  delete props.import;
117
118
  delete props.exports;
118
- if( defaultStr(props.name,this.name) === 'country' && props.multiple !== true){
119
- props.defaultValue = defaultStr(props.defaultValue);
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"));
120
122
  }
121
123
  return <Dropdown
122
124
  {...props}
@@ -1,5 +1,6 @@
1
1
  import PhoneInput from "$ecomponents/PhoneInput";
2
2
  import Field from "./Field";
3
+ import appConfig from "$capp/config";
3
4
 
4
5
  export default class FormPhoneInputField extends Field {
5
6
  canFocus(){
@@ -9,6 +10,7 @@ export default class FormPhoneInputField extends Field {
9
10
  props.onChange = (args)=>{
10
11
  this.validate(args);
11
12
  }
13
+ props.country = defaultStr(props.country,appConfig.countryCode,appConfig.get("countryCode","country"))
12
14
  return <PhoneInput
13
15
  {...props}
14
16
  setRef = {setRef}
@@ -27,12 +27,13 @@ const MenuItemComponent = React.forwardRef(({
27
27
  style,
28
28
  contentStyle,
29
29
  testID,
30
- titleStyle,
30
+ labelStyle,
31
31
  primary,
32
32
  secondary,
33
33
  iconProps,
34
34
  isBottomSheetItem,
35
35
  accessibilityLabel,
36
+ right,
36
37
  ...rest
37
38
  },ref) => {
38
39
  title = defaultVal(label,text,title);
@@ -49,20 +50,20 @@ const MenuItemComponent = React.forwardRef(({
49
50
  ? disabledColor
50
51
  : color(theme.colors.text).alpha(theme.ALPHA).rgb().string();
51
52
  iconProps = defaultObj(iconProps);
52
- titleStyle = Object.assign({},StyleSheet.flatten(titleStyle));
53
+ labelStyle = Object.assign({},StyleSheet.flatten(labelStyle));
53
54
  style = Object.assign({},StyleSheet.flatten(style));
54
55
 
55
- if(Colors.isValid(titleStyle.color)){
56
- iconColor = titleStyle.color;
56
+ if(Colors.isValid(labelStyle.color)){
57
+ iconColor = labelStyle.color;
57
58
  } else if(Colors.isValid(style.color)){
58
59
  titleColor = iconColor = style.color;
59
60
  }
60
-
61
61
  const pointerEvents = disabled ? 'none' : 'auto';
62
62
  const disabledStyle = disabled ? {opacity:DISABLED_OPACITY} : null;
63
63
  const winW = Dimensions.get("window").width-30;
64
64
  const maxWidthStyle = isBottomSheetItem ? {width:winW,maxWidth:null} : undefined;
65
65
  const maxWidthTextStyle = isBottomSheetItem ? {width:winW-50} : null;
66
+ right = typeof right =='function'? right ({color:titleColor}) : right;
66
67
  return (
67
68
  <Tooltip
68
69
  {...defaultObj(rest)}
@@ -77,13 +78,14 @@ const MenuItemComponent = React.forwardRef(({
77
78
  accessibilityState={{ disabled }}
78
79
  pointerEvents = {pointerEvents}
79
80
  >
80
- <View style={[styles.row]} ref={ref}>
81
+ <View style={[styles.row]} ref={ref} testID={testID+"_ContentContainer"}>
81
82
  {icon ? (
82
- <View style={[styles.item, styles.icon]} pointerEvents="box-none">
83
+ <View testID={testID+"_IconContainer"} style={[styles.item, styles.icon]} pointerEvents="box-none">
83
84
  <Icon source={icon} size={24} {...iconProps} style={[iconProps.style,styles.iconT]} color={iconColor} />
84
85
  </View>
85
86
  ) : null}
86
87
  <View
88
+ testID={testID+"_LabelContainer"}
87
89
  style={[
88
90
  styles.item,
89
91
  styles.content,
@@ -95,14 +97,16 @@ const MenuItemComponent = React.forwardRef(({
95
97
  pointerEvents="none"
96
98
  >
97
99
  <Label
100
+ testID={testID+"_Label"}
98
101
  selectable={false}
99
102
  numberOfLines={1}
100
103
  ellipsizeMode = {"tail"}
101
- style={[styles.title, { color: titleColor }, titleStyle,styles.noMargin,maxWidthTextStyle]}
104
+ style={[styles.title, { color: titleColor }, labelStyle,styles.noMargin,maxWidthTextStyle]}
102
105
  >
103
106
  {title}
104
107
  </Label>
105
108
  </View>
109
+ {React.isValidElement(right) && right || null}
106
110
  </View>
107
111
  </Tooltip>
108
112
  );
@@ -185,7 +189,7 @@ MenuItemComponent.propTypes = {
185
189
  */
186
190
  style: StylePropTypes,
187
191
  contentStyle: StylePropTypes,
188
- titleStyle : StylePropTypes,
192
+ labelStyle : StylePropTypes,
189
193
  /**
190
194
  * @optional
191
195
  */