@fto-consult/expo-ui
Version:
Bibliothèque de composants UI Expo,react-native
317 lines (305 loc) • 12.9 kB
JavaScript
// Copyright 2022 @fto-consult/Boris Fouomene. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
///permet de sélectionner un theme utilisateur
import React from "$react";
import Label from "$ecomponents/Label";
import Auth,{login,getLoginId} from "$cauth";
import View from "$ecomponents/View";
import { StyleSheet } from "react-native";
import defaultTheme,{getColors} from "$theme/defaultTheme";
import theme,{defaultDarkTheme,Colors,defaultLightTheme,lightColors,darkColors} from "$theme";
import Provider from "$ecomponents/Form/FormData/DialogProvider";
import Dropdown from "$ecomponents/Dropdown";
import {defaultObj} from "$cutils";
import Icon from "$ecomponents/Icon";
import {open,close} from "$preloader";
import {fields,getThemeData} from "$theme/utils";
import {modes} from "$ecomponents/TextField/utils";
import { useColorScheme } from "react-native";
import {createMaterial3Theme,getMaterial3Theme} from "@pchmn/expo-material3-theme";
import notify from "$cnotify";
const mColors = [
{
light: '#FFE082',
dark: '#FFE082',
},
{
light: '#3E8260',
dark: '#ADF2C7',
},
{
light: '#756FAB',
dark: '#E5DFFF',
},
{
light: '#9F6C2C',
dark: '#FDDDB9',
},
]
const getStyle = (color)=>{
if(!Colors.isValid(color)) return {};
return {backgroundColor:color,paddingHorizontal:10,paddingVertical:5,color:Colors.getContrast(color)};
}
const ThemeSelectorComponent = React.forwardRef((props,ref)=>{
const innerRef = React.useRef(null);
return <Dropdown
{...getThemeFieldProps(props,innerRef)}
ref = {React.useMergeRefs(innerRef,ref)}
/>
});
const isDocEditing = ({data})=>{
return data && isNonNullString(data.name)? true : false;
};
export const getThemeFieldProps = (props,ref)=>{
props = defaultObj(props);
let {user,showAdd,onValidate,onChange,onUpsert,...rest} = props;
const loggedUser = defaultObj(Auth.getLoggedUser());
user = defaultObj(user,loggedUser);
const loginId = getLoginId(user);
const hasLoginId = isNonNullString(loginId) || typeof loginId =='number';
const isUserActive = getLoginId(loggedUser) == loginId && hasLoginId ? true : false;
const userTheme = defaultObj(user.theme);
const userThemeName = defaultStr(userTheme.name,defaultTheme.name);
const isDark = theme.isDark() || theme.isDarkUI();
const defTheme = isDark ? {...defaultDarkTheme.colors,dark:true} : {...defaultLightTheme.colors,dark:false};
const colorScheme = useColorScheme();
const prepareTheme = (_theme)=>{
if(!isObj(_theme)) return _theme;
const dark = _theme.dark;
const cols = dark ? darkColors : lightColors;
_theme.divider = Colors.isValid(_theme.divider)? _theme.divider : cols.divider;
_theme.text = Colors.isValid(_theme.text) ? _theme.text : _theme.onBackground || _theme.onSurface;
["primary","secondary"].map((p)=>{
const k = `on${p.ucFirst()}`;
if(_theme[k] && _theme[p]){
if(Colors.getContrast(_theme[k]) === Colors.getContrast(_theme[p])){
_theme[k] = dark ? Colors.lighten(_theme[k]) : Colors.darken(_theme[k]);
}
}
});
["warning","error","info","success","divider"].map((c)=>{
if(!Colors.isValid(_theme[c])){
_theme[c] = cols[c];
}
const onKey = `on${c.ucFirst()}`;
_theme[onKey] = _theme[onKey] || cols[onKey];
});
if(!Colors.isValid(_theme.primaryOnSurface)){
if( Colors.getContrast(_theme.primary) === Colors.getContrast(_theme.onSurface)){
_theme.primaryOnSurface = _theme.primary;
} else {
_theme.primaryOnSurface = _theme.onSurface;
}
}
if(!Colors.isValid(_theme.secondaryOnSurface)){
if(Colors.getContrast(_theme.secondary) == Colors.getContrast(_theme.onSurface)){
_theme.secondaryOnSurface = _theme.secondary
} else {
_theme.secondaryOnSurface = _theme.onSurface;
}
}
return _theme;
}
const customColors = React.useMemo(()=>{
const colors = getColors({withNamed:false});
const namedColors = getColors({withDefaults:false});
const nColors = {};
mColors.map((c,index)=>{
try {
const name = `custom${index+1}`;
['light','dark'].map((sheme)=>{
const n = `${name}-${sheme}`;
const t = getMaterial3Theme(c[sheme]);
nColors[n] = prepareTheme({
name:n,
primaryName : c[sheme],
secondaryName : n,
...t[sheme],
dark:sheme ==="dark",
});
});
} catch(e){console.log(e," preparing theme")}
});
return {...colors,...nColors,...namedColors};
},[]);
const itemsRef = React.useRef({...defaultObj(user.customThemes),...customColors});
fields.textFieldMode = {
type : 'select',
items : {...modes,none:{code:'',label:'Dynamique'}},
text : 'Mode d\'affichage des champs de texte'
}
fields.dark = Object.assign({},fields.dark);
fields.dark.defaultValue = colorScheme =="dark"? 1 : 0;
const showThemeExplorer = (data)=>{
const dat2 = data = defaultObj(data,defTheme);
fields.name.disabled = ({data})=> data && isNonNullString(data.name);
const title = data && data.name ? ("Modifier ["+data.name+"]") : ('Nouv theme['+loginId+"]");
const isEditing = isDocEditing(data);
fields.textFieldMode.defaultValue = theme.textFieldMode;
fields.profilAvatarPosition.defaultValue = theme.profilAvatarPosition;
Provider.open({
cancelButton : true,
dialogProps : {
withScrollView : true,
fullScreen : true,
},
data,
title,
isDocEditing,
actions : [{
text : 'Enregistrer',
primary : true,
icon : "check",
onPress : ({data,formName,...args})=>{
///un utilisateur doit avoir au max 10 Thème personnalisés
const customThemes = defaultObj(user.customThemes);
let cKeys = [];
Object.map(customThemes,(cT,i)=>{
if(!isObj(cT) || !cT.custom){
delete customThemes[i];
return;
}
cKeys.push(i);
});
const counter = cKeys.length;
if(counter > 10){
delete customThemes[cKeys[0]]
}
data.custom = true;
customThemes[data.name] = data;
itemsRef.current = {...customThemes,...customColors};
user.customThemes = customThemes;
open((isEditing?"Modification ":"Enregistrement ")+"du thème...");
Auth.upsertUser(user,false).then(()=>{
if(Auth.getLoginId(Auth.getLoggedUser()) == getLoginId(user)){
login(user,false);
}
if(ref && ref.current && ref.current.refresh){
ref.current.refresh(true);
}
if(typeof onUpsert =='function'){
onUpsert({data,theme:data,value:data});
}
}).finally(()=>{
Provider.close();
close();
});
}
}],
fields,
});
}
rest = {
multiple : false,
...defaultObj(rest),
showAdd : typeof showAdd =='boolean'? showAdd : isUserActive || Auth.isTableDataAllowed({table:'users',action:'"changeTheme"'}),
addIconTooltip : "Cliquez pour ajouter un nouveau thème",
onAdd : ()=>{
return Provider.open({
title : "Ajouter un theme personnalisé",
fields : {
color : {
type :"color",
text : 'A partir de la couleur',
required : true,
},
name : fields.name,
dark : fields.dark,
},
onSuccess : ({data})=>{
try {
const theme = createMaterial3Theme(data.color)
const dat = prepareTheme({...data,...Object.assign({},(data.dark? theme.dark : theme?.light))});
delete dat.color;
setTimeout(()=>{
showThemeExplorer(dat);
},200);
} catch(e){
notify.error(e);
Provider.close();
}
}
})
},
onChange : (args)=>{
args = defaultObj(args);
const {value} = args
if(!value) return;
const {theme,value:validValue} = getThemeData(value);
args.theme = theme;
args.realValue = value;
args.value = validValue;
if(typeof onValidate =='function'){
onValidate(args);
}
if(typeof onChange =='function'){
onChange(args);
}
},
defaultValue : userThemeName,
items : x =>{
return itemsRef.current;
},
compare : (item,selected)=>{
if(isNonNullString(selected)){
if(isObj(item) && item.name == selected) {
return true;
}
if(isNonNullString(item)) return item === selected ? true : false;
}
return isObj(item) && isObj(selected) && item.name == selected.name ? true : false;
},
itemValue : ({item})=>{return item;},
renderText : ({item,index}) =>{ return defaultStr(item?.name,index+"")},
renderItem : ({item,index}) =>{
let {primary,secondary,name,primaryName,secondaryName,dark} = item
let split = index.split("-")
let pText = defaultStr(item.custom?name:primaryName,split[0],name,primary)
let sText = defaultStr(secondaryName,split[1],secondary);
//<Icon icon={dark?'brightness-6':'brightness-4'} size={15} title={dark?'Sombre':'Clair'}/>
return <View style={[styles.buttonContainer]}>
<Label style={[getStyle(primary),{height:'100%',borderLeftWidth:10,borderLeftColor:dark?"black":"white"}]}>
{pText}
</Label>
<View style={[styles.labelRight]}>
<Label style={[getStyle(secondary)]}>
{sText}
</Label>
{<Icon size={20}
name={item.custom?'pencil':'plus'}
title={(item.custom?'Cliquer pour modifier le thème': ' Ajouter un thème basé sur le thème')+' ['+item.name+"]"}
onPress = {(e)=>{
React.stopEventPropagation(e);
showThemeExplorer({...Object.clone(item),name:item.custom?item.name:undefined,primaryName:item.custom ? item.primaryName : undefined, secondaryName : item.custom ? item.secondaryName:undefined});
}}
/>}
</View>
</View>
}
}
rest.text = defaultStr(rest.text,rest.label,'Thème');
return rest;
}
ThemeSelectorComponent.displayName = "ThemeSelectorComponent";
export default ThemeSelectorComponent;
const styles = StyleSheet.create({
theme : {
padding : 2,
marginBottom : 2,
},
itemContainer : {
width : '100%',
},
buttonContainer : {
width : '100%',
flexDirection :'row',
alignItems :'center',
},
labelRight : {
width : '100%',
flex : 1,
flexDirection :'row',
alignItems :'center',
}
});