@fto-consult/expo-ui 1.2.3 → 1.2.5

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.
@@ -17,8 +17,15 @@ module.exports = (opts)=>{
17
17
  r["$emedia"] = path.resolve(expo,"media");
18
18
  r["$enavigation"] = path.resolve(expo,"navigation");
19
19
  r["$escreens"] = path.resolve(expo,"screens");
20
+ r["$emainScreens"] = path.resolve(expo,"screens","mainScreens");
21
+ ///les screens principaux de l'application
22
+ r["$mainScreens"] = r["$mainScreens"] || r["$emainScreens"];
20
23
  r["$escreen"] = path.resolve(expo,"layouts/Screen");
21
24
  r["$eassets"] = path.resolve(dir,"assets");
25
+ r["$ethemeSelectorComponent"] = path.resolve(expo,"auth","ThemeSelector");
26
+ /*** le composant permettant de sélectionner un theme utilisateur */
27
+ r["$themeSelectorComponent"] = r["$themeSelectorComponent"] || r["$ethemeSelectorComponent"];
28
+
22
29
 
23
30
  ///pour personnaliser les écrans de l'application, il suffit de redefinir l'alis $screens, pointant vers le repertoire racine des écrans personnalisés
24
31
  ///cependant, ce repertoire doit contenir un fichier mainScreens.js qui contient la liste de tous les écrans de lapplicaiton
package/package.json CHANGED
@@ -1,101 +1,102 @@
1
- {
2
- "name": "@fto-consult/expo-ui",
3
- "version": "1.2.3",
4
- "description": "Bibliothèque de composants UI Expo,react-native",
5
- "main": "index.js",
6
- "scripts": {
7
- "publish1": "npm publish --access=public",
8
- "unpublish": "npm -f unpublish @fto-consult/expo-ui",
9
- "build-web": "",
10
- "dev": "npx expo start -c --no-minify",
11
- "start": "npx expo start --dev --no-minify",
12
- "start-d": "npx expo start -c --no-dev --no-minify",
13
- "start-p": "npm run start-d",
14
- "expo-start-client": "npx expo start --dev --no-minify --dev-client",
15
- "start-m": "npx expo start - c--dev--no -minify",
16
- "android": "npx expo start --android -c",
17
- "ios": "npx expo start --ios",
18
- "web": "npx expo start --web",
19
- "web-c": "npx expo start --web -c",
20
- "eject": "expo eject",
21
- "emulator": "npm run android-emulator",
22
- "web-server": "npx serve web-build",
23
- "build-android": "eas build --clear-cache -p android --profile preview",
24
- "build-android-local": "eas build --platform android --local",
25
- "build-android-dist": "eas build --clear-cache -p android",
26
- "build-ios": "eas build --clear-cache -p ios --profile preview",
27
- "build-ios-dist": "eas build --clear-cache -p ios",
28
- "install-apk": "adb -s emulator-5554 install myapp.apk",
29
- "android-emulator": "emulator -avd EMULATOR",
30
- "flipper": "npx cross-env METRO_SERVER_PORT=19000 E:\\Studies\\react-native\\debugger\\Flipper-win\\Flipper.exe",
31
- "build-electron": "electron-webpack && electron-builder --dir -c.compression=store",
32
- "test:build": "electron-webpack && electron-builder --dir -c.compression=store -c.mac.identity=null",
33
- "compile-electron": "webpack --config ./electron/webpack.config.js",
34
- "compile-electron-p": "webpack --config ./electron/webpack.config.js --mode=production",
35
- "electron": "electron ./electron",
36
- "logcat": "adb -d logcat com.ftc.apps.salite1:E > errors.txt",
37
- "logcat-export": "adb -d logcat com.ftc.apps.salite1 *:S > logcat.txt",
38
- "electron-c": "npm run compile-electron && npm run electron",
39
- "electron-p": "npm run compile-electron-p && npm run electron",
40
- "update-app-version": "node ./update-app-version.js",
41
- "find-licenses": "node ./find-licenses.js",
42
- "fix-dependencies": "expo doctor --fix-dependencies"
43
- },
44
- "repository": {
45
- "type": "git",
46
- "url": "git+https://github.com/borispipo/expo-ui.git"
47
- },
48
- "keywords": [
49
- "Expo",
50
- "React-Native"
51
- ],
52
- "author": "Boris Fouomene",
53
- "license": "ISC",
54
- "bugs": {
55
- "url": "https://github.com/borispipo/expo-ui/issues"
56
- },
57
- "homepage": "https://github.com/borispipo/expo-ui#readme",
58
- "dependencies": {
59
- "@babel/preset-react": "^7.18.6",
60
- "@emotion/native": "^11.10.0",
61
- "@expo/html-elements": "^0.2.0",
62
- "@expo/metro-config": "^0.4.0",
63
- "@expo/webpack-config": "^0.17.2",
64
- "@fto-consult/common": "^1.3.3",
65
- "@gorhom/portal": "^1.0.14",
66
- "@react-native-async-storage/async-storage": "~1.17.3",
67
- "@react-native-community/datetimepicker": "6.2.0",
68
- "@react-native-community/netinfo": "9.3.0",
69
- "@react-navigation/native": "^6.0.13",
70
- "@react-navigation/native-stack": "^6.9.1",
71
- "@shopify/flash-list": "1.1.0",
72
- "babel-plugin-inline-dotenv": "^1.7.0",
73
- "babel-plugin-module-resolver": "^4.1.0",
74
- "babel-plugin-transform-inline-environment-variables": "^0.4.4",
75
- "expo": "^46.0.15",
76
- "expo-camera": "~12.3.0",
77
- "expo-linking": "~3.2.2",
78
- "expo-sqlite": "~10.3.0",
79
- "expo-status-bar": "~1.4.0",
80
- "expo-system-ui": "~1.3.0",
81
- "expo-web-browser": "~11.0.0",
82
- "google-libphonenumber": "^3.2.31",
83
- "htmlparser2-without-node-native": "^3.9.2",
84
- "parent-package-json": "^2.0.1",
85
- "prop-types": "^15.8.1",
86
- "react-apexcharts": "^1.4.0",
87
- "react-content-loader": "^6.2.0",
88
- "react-dom": "18.0.0",
89
- "react-native-big-list": "^1.5.5",
90
- "react-native-gesture-handler": "~2.5.0",
91
- "react-native-paper": "^4.12.5",
92
- "react-native-paper-dates": "^0.9.2",
93
- "react-native-reanimated": "~2.9.1",
94
- "react-native-safe-area-context": "4.3.1",
95
- "react-native-svg": "12.3.0",
96
- "react-native-web": "~0.18.7",
97
- "react-native-webview": "11.23.0",
98
- "sharp-cli": "^2.1.0",
99
- "tippy.js": "^6.3.7"
100
- }
101
- }
1
+ {
2
+ "name": "@fto-consult/expo-ui",
3
+ "version": "1.2.5",
4
+ "description": "Bibliothèque de composants UI Expo,react-native",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "publish1": "npm publish --access=public",
8
+ "unpublish": "npm -f unpublish @fto-consult/expo-ui",
9
+ "build-web": "",
10
+ "dev": "npx expo start -c --no-minify",
11
+ "start": "npx expo start --dev --no-minify",
12
+ "start-d": "npx expo start -c --no-dev --no-minify",
13
+ "start-p": "npm run start-d",
14
+ "expo-start-client": "npx expo start --dev --no-minify --dev-client",
15
+ "start-m": "npx expo start - c--dev--no -minify",
16
+ "android": "npx expo start --android -c",
17
+ "ios": "npx expo start --ios",
18
+ "web": "npx expo start --web",
19
+ "web-c": "npx expo start --web -c",
20
+ "eject": "expo eject",
21
+ "emulator": "npm run android-emulator",
22
+ "web-server": "npx serve web-build",
23
+ "build-android": "eas build --clear-cache -p android --profile preview",
24
+ "build-android-local": "eas build --platform android --local",
25
+ "build-android-dist": "eas build --clear-cache -p android",
26
+ "build-ios": "eas build --clear-cache -p ios --profile preview",
27
+ "build-ios-dist": "eas build --clear-cache -p ios",
28
+ "install-apk": "adb -s emulator-5554 install myapp.apk",
29
+ "android-emulator": "emulator -avd EMULATOR",
30
+ "flipper": "npx cross-env METRO_SERVER_PORT=19000 E:\\Studies\\react-native\\debugger\\Flipper-win\\Flipper.exe",
31
+ "build-electron": "electron-webpack && electron-builder --dir -c.compression=store",
32
+ "test:build": "electron-webpack && electron-builder --dir -c.compression=store -c.mac.identity=null",
33
+ "compile-electron": "webpack --config ./electron/webpack.config.js",
34
+ "compile-electron-p": "webpack --config ./electron/webpack.config.js --mode=production",
35
+ "electron": "electron ./electron",
36
+ "logcat": "adb -d logcat com.ftc.apps.salite1:E > errors.txt",
37
+ "logcat-export": "adb -d logcat com.ftc.apps.salite1 *:S > logcat.txt",
38
+ "electron-c": "npm run compile-electron && npm run electron",
39
+ "electron-p": "npm run compile-electron-p && npm run electron",
40
+ "update-app-version": "node ./update-app-version.js",
41
+ "find-licenses": "node ./find-licenses.js",
42
+ "fix-dependencies": "expo doctor --fix-dependencies"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/borispipo/expo-ui.git"
47
+ },
48
+ "keywords": [
49
+ "Expo",
50
+ "React-Native"
51
+ ],
52
+ "author": "Boris Fouomene",
53
+ "license": "ISC",
54
+ "bugs": {
55
+ "url": "https://github.com/borispipo/expo-ui/issues"
56
+ },
57
+ "homepage": "https://github.com/borispipo/expo-ui#readme",
58
+ "dependencies": {
59
+ "@babel/preset-react": "^7.18.6",
60
+ "@emotion/native": "^11.10.0",
61
+ "@expo/html-elements": "^0.2.0",
62
+ "@expo/metro-config": "^0.4.0",
63
+ "@expo/webpack-config": "^0.17.2",
64
+ "@fto-consult/common": "^1.4.3",
65
+ "@gorhom/portal": "^1.0.14",
66
+ "@react-native-async-storage/async-storage": "~1.17.3",
67
+ "@react-native-community/datetimepicker": "6.2.0",
68
+ "@react-native-community/netinfo": "9.3.0",
69
+ "@react-navigation/native": "^6.0.13",
70
+ "@react-navigation/native-stack": "^6.9.1",
71
+ "@shopify/flash-list": "1.1.0",
72
+ "babel-plugin-inline-dotenv": "^1.7.0",
73
+ "babel-plugin-module-resolver": "^4.1.0",
74
+ "babel-plugin-transform-inline-environment-variables": "^0.4.4",
75
+ "expo": "^46.0.15",
76
+ "expo-camera": "~12.3.0",
77
+ "expo-image-picker": "~13.3.1",
78
+ "expo-linking": "~3.2.2",
79
+ "expo-sqlite": "~10.3.0",
80
+ "expo-status-bar": "~1.4.0",
81
+ "expo-system-ui": "~1.3.0",
82
+ "expo-web-browser": "~11.0.0",
83
+ "google-libphonenumber": "^3.2.31",
84
+ "htmlparser2-without-node-native": "^3.9.2",
85
+ "parent-package-json": "^2.0.1",
86
+ "prop-types": "^15.8.1",
87
+ "react-apexcharts": "^1.4.0",
88
+ "react-content-loader": "^6.2.0",
89
+ "react-dom": "18.0.0",
90
+ "react-native-big-list": "^1.5.5",
91
+ "react-native-gesture-handler": "~2.5.0",
92
+ "react-native-paper": "^4.12.5",
93
+ "react-native-paper-dates": "^0.9.2",
94
+ "react-native-reanimated": "~2.9.1",
95
+ "react-native-safe-area-context": "4.3.1",
96
+ "react-native-svg": "12.3.0",
97
+ "react-native-web": "~0.18.7",
98
+ "react-native-webview": "11.23.0",
99
+ "sharp-cli": "^2.1.0",
100
+ "tippy.js": "^6.3.7"
101
+ }
102
+ }
package/src/auth/Login.js CHANGED
@@ -35,6 +35,7 @@ export default function LoginComponent(props){
35
35
  const backgroundColor = theme.colors.surface;
36
36
  const Wrapper = withPortal ? ScreenWithoutAuthContainer : View;
37
37
  const _getForm = x=> getForm(formName);
38
+ const isMounted = React.useIsMounted();
38
39
 
39
40
  const auth = useAuth();
40
41
  const notifyUser = (message,title)=> {
@@ -43,10 +44,15 @@ export default function LoginComponent(props){
43
44
  } else if(typeof message =='object') return false;
44
45
  return notify.error({message,title,position:'top'});
45
46
  }
46
- const [state,setState] = React.useState({
47
+ const [state,_setState] = React.useState({
47
48
  step : defaultNumber(step,1),
48
49
  data : defaultObj(props.data),
49
50
  });
51
+ const setState = (state2)=>{
52
+ if(!isMounted()) return;
53
+ state2 = isObj(state2)? state2 : {};
54
+ return _setState({...state,...state2});
55
+ }
50
56
  const getData = ()=>{
51
57
  const form = _getForm();
52
58
  if(form && form.getData){
@@ -79,8 +85,9 @@ export default function LoginComponent(props){
79
85
  },1000)
80
86
  }
81
87
  },[withPortal])
82
- const {header,children,data:loginData,canGoToNext,keyboardEvents,onSuccess:onLoginSuccess,canSubmit:canSubmitForm,onStepChange,...loginProps} = defaultObj(getProps({
88
+ const {header,children,initialize,data:loginData,canGoToNext,keyboardEvents,onSuccess:onLoginSuccess,canSubmit:canSubmitForm,onStepChange,...loginProps} = defaultObj(getProps({
83
89
  ...state,
90
+ setState,
84
91
  state,
85
92
  showError : notifyUser,
86
93
  notifyUser,
@@ -93,6 +100,10 @@ export default function LoginComponent(props){
93
100
  previousButtonRef,
94
101
  }));
95
102
  React.useEffect(()=>{
103
+ /*** pour initializer les cordonnées du composant login */
104
+ if(typeof initialize =='function'){
105
+ initialize();
106
+ }
96
107
  Preloader.closeAll();
97
108
  },[]);
98
109
  const prevStep = React.usePrevious(state.step);
@@ -120,11 +131,16 @@ export default function LoginComponent(props){
120
131
  }
121
132
  if(typeof canGoToNext =='function'){
122
133
  const s = canGoToNext(args);
123
- if(s === false) return;
134
+ if(s === false) {
135
+ nextButtonRef.current?.disable();
136
+ return;
137
+ }
124
138
  if(isNonNullString(s)){
125
139
  notifyUser(s);
140
+ nextButtonRef.current?.disable();
126
141
  return
127
142
  }
143
+ nextButtonRef.current?.enable();
128
144
  }
129
145
  if(step > 1){
130
146
  if(!form.isValid()){
@@ -0,0 +1,197 @@
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
+ ///permet de sélectionner un theme utilisateur
5
+ import React from "$react";
6
+ import Label from "$ecomponents/Label";
7
+ import Auth,{login} from "$cauth";
8
+ import View from "$ecomponents/View";
9
+ import { StyleSheet } from "react-native";
10
+ import {colors as defaultColors,defaultThemeName} from "$theme/defaultTheme";
11
+ import theme,{defaultDarkTheme,Colors,defaultLightTheme} from "$theme";
12
+ import Provider from "$ecomponents/Form/FormData/DialogProvider";
13
+ import Dropdown from "$ecomponents/Dropdown";
14
+ import {defaultObj} from "$utils";
15
+ import Icon from "$ecomponents/Icon";
16
+ import {open,close} from "$preloader";
17
+ import {fields,getThemeData} from "./utils";
18
+
19
+ const getStyle = (color)=>{
20
+ if(!Colors.isValid(color)) return {};
21
+ return {backgroundColor:color,paddingHorizontal:10,paddingVertical:5,color:Colors.getContrast(color)};
22
+ }
23
+
24
+ const ThemeSelectorComponent = React.forwardRef((props,ref)=>{
25
+ const innerRef = React.useRef(null);
26
+ return <Dropdown
27
+ {...getThemeFieldProps(props,innerRef)}
28
+ ref = {React.useMergeRefs(innerRef,ref)}
29
+ />
30
+ });
31
+
32
+
33
+ const isDocEditing = ({data})=>{
34
+ return data && isNonNullString(data.name)? true : false;
35
+ };
36
+
37
+ export const getThemeFieldProps = (props,ref)=>{
38
+ props = defaultObj(props);
39
+ let {user,showAdd,onValidate,onChange,onUpsert,...rest} = props;
40
+ const loggedUser = defaultObj(Auth.getLoggedUser());
41
+ user = defaultObj(user,loggedUser);
42
+ const isUserActive = loggedUser.code == user.code && user.code ? true : false;
43
+ const userTheme = defaultObj(user.theme);
44
+ const userThemeName = defaultStr(userTheme.name,defaultThemeName);
45
+ const isDark = theme.isDark() || theme.isDarkUI();
46
+ const defTheme = isDark ? {...defaultDarkTheme.colors,dark:true} : {...defaultLightTheme.colors,dark:false};
47
+ const itemsRef = React.useRef({...defaultObj(user.customThemes),...defaultColors});
48
+ const showThemeExplorer = (data)=>{
49
+ data = defaultObj(data,defTheme);
50
+ fields.name.disabled = ({data})=> data && isNonNullString(data.name);
51
+ const title = data && data.name ? ("Modifier ["+data.name+"]") : ('Nouv theme['+user.code+"]");
52
+ const isEditing = isDocEditing(data);
53
+ fields.textFieldMode.defaultValue = theme.textFieldMode;
54
+ Provider.open({
55
+ cancelButton : true,
56
+ dialogProps : {
57
+ withScrollView : true,
58
+ fullScreen : true,
59
+ },
60
+ data,
61
+ title,
62
+ isDocEditing,
63
+ actions : [{
64
+ text : 'Enregistrer',
65
+ primary : true,
66
+ icon : "check",
67
+ onPress : ({data,formName,...args})=>{
68
+ ///un utilisateur doit avoir au max 10 Thème personnalisés
69
+ const customThemes = defaultObj(user.customThemes);
70
+ let cKeys = [];
71
+ Object.map(customThemes,(cT,i)=>{
72
+ if(!isObj(cT) || !cT.custom){
73
+ delete customThemes[i];
74
+ return;
75
+ }
76
+ cKeys.push(i);
77
+ });
78
+ const counter = cKeys.length;
79
+ if(counter > 10){
80
+ delete customThemes[cKeys[0]]
81
+ }
82
+ data.custom = true;
83
+ customThemes[data.name] = data;
84
+ itemsRef.current = user.customThemes = customThemes;
85
+ open((isEditing?"Modification ":"Enregistrement ")+"du thème...");
86
+ Auth.upsertUser(user,false).then(()=>{
87
+ if(Auth.getLoggedUserCode() == user.code){
88
+ login(user,false);
89
+ }
90
+ if(ref && ref.current && ref.current.refresh){
91
+ ref.current.refresh(true);
92
+ }
93
+ if(typeof onUpsert =='function'){
94
+ onUpsert({data,theme:data,value:data});
95
+ }
96
+ }).finally(()=>{
97
+ Provider.close();
98
+ close();
99
+ });
100
+ }
101
+ }],
102
+ fields,
103
+ });
104
+ }
105
+ rest = {
106
+ multiple : false,
107
+ ...defaultObj(rest),
108
+ showAdd : typeof showAdd =='boolean'? showAdd : isUserActive || Auth.isTableDataAllowed({table:'users',action:'"changeTheme"'}),
109
+ addIconTooltip : "Cliquez pour ajouter un nouveau thème",
110
+ onAdd : ()=>{
111
+ showThemeExplorer();
112
+ },
113
+ onChange : (args)=>{
114
+ args = defaultObj(args);
115
+ const {value} = args
116
+ if(!value) return;
117
+ const {theme,value:validValue} = getThemeData(value);
118
+ args.theme = theme;
119
+ args.realValue = value;
120
+ args.value = validValue;
121
+ if(typeof onValidate =='function'){
122
+ onValidate(args);
123
+ }
124
+ if(typeof onChange =='function'){
125
+ onChange(args);
126
+ }
127
+ },
128
+ defaultValue : userThemeName,
129
+ items : x =>{
130
+ return itemsRef.current;
131
+ },
132
+ compare : (item,selected)=>{
133
+ if(isNonNullString(selected)){
134
+ if(isObj(item) && item.name == selected) {
135
+ return true;
136
+ }
137
+ if(isNonNullString(item)) return item === selected ? true : false;
138
+ }
139
+ return isObj(item) && isObj(selected) && item.name == selected.name ? true : false;
140
+ },
141
+ itemValue : ({item})=>{return item;},
142
+ renderText : ({item,index}) =>{ return defaultStr(item?.name,index+"")},
143
+ renderItem : ({item,index}) =>{
144
+ let {primary,secondary,name,primaryName,secondaryName,dark} = item
145
+ let split = index.split("-")
146
+ let pText = defaultStr(item.custom?name:primaryName,split[0],name,primary)
147
+ let sText = defaultStr(secondaryName,split[1],secondary);
148
+ //<Icon icon={dark?'brightness-6':'brightness-4'} size={15} title={dark?'Sombre':'Clair'}/>
149
+ return <View style={[styles.buttonContainer]}>
150
+ <Label style={[getStyle(primary),{height:'100%',borderLeftWidth:10,borderLeftColor:dark?defaultDarkTheme.colors.surface:defaultLightTheme.colors.background}]}>
151
+ {pText}
152
+ </Label>
153
+ <View style={[styles.labelRight]}>
154
+ <Label style={[getStyle(secondary)]}>
155
+ {sText}
156
+ </Label>
157
+ {<Icon size={20}
158
+ name={item.custom?'pencil':'plus'}
159
+ title={(item.custom?'Cliquer pour modifier le thème': ' Ajouter un thème basé sur le thème')+' ['+item.name+"]"}
160
+ onPress = {(e)=>{
161
+ React.stopEventPropagation(e);
162
+ showThemeExplorer({...Object.clone(item),name:item.custom?item.name:undefined,primaryName:item.custom ? item.primaryName : undefined, secondaryName : item.custom ? item.secondaryName:undefined});
163
+ }}
164
+ />}
165
+ </View>
166
+ </View>
167
+ }
168
+ }
169
+ rest.text = defaultStr(rest.text,rest.label,'Thème');
170
+ return rest;
171
+ }
172
+
173
+ ThemeSelectorComponent.displayName = "ThemeSelectorComponent";
174
+
175
+
176
+ export default ThemeSelectorComponent;
177
+
178
+ const styles = StyleSheet.create({
179
+ theme : {
180
+ padding : 2,
181
+ marginBottom : 2,
182
+ },
183
+ itemContainer : {
184
+ width : '100%',
185
+ },
186
+ buttonContainer : {
187
+ width : '100%',
188
+ flexDirection :'row',
189
+ alignItems :'center',
190
+ },
191
+ labelRight : {
192
+ width : '100%',
193
+ flex : 1,
194
+ flexDirection :'row',
195
+ alignItems :'center',
196
+ }
197
+ });
@@ -0,0 +1,141 @@
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
+ import { lightColors,darkColors,primary as defaultPrimary,secondary as defaultSecondary} from "$theme/defaultTheme";
6
+ import {modesObject} from "$ecomponents/TextField/utils";
7
+
8
+ export const fields = {
9
+ name : {
10
+ type : 'text',
11
+ maxLength : 30,
12
+ text : 'Nom du thème',
13
+ required : true,
14
+ upper : true,
15
+ },
16
+ dark : {
17
+ type : 'switch',
18
+ checkedValue : true,
19
+ uncheckedValue : false,
20
+ label : 'Mode Sombre',
21
+ checkedTooltip : 'Mode sombre actif',
22
+ uncheckedTooltip : 'Mode sombre inactif',
23
+ onChange : ({value,checked,context})=>{
24
+ if(checked === undefined) return;
25
+ if(context && context.getField){
26
+ const darkFields = checked ? darkColors : lightColors;
27
+ Object.map(darkFields,(dF,i)=>{
28
+ const dFContext = context.getField(i);
29
+ if(dFContext && dFContext.setValue){
30
+ dFContext.validate({value:dF});
31
+ }
32
+ })
33
+ }
34
+ }
35
+ },
36
+ textFieldMode : {
37
+ type : 'select',
38
+ items : {...modesObject,none:{code:'',label:'Dynamique'}},
39
+ text : 'Mode d\'affichage des champs de texte'
40
+ },
41
+ primary : {
42
+ type : 'color',
43
+ text : 'Couleur principale [primary]',
44
+ required : true,
45
+ onChange : ({value,context})=>{
46
+ if(context && context.getField){
47
+ const pOnSurface = context.getField("primaryOnSurface");
48
+ if(pOnSurface){
49
+ pOnSurface.setValue(value);
50
+ }
51
+ }
52
+ }
53
+ },
54
+ primaryText : {
55
+ type :'color',
56
+ text :'Texte sur couleur principale [primaryText]',
57
+ required : true,
58
+ },
59
+ secondary : {
60
+ type : 'color',
61
+ text : ' Couleur secondaire [secondary]',
62
+ required : true,
63
+ onChange : ({value,context})=>{
64
+ if(context && context.getField){
65
+ const sOnSurface = context.getField("secondaryOnSurface");
66
+ if(sOnSurface){
67
+ sOnSurface.setValue(value);
68
+ }
69
+ }
70
+ }
71
+ },
72
+ secondaryText : {
73
+ type :'color',
74
+ text :'Texte sur couleur secondaire [secondaryText]',
75
+ required : true,
76
+ },
77
+ text : {
78
+ type :'color',
79
+ text : 'Couleur de texte [text]',
80
+ required : true,
81
+ },
82
+ disabled : {
83
+ type :'color',
84
+ text :'Texte Inactif [disabeld]'
85
+ },
86
+ divider : {
87
+ type : 'color',
88
+ text : 'Couleur|Soulignement [divider]'
89
+ },
90
+ background : {
91
+ type : 'color',
92
+ text : 'Couleur d\'arrière plan [background]',
93
+ },
94
+ surface : {
95
+ type :'color',
96
+ text :'Couleur de surface [surface]',
97
+ },
98
+ surfaceText : {
99
+ type :'color',
100
+ text :'Texte sur surface [surfaceText]',
101
+ },
102
+ primaryOnSurface : {
103
+ type :'color',
104
+ text :'Couleur principale sur surface [primaryOnSurface]',
105
+ },
106
+ secondaryOnSurface : {
107
+ type :'color',
108
+ text :'Couleur secondaire sur surface [secondaryOnSurface]',
109
+ },
110
+ info : {
111
+ type :'color',
112
+ text :'Notification|info [info]'
113
+ },
114
+ success : {
115
+ type :'color',
116
+ text :'Notification|Succès [success]'
117
+ },
118
+ warning : {
119
+ type :'color',
120
+ text :'Notification|Alerte [warning]'
121
+ },
122
+ error : {
123
+ type :'color',
124
+ text :'Notification|Erreur [error]'
125
+ },
126
+ };
127
+ export const getThemeData = (value)=>{
128
+ const theme = {name:value.name,colors:{}};
129
+ const validValue = {};
130
+ Object.map(fields,(f,i)=>{
131
+ if(!f) return;
132
+ if(f.type =='color'){
133
+ const fText = i+"Color";
134
+ theme.colors[i] = value[i] || value[fText];
135
+ } else {
136
+ theme[i] = value[i];
137
+ }
138
+ validValue[i] = value[i];
139
+ });
140
+ return {theme,value:validValue};
141
+ }
@@ -0,0 +1,22 @@
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 {isNativeMobile} from "$platform";
5
+ import defaultSrc from "./defaultAvatar";
6
+ export default {
7
+ accordion:false,
8
+ editable : !isNativeMobile(),
9
+ text : 'Avatar',
10
+ type : 'image',
11
+ defaultSrc,
12
+ size : 100,
13
+ sortable : false,
14
+ datagrid : {
15
+ size : 50,
16
+ },
17
+ rounded : true,
18
+ cropProps : {
19
+ width : 100,
20
+ heigh : 100
21
+ },
22
+ }
@@ -1075,6 +1075,7 @@ class DropdownComponent extends AppComponent {
1075
1075
  {...listProps}
1076
1076
  ref = {this.listRef}
1077
1077
  responsive = {false}
1078
+ contentContainerStyle = {[{backgroundColor:theme.colors.surface},listProps.contentContainerStyle]}
1078
1079
  style = {[listProps.style]}
1079
1080
  prepareItems = {false}
1080
1081
  items = {renderingItems}
@@ -1,6 +1,7 @@
1
1
  import React from "$react";
2
2
  import {InteractionManager} from "react-native";
3
3
  /**** options, les options lié à la transition */
4
+ export const AFTER_INTERACTIONS_TIMEOUT = 10;
4
5
  export const useAfterInteractions = (options) => {
5
6
  const [areInteractionsComplete, setInteractionsComplete] = React.useState(false)
6
7
  if(isNumber(options)){
@@ -1,7 +1,7 @@
1
1
 
2
2
  import React from "$react";
3
3
  import { prepareItems as customPrepareItems,getBToTopRef } from "./utils";
4
- import {grid,StylePropTypes} from "$theme";
4
+ import theme,{grid,StylePropTypes} from "$theme";
5
5
  import PropTypes from "prop-types";
6
6
  import {defaultObj,defaultDecimal,defaultArray,defaultFunc} from "$utils";
7
7
  import {isMobileMedia} from "$cplatform/dimensions";
@@ -159,7 +159,7 @@ const CommonListComponent = React.forwardRef((props,ref)=>{
159
159
  const restP = numColumns > 1 && isFlatList ? {
160
160
  columnWrapperStyle : [styles.columnWrapperStyle,props.columnWrapperStyle]
161
161
  } : {};
162
- return <View {...containerProps} style={[styles.container,containerProps.style]}>
162
+ return <View {...containerProps} style={[{backgroundColor:theme.colors.surface},styles.container,containerProps.style]}>
163
163
  <Component
164
164
  onEndReachedThreshold={0}
165
165
  scrollEventThrottle={16}
@@ -1,6 +1,6 @@
1
1
  import React,{BaseComponent as AppComponent} from '$react';
2
- import View from "$ecomponents/View";
3
- import ScrollView from "$ecomponents/ScrollView";
2
+ import View from "$components/View";
3
+ import ScrollView from "$components/ScrollView";
4
4
  import {
5
5
  Platform,
6
6
  StyleSheet,
@@ -14,11 +14,11 @@ import {
14
14
  } from 'react-native';
15
15
  import PropTypes from "prop-types";
16
16
  import { withTheme,Surface,Portal} from 'react-native-paper';
17
- //import Surface from "$ecomponents/Surface";
17
+ //import Surface from "$components/Surface";
18
18
  import { NativeModules} from 'react-native';
19
19
  import {defaultDecimal,extendObj} from "$utils";
20
20
  import theme,{StylePropTypes} from "$theme";
21
- import APP from "$capp/instance";
21
+ import APP from "$app/instance";
22
22
  import MenuItem from "./Item";
23
23
 
24
24
  const RESIZE_PAGE = APP.EVENTS.RESIZE_PAGE;
@@ -361,6 +361,7 @@ const SimpleSelect = React.forwardRef((props,ref)=>{
361
361
  {...listProps}
362
362
  getItemLayout = {getItemLayout}
363
363
  ref = {listRef}
364
+ contentContainerStyle = {[{backgroundColor:theme.colors.surface},listProps.contentContainerStyle]}
364
365
  style = {[listProps.style]}
365
366
  prepareItems = {false}
366
367
  items = {renderingItems}
@@ -1,7 +1,7 @@
1
1
  import {isObj} from "$utils";
2
- import {notify} from "$ecomponents/Dialog";
2
+ import {notify} from "$components/Dialog";
3
3
  import Camera from "./camera";
4
- import {isMobileNative} from "$cplatform";
4
+ import {isMobileNative} from "$platform";
5
5
  import React from "react";
6
6
 
7
7
  let cameraRef = null;
@@ -16,11 +16,7 @@ export const createCameraRef = ()=>{
16
16
  return ref;
17
17
  }
18
18
 
19
- const ImagePicker = {
20
- MediaTypeOptions : {},
21
- mediaTypes : {},
22
- };
23
- ///import * as ImagePicker from 'expo-image-picker';
19
+ import * as ImagePicker from 'expo-image-picker';
24
20
 
25
21
  export const MEDIA_TYPES = {
26
22
  ALL : ImagePicker.MediaTypeOptions.All,
@@ -3,10 +3,10 @@ import i18n from "$i18n"
3
3
  import Auth from "$cauth";
4
4
  import Menu from "$ecomponents/Menu";
5
5
  import React from "$react";
6
- import Tooltip from "$ecomponents/Tooltip";
6
+ import { screenName } from "$escreens/Auth/Profile";
7
7
  import Image from "$ecomponents/Image";
8
8
  import { StyleSheet,View} from "react-native";
9
- import defaultSource from "./defaultAvatar";
9
+ import avatarProps from "$eauth/avatarProps";
10
10
  import Button from "$ecomponents/Button";
11
11
  import Label from "$ecomponents/Label";
12
12
  import Icon from "$ecomponents/Icon";
@@ -32,7 +32,7 @@ const UserProfileAvatarComponent = React.forwardRef(({drawerRef,...props},ref)=>
32
32
  onPress : (a)=>{
33
33
  closeDrawer(()=>{
34
34
  return navigate({
35
- //routeName : screenName,
35
+ routeName : screenName,
36
36
  params : {
37
37
  user : u,
38
38
  }
@@ -48,17 +48,16 @@ const UserProfileAvatarComponent = React.forwardRef(({drawerRef,...props},ref)=>
48
48
  }
49
49
  }
50
50
  ];
51
- let pseudo = defaultStr(u.code,u.pseudo,u.email);
52
- let pT = pseudo;
53
- if(pseudo.length > 8){
54
- pT = pseudo.substring(0,7)+".."
55
- }
56
- pseudo = <Tooltip title={defaultStr(u.label)+" ["+pseudo+"]"}>{pT}</Tooltip>;
51
+ let pseudo = defaultStr(u.code,u.pseudo,u.email)
52
+ const label = defaultStr(u.label,u.name,u.fullName,u.userName)
53
+ //let pT = pseudo;
54
+ //pseudo = <Tooltip uppserCase={false} title={defaultStr(u.label)+" ["+pseudo+"]"}>{pT}</Tooltip>;
57
55
  return <View ref ={ref}>
58
56
  <Menu
59
57
  anchor = { (aProps)=>{
60
58
  return <Button
61
59
  normal
60
+ upperCase = {false}
62
61
  disableRipple
63
62
  {...aProps}
64
63
  style = {[styles.container]}
@@ -69,7 +68,7 @@ const UserProfileAvatarComponent = React.forwardRef(({drawerRef,...props},ref)=>
69
68
  style = {styles.itemLeft}
70
69
  testID = {"RN_ProfilAvatar_Avatar"}
71
70
  editable
72
- defaultSource ={defaultSource}
71
+ defaultSource ={avatarProps.defaultSrc}
73
72
  onChange = {({dataURL})=>{
74
73
  if(u.avatar === dataURL) {
75
74
  return;
@@ -97,7 +96,7 @@ const UserProfileAvatarComponent = React.forwardRef(({drawerRef,...props},ref)=>
97
96
  <View style={styles.labelContainer}>
98
97
  <Label splitText style={{color:theme.colors.primaryOnSurface}}>{pseudo}</Label>
99
98
  <Label splitText style={{fontSize:12,color:theme.colors.secondaryOnSurface,marginTop:6}}>
100
- {u.label}
99
+ {label}
101
100
  </Label>
102
101
  </View>
103
102
  </Button>
@@ -120,6 +119,7 @@ const styles = StyleSheet.create({
120
119
  flexDirection : 'column',
121
120
  paddingRight : 5,
122
121
  maxWidth : 130,
122
+ minWidth : 100,
123
123
  },
124
124
  pseudo : {
125
125
  flexDirection : "row",
@@ -0,0 +1,97 @@
1
+ import FormDataScreen from "$elayouts/Screen/FormData";
2
+ import {defaultStr,defaultObj,isObjOrArray,isObj} from "$utils";
3
+ import Auth from "$auth";
4
+ import {getScreenProps,navigate} from "$cnavigation";
5
+ import SelectTheme from "$themeSelectorComponent";
6
+ import Preloader from "$epreloader";
7
+ import {SignIn2SignOut} from "$cauth";
8
+ import React from "$react";
9
+ import avatarProps from "$eauth/avatarProps";
10
+
11
+ export const screenName = "UserProfile";
12
+
13
+ export default function UserProfileScreen(prs){
14
+ const props = getScreenProps(prs);
15
+ const user = defaultObj(props.user,Auth.getLoggedUser());
16
+ const testID = defaultStr(props.testID,"RN_UserProfile_FormData");
17
+ const themeRef = React.useRef(defaultObj(user.theme));
18
+ const hasChangeRef = React.useRef(false);
19
+ const authProfileFields = typeof SignIn2SignOut.authProfileFields =='function'?SignIn2SignOut.authProfileFields(props) : SignIn2SignOut.authProfileFields;
20
+ const fields = isObj(authProfileFields)? authProfileFields : {};
21
+ const formFields = {
22
+ avatar : {
23
+ ...avatarProps,
24
+ ...(isObj(fields.avatar)?fields.avatar:{}),
25
+ text : undefined,
26
+ label : undefined,
27
+ responsive : false,
28
+ responsiveProps : {
29
+ style : {
30
+ width : '100%',
31
+ alignItems : 'center',
32
+ justifyContent : 'center',
33
+ }
34
+ },
35
+ },
36
+ }
37
+ Object.map(fields,(field,i)=>{
38
+ if(i !='avatar' && isObj(field)){
39
+ formFields[i] = field;
40
+ }
41
+ })
42
+ formFields.theme = {
43
+ type : 'html',
44
+ text : 'Theme',
45
+ onChange : ({value})=>{
46
+ if(isObj(value) && value.name && value.primary && value.name !== themeRef.current.name){
47
+ hasChangeRef.current = true;
48
+ themeRef.current = value;
49
+ }
50
+ },
51
+ onUpsert : ({value})=>{
52
+ if(value.name === themeRef.current.name){
53
+ hasChangeRef.current = true;
54
+ themeRef.current = value;
55
+ }
56
+ },
57
+ render : (p)=>{
58
+ return <SelectTheme {...p} defaultValue = {themeRef.current.name}/>
59
+ }
60
+ }
61
+ formFields.avatar.onChange = (args)=>{
62
+ if(args.value === user.avatar) return;
63
+ hasChangeRef.current = true;
64
+ }
65
+ const onSaveProfile = ({data,goBack,...rest})=>{
66
+ data.theme = themeRef.current;
67
+ Preloader.open("Modification en cours...");
68
+ Auth.upsertUser(data,true).then(()=>{
69
+ if(typeof goBack =='function' && !hasChangeRef.current){
70
+ return goBack(true);
71
+ }
72
+ navigate('Home');
73
+ }).finally(()=>{
74
+ setTimeout(()=>{
75
+ Preloader.close();
76
+ },1000);
77
+ });
78
+ }
79
+
80
+ return <FormDataScreen
81
+ title = {(user.label?(user.label+" ["+user.code+"] | "):"")+"Profil : Modifier"}
82
+ {...props}
83
+ modal
84
+ withScrollView
85
+ fields = {formFields}
86
+ data = {user}
87
+ testID = {testID}
88
+ onSave = {onSaveProfile}
89
+ subtitle = {false}
90
+ />
91
+ }
92
+
93
+ UserProfileScreen.screenName = screenName;
94
+
95
+ UserProfileScreen.Modal = true;
96
+
97
+ UserProfileScreen.authRequired = true;
@@ -0,0 +1,29 @@
1
+
2
+ import {SIGN_IN} from "$cauth/routes"
3
+ import Screen from "$escreen";
4
+ import Login from "$eauth/Login";
5
+ import {getScreenProps} from "$cnavigation";
6
+ import {getTitle} from "./utils";
7
+
8
+
9
+ function AuthSignInScreen(_props){
10
+ const props = getScreenProps(_props);
11
+ const title = getTitle();
12
+ return <Screen
13
+ title = {title}
14
+ appBarProps = {{
15
+ title,backAction:false
16
+ }}
17
+ contentContainerStyle = {{justifyContent:'center',alignItems:'center'}}
18
+ >
19
+ <Login
20
+ {...props}
21
+ />
22
+ </Screen>
23
+ }
24
+
25
+ AuthSignInScreen.screenName = SIGN_IN;
26
+ AuthSignInScreen.authRequired = false;
27
+ AuthSignInScreen.modal = true;
28
+ AuthSignInScreen.allowDrawer = false;
29
+ export default AuthSignInScreen;
@@ -1,30 +1,8 @@
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.
1
4
 
2
- import {SIGN_IN} from "$cauth/routes"
3
- import Screen from "$escreen";
4
- import Login from "$eauth/Login";
5
- import {getScreenProps} from "$cnavigation";
6
- import {getTitle} from "./utils";
5
+ import AuthSignInScreen from "./SignIn";
6
+ import UserProfileScreen from "./Profile";
7
7
 
8
-
9
- function AuthSignInScreen(_props){
10
- const props = getScreenProps(_props);
11
- const title = getTitle();
12
- return <Screen
13
- title = {title}
14
- appBarProps = {{
15
- title,backAction:false
16
- }}
17
- contentContainerStyle = {{justifyContent:'center',alignItems:'center'}}
18
- >
19
- <Login
20
- {...props}
21
- />
22
- </Screen>
23
- }
24
-
25
- AuthSignInScreen.screenName = SIGN_IN;
26
- AuthSignInScreen.authRequired = false;
27
- AuthSignInScreen.modal = true;
28
- AuthSignInScreen.allowDrawer = false;
29
-
30
- export default [AuthSignInScreen];
8
+ export default [AuthSignInScreen,UserProfileScreen];
@@ -1,6 +1,6 @@
1
1
  import {defaultBool,defaultArray,defaultVal,isObj,isNonNullString,defaultObj,isArray,defaultStr} from "$utils";
2
2
  import {sanitizeName,GROUP_NAMES} from "./utils";
3
- import mainScreens from "$screens/mainScreens"
3
+ import mainScreens from "$mainScreens"
4
4
  import React from "$react";
5
5
  import { BackHandler } from "react-native";
6
6
  import APP from "$capp";
@@ -1,4 +1,4 @@
1
1
  import Auth from "./Auth";
2
2
  export default [
3
- Auth,
3
+ ...Auth,
4
4
  ]