@fto-consult/expo-ui 2.13.1 → 2.14.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.13.1",
3
+ "version": "2.14.0",
4
4
  "description": "Bibliothèque de composants UI Expo,react-native",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/src/App.js CHANGED
@@ -87,6 +87,7 @@ export default function getIndex(options){
87
87
  },
88
88
  isVisible() {
89
89
  /* Customize the visibility state detector */
90
+ return false;
90
91
  return isScreenFocusedRef.current;
91
92
  },
92
93
  initFocus(callback) {
@@ -325,6 +325,7 @@ const DatagridFactory = (Factory)=>{
325
325
  backToTopProps,
326
326
  testID,
327
327
  renderEmpty,
328
+ ...rest
328
329
  } = this.props
329
330
  const hasData = this.state.data.length ? true : false;
330
331
  testID = defaultStr(testID,"RN_DatagridAccordion");
@@ -433,6 +434,7 @@ const DatagridFactory = (Factory)=>{
433
434
  <View testID={testID+"_HeaderQueryLimit"} style={[styles.paginationItem]}>
434
435
  {this.renderQueryLimit(this.state.data.length.formatNumber())}
435
436
  </View>
437
+ {this.renderCustomPagination()}
436
438
  {sortedColumnsLength ? <View testID={testID+"_HeaderSortedColumns"} style={[styles.sortableItems,styles.paginationItem,{paddingRight:10}]}>
437
439
  <Icon
438
440
  testID={testID+"_HeaderSortIcon"}
@@ -503,6 +505,7 @@ const DatagridFactory = (Factory)=>{
503
505
  ,icon : showFooters?'view-column':'view-module'
504
506
  ,text : (showFooters?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux')
505
507
  }:null,
508
+ ...this.renderCustomMenu(),
506
509
  ...restItems,
507
510
  this.canScrollTo() && {
508
511
  text : 'Retour en haut',
@@ -579,6 +582,7 @@ const DatagridFactory = (Factory)=>{
579
582
  {hasData ? <FlashList
580
583
  estimatedItemSize = {150}
581
584
  prepareItems = {false}
585
+ {...rest}
582
586
  {...accordionProps}
583
587
  testID = {testID}
584
588
  extraData = {this.state.refresh}
@@ -308,6 +308,7 @@ export default class CommonDatagridComponent extends AppComponent {
308
308
  return ;
309
309
  }
310
310
  canPaginateData(){
311
+ if(this.props.handlePagination === false) return false;
311
312
  return false;
312
313
  }
313
314
  getFiltersProps(){
@@ -752,7 +753,41 @@ export default class CommonDatagridComponent extends AppComponent {
752
753
  }
753
754
  this.handlePagination(newStart, limit, nextPage);
754
755
  }
755
-
756
+ /*** pour le rendu personalisé de la pagination */
757
+ renderCustomPagination(){
758
+ if(typeof this.props.renderCustomPagination ==='function'){
759
+ const r = this.props.renderCustomPagination({
760
+ context:this,refresh:this.refresh.bind(this)
761
+ });
762
+ return React.isValidElement(r)? r : null;
763
+ }
764
+ return null;
765
+ }
766
+ /*** permet de faire le rendu de certaines entête personalisés
767
+ * utile lorsque l'on veut par exemple afficher d'autres information au niveau de l'entête du tableau
768
+ */
769
+ renderCustomMenu(){
770
+ const customMenu = []
771
+ Object.map(this.props.customMenu,(menu,i)=>{
772
+ if(isObj(menu)){
773
+ const {onPress,menu,label,text,children,...rest} = menu;
774
+ const args = {context:this,refresh:this.refresh.bind(this)};
775
+ const lCB = defaultVal(children,label,text);
776
+ const labelText = typeof lCB ==='function'? lCB(args) : lCB;
777
+ if(labelText === false || !React.isValidElement(labelText,true)) return;
778
+ customMenu.push({
779
+ ...rest,
780
+ label : labelText,
781
+ onPress : (event)=>{
782
+ if(typeof onPress =='function'){
783
+ return onPress({...React.getOnPressArgs(event),...args})
784
+ }
785
+ }
786
+ })
787
+ }
788
+ });
789
+ return customMenu;
790
+ }
756
791
  /*** aller à la dernière page */
757
792
  _goToLastPage(){
758
793
  let { start,limit} = this._pagination;
@@ -1320,7 +1355,7 @@ export default class CommonDatagridComponent extends AppComponent {
1320
1355
  this._pagination.start = 0;
1321
1356
  }
1322
1357
  this.filtersSelectors = {selector:this.getFilters()};
1323
- return this.fetchData({force:true,fetchOptions:this.filtersSelectors});
1358
+ return this.fetchData({force:true,isFiltering : true,fetchOptions:this.filtersSelectors});
1324
1359
  }
1325
1360
  onSetQueryLimit(){
1326
1361
  if(!this.canSetQueryLimit()) return;
@@ -1334,7 +1369,7 @@ export default class CommonDatagridComponent extends AppComponent {
1334
1369
  this.currentDatagridQueryLimit = this.getSessionData("dataSourceQueryLimit");
1335
1370
  setQueryLimit(this.currentDatagridQueryLimit,(limit)=>{
1336
1371
  this.setSessionData("dataSourceQueryLimit",limit)
1337
- notify.success("Le nombre maximal d'élément a été définit à la valeur "+(limit==0?" infinit ":limit.formatNumber())+". Cette valeur sera prise en compte à la prochaine réactualisation du tableau")
1372
+ notify.success("Le nombre maximal d'élément à récuperer par page a été définit à la valeur "+(limit==0?" infinit ":limit.formatNumber())+". Cette valeur sera prise en compte à la prochaine réactualisation du tableau")
1338
1373
  });
1339
1374
  }
1340
1375
 
@@ -1348,6 +1383,9 @@ export default class CommonDatagridComponent extends AppComponent {
1348
1383
  return 0;
1349
1384
  }
1350
1385
  canHandleQueryLimit(){
1386
+ if(typeof this.props.handleQueryLimit ==='boolean'){
1387
+ return this.props.handleQueryLimit;
1388
+ }
1351
1389
  return true;
1352
1390
  }
1353
1391
  renderQueryLimit(content){
@@ -1357,7 +1395,7 @@ export default class CommonDatagridComponent extends AppComponent {
1357
1395
  let s = "";
1358
1396
  let canSetQ = this.canSetQueryLimit();
1359
1397
  if(canSetQ){
1360
- s = ".\nPressez pendent quelques secondes pour modifier cette valeur du nombre limite d'éléments de la liste";
1398
+ s = ".\nPressez pendent quelques secondes pour modifier cette valeur du nombre limite d'éléments de la liste par page";
1361
1399
  }
1362
1400
  else if(isDecimal(cLImit)){
1363
1401
  cLImit = (" de "+cLImit.formatNumber())
@@ -1666,16 +1704,14 @@ export default class CommonDatagridComponent extends AppComponent {
1666
1704
  else if(_type =='select_country' || _type =='selectcountry'){
1667
1705
  _render = <Flag withCode {...columnDef} length={undefined} width={undefined} height={undefined} code={defaultValue}/>
1668
1706
  }
1669
- ///le lien vers le table data se fait via une colonne de type selecttabledata ou select_tabledata ou potant l'une des propriétés foreignKeyTable ou linkToTable de type chaine de caractère non nulle
1670
- else if(arrayValueExists(['piece','selecttabledata','id'],_type) || isNonNullString(columnDef.linkToTable) || isNonNullString(columnDef.foreignKeyTable)){
1671
- let tableName = defaultStr(columnDef.linkToTable && columnDef.linkToTable,columnDef.foreignKeyTable && columnDef.foreignKeyTable,columnDef.tableName,columnDef.table).toUpperCase();
1707
+ ///le lien vers le table data se fait via la colonne ayant la propriété foreignKeyTable de type chaine de caractère non nulle
1708
+ else if(isNonNullString(columnDef.foreignKeyTable)){
1672
1709
  const id = rowData[columnField]?.toString();
1673
1710
  if(isNonNullString(id)){
1674
1711
  _render = <TableLink
1675
- tableName = {tableName}
1676
1712
  id = {id}
1713
+ {...columnDef}
1677
1714
  data = {rowData}
1678
- columnDef = {columnDef}
1679
1715
  columnField = {columnField}
1680
1716
  >
1681
1717
  {rowData[columnField]}
@@ -1950,6 +1986,14 @@ CommonDatagridComponent.propTypes = {
1950
1986
  /*** pour le rendu du footer, pied de page en affichage accordion */
1951
1987
  sessionName : PropTypes.string,
1952
1988
  onFetchData : PropTypes.func,
1989
+ handleQueryLimit : PropTypes.bool, ///si le datagrid devra gérer les queryLimit
1990
+ /**** les menus customisés à ajouter au composant Datagrid */
1991
+ customMenu : PropTypes.oneOfType([
1992
+ PropTypes.arrayOf(PropTypes.object),
1993
+ PropTypes.objectOf(PropTypes.object),
1994
+ ]),
1995
+ /**** la fonction permettant de faire le rendu dun contenu paginé, personalisé */
1996
+ renderCustomPagination : PropTypes.func,
1953
1997
  getActionsArgs : PropTypes.func,//fonction permettant de récupérer les props supplémentaires à passer aux actions du datagrid
1954
1998
  }
1955
1999
 
@@ -60,7 +60,7 @@ export default class CommonTableDatagrid extends CommonDatagrid{
60
60
  la table dans la base common.
61
61
  Elle pourra éventuellement passer directement la limite et les filtres à la fonction fetchdata
62
62
  */
63
- fetchData ({cb,callback,force,fetchOptions}){
63
+ fetchData ({cb,callback,force,fetchOptions,...rest}){
64
64
  if(!this._isMounted()) return Promise.resolve(this.state.data);
65
65
  if(this.isFetchingData) {
66
66
  if(!isPromise(this.fetchingPromiseData)){
@@ -92,7 +92,7 @@ export default class CommonTableDatagrid extends CommonDatagrid{
92
92
  }
93
93
  }
94
94
  this.beforeFetchData(fetchOptions);
95
- if(typeof this.props.beforeFetchData =='function' && this.props.beforeFetchData({context:this,force,fetchOptions,options:fetchOptions}) === false){
95
+ if(typeof this.props.beforeFetchData =='function' && this.props.beforeFetchData({...rest,context:this,force,fetchOptions,options:fetchOptions}) === false){
96
96
  this.isFetchingData = false;
97
97
  return resolve(this.state.data);
98
98
  }
@@ -7,6 +7,8 @@
7
7
  */
8
8
  import Datagrid from "./IndexComponent";
9
9
  import {defaultStr,defaultObj,defaultVal,isObjOrArray,isObj,extendObj} from "$utils";
10
+ import {Pressable} from "react-native";
11
+ import SimpleSelect from "$ecomponents/SimpleSelect";
10
12
  import React from "$react";
11
13
  import Auth from "$cauth";
12
14
  import DateLib from "$lib/date";
@@ -22,6 +24,9 @@ import appConfig from "$capp/config";
22
24
  import APP from "$capp/instance";
23
25
  import cAction from "$cactions";
24
26
  import PropTypes from "prop-types";
27
+ import {isDesktopMedia} from "$dimensions";
28
+ import ActivityIndicator from "$ecomponents/ActivityIndicator";
29
+ import {Menu} from "$ecomponents/BottomSheet";
25
30
 
26
31
  const timeout = 5000*60*60;
27
32
  export const swrOptions = {
@@ -31,7 +36,9 @@ export const swrOptions = {
31
36
  errorRetryInterval : timeout*2,
32
37
  errorRetryCount : 5,
33
38
  }
34
-
39
+ const getDefaultPaginationRowsPerPageItems = ()=>{
40
+ return [5,10,15,20,25,30,40,50,60,80,100,500,1000,...(isDesktopMedia() ? [1500,2000,2500,3000,3500,4000,4500,5000,10000]:[])];
41
+ }
35
42
  /****la fonction fetcher doit toujours retourner :
36
43
  * 1. la liste des éléments fetchés dans la props data
37
44
  * 2. le nombre total d'éléments de la liste obtenue en escluant les clause limit et offset correspondant à la même requête
@@ -53,10 +60,13 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
53
60
  fetchData,
54
61
  fetchPath,
55
62
  fetcher,
63
+ ListFooterComponent,
64
+ testID,
56
65
  ...rest
57
66
  } = props;
58
67
  rest = defaultObj(rest);
59
68
  rest.exportTableProps = defaultObj(rest.exportTableProps)
69
+ const firstPage = 1;
60
70
  const tableName = defaultStr(table.tableName,table.table).trim().toUpperCase();
61
71
  canMakePhoneCall = defaultBool(canMakePhoneCall,table.canMakePhoneCall);
62
72
  makePhoneCallProps = defaultObj(makePhoneCallProps,rest.makePhoneCallProps,table.makePhoneCallProps);
@@ -98,13 +108,30 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
98
108
  const hasResultRef = React.useRef(false);
99
109
  const totalRef = React.useRef(0);
100
110
  const isFetchingRef = React.useRef(false);
111
+ const pageRef = React.useRef(1);
112
+ const limitRef = React.useRef(500);
113
+ const isInitializedRef = React.useRef(false);
114
+ testID = defaultStr(testID,"RNSWRDatagridComponent")
101
115
  const {error, isValidating,isLoading,refresh} = useSWR(fetchPath,{
102
116
  fetcher : (url,opts)=>{
117
+ if(!isInitializedRef.current) {
118
+ isFetchingRef.current = false;
119
+ return;
120
+ }
103
121
  opts = extendObj({},opts,fetchOptionsRef.current);
104
122
  opts.queryParams = defaultObj(opts.queryParams);
105
123
  opts.queryParams.withTotal = true;
124
+ opts.queryParams.limit = limitRef.current;
125
+ opts.queryParams.page = pageRef.current -1;
106
126
  const fetchCB = ({data,total})=>{
107
127
  totalRef.current = total;
128
+ /***
129
+ * if(pageRef.current ===firstPage){
130
+ dataRef.current = data;
131
+ } else {
132
+ dataRef.current = prevPage != pageRef.current ? (isObj(data)?{...dataRef.current,...data}:[...dataRef.current,...data]) : data;
133
+ }
134
+ */
108
135
  dataRef.current = data;
109
136
  hasResultRef.current = true;
110
137
  return data;
@@ -142,10 +169,41 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
142
169
  showProgressRef.current = showProgress ? typeof showProgress ==='boolean' : false;
143
170
  if(isFetchingRef.current) return;
144
171
  refreshCBRef.current = ()=>{
145
- showProgressRef.current = false;
172
+ //showProgressRef.current = false;
146
173
  };
147
174
  refresh();
148
175
  }
176
+ const canPagninate = ()=>{
177
+ if(typeof totalRef.current !=='number' || typeof pageRef.current !='number' || typeof limitRef.current !='number') return false;
178
+ if(limitRef.current <= 0) return false;
179
+ return true;
180
+ }
181
+ const getTotalPages = ()=>{
182
+ if(!canPagninate()) return false;
183
+ return Math.ceil(totalRef.current / limitRef.current);;
184
+ };
185
+ const getNextPage = ()=>{
186
+ if(!canPagninate()) return false;
187
+ const totalPages = getTotalPages();
188
+ let nPage = pageRef.current+1;
189
+ if(nPage > totalPages){
190
+ nPage = totalPages;
191
+ }
192
+ if(nPage === pageRef.current){
193
+ return false;
194
+ }
195
+ return nPage;
196
+ },getPrevPage = ()=>{
197
+ if(!canPagninate()) return false;
198
+ let pPage = pageRef.current - 1;
199
+ if(pPage < firstPage){
200
+ pPage = firstPage;
201
+ }
202
+ if(pPage === pageRef.current){
203
+ return false;
204
+ }
205
+ return pPage;
206
+ }
149
207
  React.useEffect(()=>{
150
208
  const upsert = cAction.upsert(tableName);
151
209
  const remove = cAction.remove(tableName);
@@ -159,15 +217,138 @@ const SWRDatagridComponent = React.forwardRef((props,ref)=>{
159
217
  APP.off(remove,onUpsert);
160
218
  }
161
219
  },[]);
220
+ const loading = (isLoading|| isValidating);
221
+ const pointerEvents = loading ?"node" : "auto";
162
222
  return (
163
223
  <Datagrid
224
+ testID = {testID}
164
225
  {...rest}
165
226
  {...defaultObj(table.datagrid)}
166
- isLoading = {(isLoading|| isValidating) && !error && showProgressRef.current && true || false}
227
+ renderCustomPagination = {({context})=>{
228
+ if(!canPagninate()) return null;
229
+ const page = pageRef.current, totalPages = getTotalPages(), prevPage = getPrevPage(),nextPage = getNextPage();
230
+ const iconProp = {
231
+ size : 25,
232
+ style : [theme.styles.noMargin,theme.styles.noPadding],
233
+ }
234
+ const sStyle = [styles.limitStyle1,theme.styles.noPadding,theme.styles.noMargin];
235
+ return <View testID={testID+"_PaginationContainer"} pointerEvents={pointerEvents}>
236
+ <View style={[theme.styles.row,theme.styles.w100]} pointerEvents={pointerEvents} testID={testID+"_PaginationContentContainer"}>
237
+ <Menu
238
+ testID={testID+"_SimpleSelect"}
239
+ style = {sStyle}
240
+ anchor = {(p)=>{
241
+ return <Pressable style={[theme.styles.noMargin,theme.styles.noPadding]} {...p} testID={testID+"MenuSelectLimit"}>
242
+ <Label primary testID={testID+"_Label"} style={[{fontSize:15}]}>
243
+ {limitRef.current.formatNumber()}
244
+ </Label>
245
+ </Pressable>
246
+ }}
247
+ title = {'Limite du nombre d\'éléments par page'}
248
+ items = {getDefaultPaginationRowsPerPageItems().map((item)=>{
249
+ return {
250
+ text : item.formatNumber(),
251
+ icon : limitRef.current == item ? 'check' : null,
252
+ primary : limitRef.current === item ? true : false,
253
+ onPress : ()=>{
254
+ if(item == limitRef.current) return;
255
+ limitRef.current = item;
256
+ pageRef.current = firstPage;
257
+ setTimeout(() => {
258
+ doRefresh(true);
259
+ }, (500));
260
+ }
261
+ }
262
+ })}
263
+ />
264
+ <Icon
265
+ ///firstPage
266
+ {...iconProp}
267
+ title = {"Aller à la première page"}
268
+ name="material-first-page"
269
+ disabled = {pageRef.current <= 1 && true || false}
270
+ onPress = {()=>{
271
+ if(pageRef.current <= firstPage) return;
272
+ pageRef.current = firstPage;
273
+ doRefresh(true);
274
+ }}
275
+ />
276
+ <Icon
277
+ //decrement
278
+ {...iconProp}
279
+ title = {"Aller à la page précédente {0}".sprintf(prevPage && prevPage.formatNumber()||undefined)}
280
+ name="material-keyboard-arrow-left"
281
+ disabled = {page === prevPage || getPrevPage() === false ? true : false}
282
+ onPress = {()=>{
283
+ const page = getPrevPage();
284
+ if(page === false) return;
285
+ if(pageRef.current === page) return;
286
+ pageRef.current = page;
287
+ doRefresh(true);
288
+ }}
289
+
290
+ />
291
+ <View testID={testID+"_PaginationLabel"}>
292
+ <Label style={{fontSize:15}}>
293
+ {page.formatNumber()}-{totalPages.formatNumber()}{" / "}{totalRef.current.formatNumber()}
294
+ </Label>
295
+ </View>
296
+ <Icon
297
+ //increment
298
+ {...iconProp}
299
+ title = {"Aller à la page suivante {0}".sprintf(nextPage && nextPage.formatNumber()||undefined)}
300
+ name="material-keyboard-arrow-right"
301
+ disabled = {nextPage >= totalPages || getNextPage() === false ? true : false}
302
+ onPress = {()=>{
303
+ const page = getNextPage();
304
+ if(page === false) return;
305
+ if(pageRef.current === page) return;
306
+ pageRef.current = page;
307
+ doRefresh(true);
308
+ }}
309
+ />
310
+ <Icon
311
+ //lastPage
312
+ {...iconProp}
313
+ name="material-last-page"
314
+ title = {"Aller à la dernière page {0}".sprintf(totalPages && totalPages.formatNumber()||undefined)}
315
+ disabled = {page >= totalPages ? true : false}
316
+ onPress = {()=>{
317
+ const page = getTotalPages();
318
+ if(pageRef.current >= page) return;
319
+ pageRef.current = page;
320
+ doRefresh(true);
321
+ }}
322
+
323
+ />
324
+ </View>
325
+ </View>
326
+ }}
327
+ ListFooterComponent = {(props)=>{
328
+ const r = typeof ListFooterComponent =='function'? ListFooterComponent(props) : null;
329
+ if(!loading) return r;
330
+ const aContent = <View testID={testID+"_ListHeaderActivityIndicator"} style={[theme.styles.w100,theme.styles.justifyContentCenter]}>
331
+ <ActivityIndicator color={theme.colors.primary}/>
332
+ </View>;
333
+ if(r){
334
+ return <View testID={testID+"_ListHeaderContainer"} style={[theme.styles.w100]}>
335
+ {r}
336
+ {aContent}
337
+ </View>
338
+ }
339
+ return aContent;
340
+ }}
341
+ handleQueryLimit = {false}
342
+ handlePagination = {false}
343
+ isLoading = {loading && !error && showProgressRef.current && true || false}
167
344
  beforeFetchData = {({fetchOptions:opts,force})=>{
168
345
  opts.fields = fetchFields;
169
346
  opts = getFetchOptions({showError:showProgressRef.current,...opts});
347
+ isInitializedRef.current = true;
170
348
  fetchOptionsRef.current = opts;
349
+ if(force){
350
+ pageRef.current = firstPage;
351
+ }
171
352
  doRefresh(force);
172
353
  return false;
173
354
  }}
@@ -218,6 +399,11 @@ const styles = StyleSheet.create({
218
399
  labelTitle: {
219
400
  fontSize : 18,
220
401
  },
402
+ limitStyle : {
403
+ backgroundColor:'transparent',
404
+ width:50,
405
+ height : 35,
406
+ },
221
407
  emptyText : {
222
408
  fontSize : 16,
223
409
  fontWeight : 'bold',
@@ -170,20 +170,21 @@ const DatagridFactory = (Factory)=>{
170
170
  if(selectableMultiple && max && defaultBool(this.props.selectableMultiple,true)){
171
171
  max = max.formatNumber();
172
172
  restItems = [
173
- {
174
- text : "Sélect "+max,
173
+ ...this.renderCustomMenu(),
174
+ ...(selectableMultiple ? [{
175
+ label : "Sélect "+max,
175
176
  icon : "select-all",
176
177
  onPress : (x,event)=>{
177
178
  this.handleAllRowsToggle(true);
178
179
  }
179
180
  },
180
181
  {
181
- text : "Tout désélec",
182
+ label : "Tout désélec",
182
183
  onPress : (x,event)=>{
183
184
  this.handleAllRowsToggle(false);
184
185
  },
185
186
  icon : "select"
186
- }
187
+ }] : [])
187
188
  ]
188
189
  }
189
190
  const rPagination = showPagination ? <View style={[styles.paginationContainer]}>
@@ -192,6 +193,7 @@ const DatagridFactory = (Factory)=>{
192
193
  <View testID={testID+"_HeaderQueryLimit"}>
193
194
  {this.renderQueryLimit(this.state.data.length.formatNumber())}
194
195
  </View>
196
+ {this.renderCustomPagination()}
195
197
  {!isMobile && <>
196
198
  <Button normal style={[styles.paginationItem]} icon = {"refresh"} onPress = {this.refresh.bind(this)}>
197
199
  Rafraichir
@@ -214,19 +216,15 @@ const DatagridFactory = (Factory)=>{
214
216
  >
215
217
  {showFooters?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux'}
216
218
  </Button>:null}
217
- {selectableMultiple && (<>
218
- {restItems.map((item,index)=>{
219
- return <Button
220
- normal
221
- style={styles.paginationItem}
222
- key = {index}
223
- icon = {item.icon}
224
- onPress = {item.onPress}
225
- >
226
- {item.text}
227
- </Button>
228
- })}
229
- </>)}
219
+ {restItems.map((item,index)=>{
220
+ return <Button
221
+ normal
222
+ key = {index}
223
+ {...item}
224
+ style={[styles.paginationItem,item.style]}
225
+ children = {item.children|| item.label}
226
+ />
227
+ })}
230
228
  </>}
231
229
  {exportable && (
232
230
  <>{/**
@@ -320,6 +318,7 @@ const DatagridFactory = (Factory)=>{
320
318
  </View>
321
319
  <Table
322
320
  ref = {this.listRef}
321
+ {...rest}
323
322
  hasFooters = {hasFooterFields}
324
323
  showFilters = {showFilters}
325
324
  showFooters = {showFooters}
@@ -11,15 +11,16 @@ import {defaultStr,isPromise,defaultObj} from "$utils";
11
11
  import {open as openPreloader,close as closePreloader} from "$preloader";
12
12
  import {styles as _styles} from "$theme";
13
13
  import Tooltip from "$ecomponents/Tooltip";
14
+ import {navigateToTableData} from "$enavigation/utils";
14
15
 
15
16
  const TableLinKComponent = React.forwardRef((props,ref)=>{
16
- let {disabled,labelProps,server,containerProps,id,columnDef,tableName,table:customTable,data,testID,Component,routeName,routeParams,component,primary,triggerProps,onPress,children, ...rest} = props;
17
+ let {disabled,labelProps,server,containerProps,id,fetchForeignData,foreignKeyTable,foreignKeyColumn,data,testID,Component,routeName,routeParams,component,primary,triggerProps,onPress,children, ...rest} = props;
17
18
  testID = defaultStr(testID,"RN_TableDataLinkContainer")
18
- tableName = defaultStr(tableName,customTable).trim();
19
+ foreignKeyTable = defaultStr(foreignKeyTable).trim();
20
+ foreignKeyColumn = defaultStr(foreignKeyColumn).trim();
19
21
  rest = defaultObj(rest);
20
22
  containerProps = defaultObj(containerProps)
21
23
  labelProps = defaultObj(labelProps);
22
- columnDef = defaultObj(columnDef);
23
24
  data = defaultObj(data);
24
25
  id = defaultStr(id);
25
26
  if(!id){
@@ -28,10 +29,25 @@ const TableLinKComponent = React.forwardRef((props,ref)=>{
28
29
  const pointerEvents = disabled || !id? 'none' : 'auto';
29
30
  const onPressLink = (event)=>{
30
31
  React.stopEventPropagation(event);
31
- const r = typeof onPress =='function'? onPress({...React.getOnPressArgs(event),...columnDef,tableName,table:tableName,data,id,value:id,}) : undefined;
32
+ const args = {...React.getOnPressArgs(event),...rest,foreignKeyTable,foreignKeyColumn,data,id,value:id};
33
+ let r = typeof onPress =='function'? onPress(args) : undefined;
34
+ if(r === false) return;
35
+ const cb = (a)=>{
36
+ const r2 = typeof fetchForeignData === 'function'? fetchForeignData({...args,...defaultObj(a)}) : undefined;
37
+ if(isPromise(r2)){
38
+ return r2.then((data)=>{
39
+ if(isObj(data) && data[foreignKeyColumn] !== undefined){
40
+ navigateToTableData({tableName:foreignKeyTable,data});
41
+ }
42
+ });
43
+ }
44
+ return Promise.reject({msg:'type de données retournée par la fonction fetchForeignKeyData invalide'});
45
+ }
46
+ openPreloader("traitement de la requête...");
32
47
  if(isPromise(r)){
33
- openPreloader("traitement de la requête...");
34
- r.finally(closePreloader);
48
+ r.then(cb).finally(closePreloader);
49
+ } else {
50
+ cb().finally(closePreloader);
35
51
  }
36
52
  }
37
53
  rest.style = [rest.style,_styles.cursorPointer];
@@ -47,6 +63,9 @@ export default TableLinKComponent;
47
63
 
48
64
 
49
65
  TableLinKComponent.propTypes = {
66
+ foreignKeyColumn : PropTypes.string.isRequired,//le nom de la colonne de la clé étrangère
67
+ foreignKeyTable : PropTypes.string.isRequired, //le nom de la table référencée
68
+ fetchForeignData : PropTypes.func, // la fonction permettant de chercher la données à distance
50
69
  server : PropTypes.string,//le serveur sur lequel rechercher les données
51
70
  primary : PropTypes.bool,//si le composant sera stylé en theme primary de react
52
71
  ///les props à utiliser pour afficher la table de données en cas de click sur le lien
@@ -13,10 +13,8 @@ import cActions from "$cactions";
13
13
  import {View} from "react-native";
14
14
 
15
15
  export default function DatabaseStatisticContainer (props){
16
- const [state,setState] = React.useState({
17
- isLoading : true,
18
- count : 0,
19
- });
16
+ const [count,setCount] = React.useState(0);
17
+ const [isLoading,setIsLoading] = React.useState(true);
20
18
  let {table,fetchCount,index,testID,title,icon,onPress} = props;
21
19
  title = defaultStr(title)
22
20
  table = defaultObj(table);
@@ -28,14 +26,14 @@ export default function DatabaseStatisticContainer (props){
28
26
  const refresh = ()=>{
29
27
  if(refreshingRef.current || !isMounted()) return;
30
28
  refreshingRef.current = true;
29
+ setIsLoading(true);
31
30
  setTimeout(()=>{
32
31
  fetchCount().then((count)=>{
33
- setState({...state,isLoading:false,count});
32
+ setCount(count);
33
+ setIsLoading(false);
34
34
  refreshingRef.current = false;
35
35
  }).catch((e)=>{
36
- setState({
37
- isLoading : false, count : 0,
38
- });
36
+ setIsLoading(false);
39
37
  refreshingRef.current = false;
40
38
  });
41
39
  },100);
@@ -50,10 +48,6 @@ export default function DatabaseStatisticContainer (props){
50
48
  APP.off(cActions.remove(tableName),refresh);
51
49
  }
52
50
  },[]);
53
- React.useEffect(()=>{
54
- refresh();
55
- },[props])
56
- const {isLoading,count} = state;
57
51
  return <Item
58
52
  testID = {defaultStr(testID,"RN_DatabaseStatistic_"+table)}
59
53
  onPress = {(args)=>{
@@ -65,7 +59,6 @@ export default function DatabaseStatisticContainer (props){
65
59
  left = {(aProps)=>{
66
60
  return <Avatar suffix={index} {...aProps} icon= {icon} size={40} label={title}/>
67
61
  }}
68
- //right = {(rP)=><Icon {...rP} name='refresh' onPress={refresh}/>}
69
62
  title = {<Label splitText numberOfLines={1} primary style={[{fontSize:15}]}>{title}</Label>}
70
63
  titleProps = {{primary : true}}
71
64
  description = {isLoading?<View style={[theme.styles.justifyContentFlexStart,theme.styles.alignItemsFlexStart]}>
@@ -29,6 +29,43 @@ const DEFAULT_TABS_KEYS = "main-tabs";
29
29
 
30
30
  const TIMEOUT = 50;
31
31
 
32
+ const checkPrimary = (data,f)=>{
33
+ return !(!(f in data) || (data[f] == null) || (!data[f] && typeof data !=='number'));
34
+ }
35
+ /*** vérifie si le document passé en paramètre est éditable
36
+ * @param {object} data la données à vérifier
37
+ * @param {object| array} les champs sur lesquels se baser pour vérifier si la donénes est une mise à jour
38
+ * @param {func} checkPrimaryKey la foncition permettant de vérifier s'il s'agit d'une clé primaire pour la données courante
39
+ */
40
+ export const isDocEditing = (data,fields,checkPrimaryKey)=>{
41
+ if(!isObj(data) || !isObjOrArray(fields)) return false;
42
+
43
+ let hasPrimaryFields = false;
44
+ let hasValidated = true;
45
+ for(let i in fields){
46
+ const field = fields[i];
47
+ if(typeof checkPrimaryKey =='function') {
48
+ hasPrimaryFields = true;
49
+ if(checkPrimaryKey({field,i,index:i,data}) === false){
50
+ return false;
51
+ }
52
+ continue;
53
+ }
54
+ if(!isObj(field)) continue;
55
+ hasPrimaryFields = true;
56
+ const f = defaultStr(field.field,i);
57
+ if(field.primaryKey === true){
58
+ if(!checkPrimary(data,f)){
59
+ hasValidated = false;
60
+ }
61
+ }
62
+ }
63
+ if(hasPrimaryFields){
64
+ return hasValidated;
65
+ }
66
+ return false;
67
+ }
68
+
32
69
  export default class TableDataScreenComponent extends FormDataScreen{
33
70
  constructor(props){
34
71
  super(props);
@@ -49,7 +86,7 @@ export default class TableDataScreenComponent extends FormDataScreen{
49
86
  Object.map(table.fields,(field,i)=>{
50
87
  if(isObj(field) && field.form !== false){
51
88
  fields[i] = Object.clone(field);
52
- if(field.primary === true){
89
+ if(field.primaryKey === true){
53
90
  primaryKeyFields[field.field || i] = true;
54
91
  }
55
92
  } else {
@@ -420,17 +457,9 @@ export default class TableDataScreenComponent extends FormDataScreen{
420
457
  isDocEditing(data){
421
458
  data = defaultObj(data);
422
459
  if(!this.isDocEditingProp){
423
- let hasPrimaryFields = false;
424
- let hasValidated = true;
425
- Object.map(this.primaryKeyFields,(v,f)=>{
426
- hasPrimaryFields = true;
427
- if(!(f in data) || (data[f] == null) || (!data[f] && typeof data !=='number')){
428
- hasValidated = false;
429
- }
430
- });
431
- if(hasPrimaryFields){
432
- return hasValidated;
433
- }
460
+ if(isDocEditing(data,this.primaryKeyFields,({index:field,data})=>{
461
+ return checkPrimary(data,field);
462
+ })) return true;
434
463
  }
435
464
  return super.isDocEditing(data);
436
465
  }