@fto-consult/expo-ui 2.27.1 → 2.28.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 +2 -2
- package/src/components/BottomSheet/Sheet.js +6 -13
- package/src/components/Datagrid/Common/Common.js +1 -1
- package/src/components/Date/DatePickerInput/index.js +1 -0
- package/src/components/Filter/BetweenComponent.js +24 -0
- package/src/components/Filter/index.js +96 -36
- package/src/components/Form/Form.js +1 -1
- package/src/components/Form/index.js +0 -3
- /package/src/components/{Form/KeyboardAvoidingView/index.native.js → KeyboardAvoidingView/index.js} +0 -0
- /package/src/components/{Form/KeyboardAvoidingView/index.web.js → KeyboardAvoidingView/index.web.old.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fto-consult/expo-ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.28.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.
|
|
64
|
+
"@fto-consult/common": "^1.25.1",
|
|
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",
|
|
@@ -10,7 +10,7 @@ import {isWeb,isNativeMobile} from "$cplatform";
|
|
|
10
10
|
import Divider from "$ecomponents/Divider";
|
|
11
11
|
import {isMobileOrTabletMedia} from "$cplatform/dimensions";
|
|
12
12
|
import APP from "$capp/instance";
|
|
13
|
-
import KeyboardAvoidingView from "$ecomponents/
|
|
13
|
+
import KeyboardAvoidingView from "$ecomponents/KeyboardAvoidingView";
|
|
14
14
|
import {Elevations} from "$ecomponents/Surface";
|
|
15
15
|
import {defaultStr} from "$utils";
|
|
16
16
|
import View from "$ecomponents/View";
|
|
@@ -258,11 +258,6 @@ const BottomSheetComponent = React.forwardRef((props,ref)=> {
|
|
|
258
258
|
activeOpacity={1}
|
|
259
259
|
onPress={() => (closeOnPressMask && dismissable !== false ? closeModal() : null)}
|
|
260
260
|
/>
|
|
261
|
-
<KeyboardAvoidingView
|
|
262
|
-
behavior="padding"
|
|
263
|
-
style={[styles.wrapper]}
|
|
264
|
-
testID = {testID+"_KeyboardAvoidingView"}
|
|
265
|
-
>
|
|
266
261
|
<Animated.View
|
|
267
262
|
{...(!dragFromTopOnly && panResponder.panHandlers)}
|
|
268
263
|
testID = {testID+"_Container"} {...containerProps}
|
|
@@ -289,19 +284,17 @@ const BottomSheetComponent = React.forwardRef((props,ref)=> {
|
|
|
289
284
|
</View>
|
|
290
285
|
<Divider testID = {testID+"_Divider"} style={styles.divider}/>
|
|
291
286
|
</> : null}
|
|
292
|
-
{withScrollView !== false ?
|
|
287
|
+
{withScrollView !== false ?
|
|
293
288
|
<ScrollView testID = {testID+"_ScrollViewContent"} contentProps = {{style:styles.scrollViewContent}} {...scrollViewProps} style={[styles.scrollView,scrollViewProps.style]} alwaysBounceVertical={false}
|
|
294
|
-
|
|
289
|
+
contentContainerStyle={[{ flexGrow: 1,margin:0,paddingBottom:30},scrollViewProps.contentContainerStyle]}
|
|
295
290
|
>
|
|
296
|
-
{children}
|
|
297
|
-
|
|
298
|
-
</>
|
|
291
|
+
<KeyboardAvoidingView testID={testID+"_KeyboardAvoidingView"}>{children}</KeyboardAvoidingView>
|
|
292
|
+
</ScrollView>
|
|
299
293
|
: <View testID = {testID+"_Children"} {...childrenContainerProps} style={[styles.childrenNotScroll,childrenContainerProps.style]}>
|
|
300
|
-
{children}
|
|
294
|
+
<KeyboardAvoidingView testID={testID+"_KeyboardAvoidingView"}>{children}</KeyboardAvoidingView>
|
|
301
295
|
</View>}
|
|
302
296
|
</View>
|
|
303
297
|
</Animated.View>
|
|
304
|
-
</KeyboardAvoidingView>
|
|
305
298
|
</View>
|
|
306
299
|
</Portal>
|
|
307
300
|
);
|
|
@@ -28,7 +28,7 @@ import { evalSingleValue,Footer,getFooterColumnValue,isValidAggregator,extendAgg
|
|
|
28
28
|
import i18n from "$i18n";
|
|
29
29
|
import { makePhoneCall,canMakePhoneCall as canMakeCall} from "$makePhoneCall";
|
|
30
30
|
import copyToClipboard from "$capp/clipboard";
|
|
31
|
-
import { Pressable
|
|
31
|
+
import { Pressable } from "react-native";
|
|
32
32
|
import TableLink from "$TableLink";
|
|
33
33
|
import appConfig from "$capp/config";
|
|
34
34
|
import stableHash from "stable-hash";
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
import React from "$react";
|
|
5
|
+
import TextField from "$ecomponents/Form/Fields/TextField";
|
|
6
|
+
import {defaultObj} from "$utils";
|
|
7
|
+
|
|
8
|
+
const FilterBetweenComponent = React.forwardRef(({inputProps,contentContainerProps,...props},ref)=>{
|
|
9
|
+
contentContainerProps = defaultObj(contentContainerProps);
|
|
10
|
+
contentContainerProps.pointerEvents = "auto";
|
|
11
|
+
return <TextField
|
|
12
|
+
ref = {ref}
|
|
13
|
+
{...props}
|
|
14
|
+
contentContainerProps={contentContainerProps}
|
|
15
|
+
inputProps = {{...defaultObj(inputProps),pointerEvents:"none",editable:false}}
|
|
16
|
+
type ="text"
|
|
17
|
+
format = {undefined}
|
|
18
|
+
onChange = {(e)=>false}
|
|
19
|
+
onValidate = {(e)=>false}
|
|
20
|
+
/>
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
FilterBetweenComponent.displayName = "FilterBetweenComponent";
|
|
24
|
+
export default FilterBetweenComponent;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
3
|
import DateLib from "$lib/date";
|
|
4
|
-
import {isNonNullString,defaultStr,isNullOrEmpty,debounce,uniqid} from "$utils";
|
|
5
|
-
import {regexParser,regexActions,operators as _operators,actions as _actions,periodActions, inActions as _inActions,getFilterStateValues,getSessionData,setSessionData} from "$cfilters";
|
|
4
|
+
import {isNonNullString,defaultStr,isNullOrEmpty,debounce,isFunction,uniqid} from "$utils";
|
|
5
|
+
import {regexParser,regexActions,operators as _operators,actions as _actions,periodActions,betweenActions, inActions as _inActions,getFilterStateValues,getSessionData,setSessionData} from "$cfilters";
|
|
6
6
|
import {parseDecimal} from "$ecomponents/TextField";
|
|
7
7
|
import notify from "$notify";
|
|
8
8
|
import PropTypes from "prop-types";
|
|
@@ -16,6 +16,7 @@ import theme from "$theme";
|
|
|
16
16
|
import {isMobileMedia} from "$cplatform/dimensions";
|
|
17
17
|
import { ActivityIndicator } from "react-native-paper";
|
|
18
18
|
import DialogProvider from "$components/Form/FormData/DialogProvider";
|
|
19
|
+
import FilterBetweenComponent from "./BetweenComponent";
|
|
19
20
|
|
|
20
21
|
const manualRunKey = "manual-run";
|
|
21
22
|
|
|
@@ -129,10 +130,19 @@ export default class Filter extends AppComponent {
|
|
|
129
130
|
this.fireFilterSearch(false);
|
|
130
131
|
});
|
|
131
132
|
}
|
|
133
|
+
getDefaultAction(type){
|
|
134
|
+
type = defaultStr(type,this.type,this.props.type).toLowerCase();
|
|
135
|
+
if(type.contains('select')){
|
|
136
|
+
return "$in";
|
|
137
|
+
} if(type !== 'date2time' && type !== 'time' && type !== 'number' && type !== 'decimal'){
|
|
138
|
+
return '$regexcontains';
|
|
139
|
+
}
|
|
140
|
+
return '$eq';
|
|
141
|
+
}
|
|
132
142
|
initFiltersOp(type){
|
|
133
143
|
type = defaultStr(type,this.type).toLowerCase();
|
|
134
144
|
let operators = {..._operators};
|
|
135
|
-
let actions = {..._actions};
|
|
145
|
+
let actions = {...betweenActions,..._actions};
|
|
136
146
|
if(this.props.orOperator === false){
|
|
137
147
|
delete operators.$or;
|
|
138
148
|
}
|
|
@@ -144,21 +154,18 @@ export default class Filter extends AppComponent {
|
|
|
144
154
|
let ignoreCase = this.props.ignoreCase;
|
|
145
155
|
ignoreCase = defaultVal(ignoreCase,true);
|
|
146
156
|
let isTextFilter = false;
|
|
147
|
-
let defaultAct = '$eq';
|
|
148
157
|
if(type =="checkbox" || type == 'switch'){
|
|
149
158
|
action = '$eq';
|
|
150
|
-
} else if(type
|
|
159
|
+
} else if(type.contains('select')){
|
|
151
160
|
actions = _inActions;
|
|
152
|
-
defaultAct = "$in";
|
|
153
161
|
} else if(type == 'date' || type =='datetime') {
|
|
154
162
|
actions = {...periodActions, ...actions}
|
|
155
163
|
} else if(type !== 'date2time' && type !== 'time' && type !== 'number' && type !== 'decimal'){
|
|
156
|
-
actions = regexActions;
|
|
157
|
-
defaultAct = '$regexcontains';
|
|
164
|
+
actions = {...betweenActions,...regexActions};
|
|
158
165
|
isTextFilter = true;
|
|
159
166
|
}
|
|
160
167
|
if(!action){
|
|
161
|
-
action =
|
|
168
|
+
action = this.getDefaultAction(type);
|
|
162
169
|
}
|
|
163
170
|
operator = defaultVal(operator,"$and");
|
|
164
171
|
return {actions,action,ignoreCase,operator,operators,manualRun:defaultBool(this.props.manualRun,false),defaultValue:defaultVal(this.props.defaultValue),isTextFilter};
|
|
@@ -248,26 +255,44 @@ export default class Filter extends AppComponent {
|
|
|
248
255
|
const t = defaultStr(this.type,this.props.type);
|
|
249
256
|
return t.contains("date") && t.contains("time");
|
|
250
257
|
}
|
|
251
|
-
|
|
258
|
+
showBetweenSelector(args){
|
|
259
|
+
return this.showBetweenActionSelector(args);
|
|
260
|
+
}
|
|
261
|
+
showPeriodSelector(args){
|
|
262
|
+
return this.showBetweenActionSelector(args);
|
|
263
|
+
}
|
|
264
|
+
showBetweenActionSelector(args){
|
|
265
|
+
if(typeof args =='function'){
|
|
266
|
+
args = {success:args};
|
|
267
|
+
}
|
|
268
|
+
let {success,callback} = defaultObj(args);
|
|
269
|
+
const _type = defaultStr(this.type,this.props.type).toLowerCase().trim();
|
|
270
|
+
success = typeof success =='function'? success : typeof callback =='function'? callback : undefined;
|
|
252
271
|
const defaultValue = defaultStr(this.state.defaultValue).trim();
|
|
253
272
|
let split = defaultValue.split("=>");
|
|
254
|
-
|
|
255
|
-
let start =
|
|
256
|
-
|
|
257
|
-
if(
|
|
258
|
-
start =
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
273
|
+
const isDateTime = this.isDateTime();
|
|
274
|
+
let start = split[0] && split[0] || undefined, end = split[1] && split[1] || undefined;
|
|
275
|
+
const willHandleDate = _type.contains('date');
|
|
276
|
+
if(willHandleDate){
|
|
277
|
+
start = isDateTime ? new Date().toSQLDateTimeFormat() : new Date().toSQLDateFormat();
|
|
278
|
+
end = start;
|
|
279
|
+
if(DateLib.isValidSQLDateTime(split[0]) || DateLib.isValidSQLDate(split[0])){
|
|
280
|
+
start = split[0];
|
|
281
|
+
}
|
|
282
|
+
if(DateLib.isValidSQLDateTime(split[1]) || DateLib.isValidSQLDate(split[1])){
|
|
283
|
+
end = split[1];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const type = willHandleDate ? (isDateTime? "datetime" : "date"):_type;
|
|
287
|
+
const format = this.props.format;
|
|
288
|
+
return new Promise((resolve,reject)=>{
|
|
289
|
+
DialogProvider.open({
|
|
265
290
|
subtitle : false,
|
|
266
291
|
fields : {
|
|
267
|
-
start : {type,text:'Du',defaultValue:start},
|
|
268
|
-
end : {type,text:'Au',defaultValue:end}
|
|
292
|
+
start : {format,type,text:(willHandleDate?'Du':"Valeur inférieure"),defaultValue:start},
|
|
293
|
+
end : {format,type,text:willHandleDate?'Au':"Valeur supérieure",defaultValue:end}
|
|
269
294
|
},
|
|
270
|
-
title :"Définir une période
|
|
295
|
+
title :"Définir {0} [{1}]".sprintf(willHandleDate?"une période":"Un intervalle",defaultStr(this.props.label,this.props.text)),
|
|
271
296
|
cancelButton : true,
|
|
272
297
|
actions : {
|
|
273
298
|
yes : {
|
|
@@ -276,19 +301,22 @@ export default class Filter extends AppComponent {
|
|
|
276
301
|
},
|
|
277
302
|
},
|
|
278
303
|
onSuccess : ({data})=>{
|
|
279
|
-
if(data.start && data.end && data.start> data.end){
|
|
280
|
-
return notify.error("La
|
|
304
|
+
if(data.start !== undefined && data.start !== null && data.end !== undefined && data.end !== null && data.start> data.end){
|
|
305
|
+
return notify.error("La {0} doit être supérieure à la {1}".sprintf(willHandleDate?"date de fin":"valeur supérieure",willHandleDate?"date de début":"valeur inférieure"));
|
|
281
306
|
}
|
|
282
|
-
if(
|
|
307
|
+
if(typeof(success) =="function"){
|
|
283
308
|
success(data.start+"=>"+data.end);
|
|
284
309
|
}
|
|
310
|
+
resolve(data.start+"=>"+data.end);
|
|
285
311
|
DialogProvider.close();
|
|
286
312
|
return true;
|
|
287
|
-
}
|
|
313
|
+
}
|
|
314
|
+
})
|
|
315
|
+
})
|
|
288
316
|
}
|
|
289
317
|
setAction(action,text){
|
|
290
318
|
if(!(this.searchFilter.current)) return;
|
|
291
|
-
if(action === this.state.action && action
|
|
319
|
+
if(action === this.state.action && !periodActions[action] && !betweenActions[action]) return;
|
|
292
320
|
let value = this.state.defaultValue;
|
|
293
321
|
let act = defaultStr(action).toLowerCase();
|
|
294
322
|
const isDateTime = this.type?.contains("time");
|
|
@@ -297,6 +325,10 @@ export default class Filter extends AppComponent {
|
|
|
297
325
|
this.showPeriodSelector((d)=>{
|
|
298
326
|
this.runAction({value:d,action});
|
|
299
327
|
})
|
|
328
|
+
} else if(action == '$between'){
|
|
329
|
+
this.showBetweenSelector((d)=>{
|
|
330
|
+
this.runAction({value:d,action});
|
|
331
|
+
})
|
|
300
332
|
} else if(action =="$today"){
|
|
301
333
|
return this.runAction({value:new Date().resetHours().resetMinutes().resetSeconds().toFormat(dateFormat),action})
|
|
302
334
|
} else if(action =="$yesterday"){
|
|
@@ -346,7 +378,7 @@ export default class Filter extends AppComponent {
|
|
|
346
378
|
})
|
|
347
379
|
}
|
|
348
380
|
clearFilter(event){
|
|
349
|
-
this.setState({defaultValue:undefined},()=>{
|
|
381
|
+
this.setState({defaultValue:undefined,action:this.getDefaultAction()},()=>{
|
|
350
382
|
this.callOnValidate();
|
|
351
383
|
this.fireValueChanged(true);
|
|
352
384
|
let {onClearFilter,onResetFilter} = this.props;
|
|
@@ -356,6 +388,33 @@ export default class Filter extends AppComponent {
|
|
|
356
388
|
}
|
|
357
389
|
})
|
|
358
390
|
}
|
|
391
|
+
isBetweenAction(action){
|
|
392
|
+
action = defaultStr(action,this.state.action).toLowerCase();
|
|
393
|
+
return !!(this.state.actions && betweenActions[action]);
|
|
394
|
+
}
|
|
395
|
+
isPeriodAction(action){
|
|
396
|
+
action = defaultStr(action,this.state.action).toLowerCase();
|
|
397
|
+
return !!(this.state.actions && periodActions[action]);
|
|
398
|
+
}
|
|
399
|
+
formatValue(value){
|
|
400
|
+
const type = defaultStr(this.type,this.props.type).toLowerCase();
|
|
401
|
+
if(this.isBetweenAction() && isNonNullString(value) && (type ==="number" || type =="decimal")){
|
|
402
|
+
const sp = value.split("=>");
|
|
403
|
+
if(sp.length ==2 && type){
|
|
404
|
+
const format = defaultStr(this.props.format).toLowerCase();
|
|
405
|
+
const v1 = defaultDecimal(parseDecimal(sp[0],type));
|
|
406
|
+
const v2 = defaultDecimal(parseDecimal(sp[1],type));
|
|
407
|
+
return format =="money"? (v1.formatMoney()+"=>"+v2.formatMoney()) : (v1.formatNumber()+"=>"+v2.formatNumber());
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if(typeof value =='number'){
|
|
411
|
+
if(this.props.format =='money'){
|
|
412
|
+
return value.formatMoney();
|
|
413
|
+
}
|
|
414
|
+
return value.formatNumber();
|
|
415
|
+
}
|
|
416
|
+
return value;
|
|
417
|
+
}
|
|
359
418
|
render (){
|
|
360
419
|
let {
|
|
361
420
|
filter,
|
|
@@ -442,8 +501,9 @@ export default class Filter extends AppComponent {
|
|
|
442
501
|
if(isFunction(filter)){
|
|
443
502
|
rest.filter = filter;
|
|
444
503
|
}
|
|
445
|
-
const isPeriodAction = this.
|
|
446
|
-
const
|
|
504
|
+
const isPeriodAction = this.isPeriodAction();
|
|
505
|
+
const isBetweenAction = this.isBetweenAction();
|
|
506
|
+
const ignoreDefaultValue = (isPeriodAction||isBetweenAction) && isNonNullString(defaultValue) && defaultValue.contains("=>");
|
|
447
507
|
rest.defaultValue = defaultValue;
|
|
448
508
|
rest.disabled = rest.readOnly = rest.affix = false;
|
|
449
509
|
rest.editable = true;
|
|
@@ -500,7 +560,7 @@ export default class Filter extends AppComponent {
|
|
|
500
560
|
style : [styles.bold,styles.noVerticalPadding],
|
|
501
561
|
}:null],
|
|
502
562
|
...Object.mapToArray(actions,(x,j)=>{
|
|
503
|
-
if(ignoreDefaultValue && !periodActions[j]){
|
|
563
|
+
if(ignoreDefaultValue && !periodActions[j] && !betweenActions[j]){
|
|
504
564
|
return null;
|
|
505
565
|
}
|
|
506
566
|
let checked = j === action?true : false;
|
|
@@ -523,7 +583,7 @@ export default class Filter extends AppComponent {
|
|
|
523
583
|
}
|
|
524
584
|
|
|
525
585
|
if(!hasS){
|
|
526
|
-
x = x+" <"+defaultValue+">"
|
|
586
|
+
x = x+" <"+this.formatValue(defaultValue)+">"
|
|
527
587
|
}
|
|
528
588
|
}
|
|
529
589
|
return {
|
|
@@ -539,10 +599,10 @@ export default class Filter extends AppComponent {
|
|
|
539
599
|
const containerProps = defaultObj(this.props.containerProps,rest.containerProps);
|
|
540
600
|
delete rest.containerProps;
|
|
541
601
|
rest.onValidate = this.onFilterValidate.bind(this);
|
|
542
|
-
const Component = this.Component;
|
|
602
|
+
const Component = isBetweenAction ? FilterBetweenComponent : this.Component;
|
|
543
603
|
const responsiveProps = Object.assign({},responsiveProps);
|
|
544
604
|
responsiveProps.style = [theme.styles.w100,responsiveProps.style]
|
|
545
|
-
if(ignoreDefaultValue) {
|
|
605
|
+
if(ignoreDefaultValue && isPeriodAction) {
|
|
546
606
|
rest.isPeriodAction = true;
|
|
547
607
|
}
|
|
548
608
|
return <View testID={testID+"_FilterContainer"} {...containerProps} style={StyleSheet.flatten([theme.styles.w100,containerProps.style])}>
|
|
@@ -4,7 +4,7 @@ import {classExtends,isFunction,isNonNullString,defaultStr,defaultObj,defaultVal
|
|
|
4
4
|
import {warning,Forms} from "./utils";
|
|
5
5
|
import Action from "./Action";
|
|
6
6
|
import View from "$ecomponents/View";
|
|
7
|
-
import KeyboardAvoidingView from "
|
|
7
|
+
import KeyboardAvoidingView from "$ecomponents/KeyboardAvoidingView";
|
|
8
8
|
import { StyleSheet } from "react-native";
|
|
9
9
|
import APP from "$capp/instance";
|
|
10
10
|
import PropTypes from "prop-types";
|
/package/src/components/{Form/KeyboardAvoidingView/index.native.js → KeyboardAvoidingView/index.js}
RENAMED
|
File without changes
|
|
File without changes
|