@fto-consult/common
Version:
Un ensemble de bibliothèques et d'utilistaires communs pour le développement d'applications javascript
436 lines (387 loc) • 17.5 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.
import {isObj,defaultNumber,extendObj,defaultObj,isNonNullString,defaultVal,defaultStr} from "$cutils";
import Colors from "./colors";
import {isWeb,isIos} from "$cplatform";
import PropTypes from "prop-types";
import styled from "$active-platform/styled";
import updateNativeTheme from "./updateNative";
import * as React from "react";
import { isComponent } from "$react/isComponent";
import defTheme,{white,black,lightColors,darkColors} from "./defaultTheme";
import { themeRef } from "./utils";
import appConfig from "$capp/config";
import styles,{flattenStyle,_cursorPointer} from "./styles";
import APP from "$capp/instance";
export * from "./utils";
import { ALPHA_OPACITY,ALPHA } from "./utils";
/**** check si le système est en mode dark */
export const isDarkUI = x => themeRef && typeof themeRef.isDarkUI =='function' ? themeRef.isDarkUI() : false;
export const isValidColors = colors => isObj(colors) && (Colors.isValid(colors.primary) || Colors.isValid(colors.primaryColor)) ? true : false;
export const isValid = theme => isObj(theme) && isValidColors(theme.colors) ? true : false;
export const isStatusBarColorDarken = x=>{
return false;
}
/*** modifie les couleurs du theme pris en paramètre
@see : https://github.com/pchmn/expo-material3-theme/blob/main/src/ExpoMaterial3Theme.types.ts#L59-L62
* @param {object} objet theme ou tout simplement les couleurs pris en paramètre, valide selon la fonction isValid
* @return {object} theme dont les couleurs ont été modifiées
*/
export const updateColors = (theme,force)=>{
if(force !== true){
const cTheme = getTheme();
if(isValidColors(theme)){
theme = {...cTheme,colors:{...cTheme.colors,...theme}};
}
}
if(!isValid(theme)) return theme;
const dColors = theme.dark || isDarkUI() ? darkColors : lightColors;
const colors = theme.colors = {...dColors,...theme.colors};
///la couleur secondaire
colors.primary = Colors.isValid(colors.primary)? colors.primary : Colors.isValid(colors.primaryColor)? colors.primaryColor : cTheme.primary;
colors.secondary = Colors.isValid(colors.secondary)? colors.secondary : Colors.isValid(colors.secondaryColor)? colors.secondaryColor : cTheme.colors.secondary;
colors.info = Colors.isValid(colors.info)? colors.info : lightColors.info;
colors.warning = Colors.isValid(colors.warning)? colors.warning : lightColors.warning;
colors.error = Colors.isValid(colors.error)? colors.error : lightColors.error;
colors.success = Colors.isValid(colors.success)? colors.success : lightColors.success;
['primary','secondary','background','surface','error','success','info','warning'].map((c)=>{
const key = c+"Text",onKey="on"+c.trim().ucFirst();
colors[key] = colors[onKey] = Colors.isValid(colors[onKey]) ? colors[onKey] : Colors.isValid(colors[key])? colors[key] : Colors.getContrast(colors[c])
});
if(!Colors.isValid(colors.primaryOnSurface)){
if( Colors.getContrast(colors.primary) === Colors.getContrast(colors.onSurface)){
colors.primaryOnSurface = colors.primary;
} else {
colors.primaryOnSurface = colors.onSurface;
}
}
if(!Colors.isValid(colors.secondaryOnSurface)){
if(Colors.getContrast(colors.secondary) == Colors.getContrast(colors.onSurface)){
colors.secondaryOnSurface = colors.secondary
} else {
colors.secondaryOnSurface = colors.onSurface;
}
}
["dark","light"].map((c)=>{
if(typeof colors[c] =="boolean"){
theme[c] = colors[c];
delete colors[c];
}
});
//la couleur du status bar
colors.statusBar = theme.dark ? colors.surface : isStatusBarColorDarken() ? Colors.darken(colors.primary) : colors.primary;
return theme;
}
const defaultTheme = updateColors(defTheme,true);
export {defaultTheme};
export {Colors,lightColors,darkColors,white,black};
export {tinyColor} from "./colors"
const Theme = {
current : defaultTheme,
}
export const getTheme = x => Theme.current ? Theme.current : defaultTheme;
export const getColors = x => getTheme().colors;
export const remToPixel= (x)=> 16*defaultNumber(x);
export const pixelToRem = x => 0.0625 * defaultNumber(x);
//@see : https://docs.expo.dev/versions/latest/sdk/status-bar/#statusbarstyle
export const getStatusBarStyle = ()=>{
const statusBarStyle = {
animated: true
}
statusBarStyle.backgroundColor = theme.colors.statusBar;
statusBarStyle.style = (Colors.isLight(theme.colors.statusBar)? "dark" : "light");
return statusBarStyle;
}
export {default as grid} from "./grid";
export const isDark = checkSystemUI => Theme.current.dark ? true : checkSystemUI === true ? isDarkUI():false;
export const isLight = x=> !isDark(x);
/*** récupère une propriétée associée au theme courant */
export const getProperty = function(){
const args = Array.prototype.slice.call(arguments,0);
for(let i in args){
let key = args[i];
if(typeof key =='string' && key){
key = key.trim();
const v = Theme.current[key];
if(v !== undefined && v !== null) return v;
}
}
return undefined;
}
///récupère la position du profil avatar
export const getProfilAvatarPosition = x=>{
if(isNonNullString(Theme.current.profilAvatarPosition)){
return Theme.current.profilAvatarPosition.trim();
}
if(appConfig.get("showProfilAvatarOnDrawer") === false) return "appBar";
return "drawer";
}
const setAlphaColor = (color,alpha)=>{
return Colors.setAlpha(color,typeof alpha ==="number"? alpha : ALPHA);
};
const theme = {
get isV3 (){ return true;},
get ALPHA () {return ALPHA},
get current(){ return Theme.current;},
get name(){return Theme.current.name;},
get alphaColor (){
return setAlphaColor;
},
get setAlphaColor(){return setAlphaColor;},
get ALPHA_OPACITY () {return ALPHA_OPACITY},
get colors () {return Theme.current.colors},
get primary (){ return Theme.current.colors.primary;},
get secondary(){return Theme.current.colors.secondary;},
get fonts () { return Theme.current.fonts},
get dark () { return Theme.current.dark},
get isDark () { return isDark},
get isLight (){ return isLight},
get isDarkUI (){return isDarkUI},
get roundness () { return Theme.current.roundness},
get animation () { return Theme.current.animation;},
get textFieldMode () {
return Theme.current.textFieldMode;
},
get profilAvatarPosition (){
return getProfilAvatarPosition();
},
get profilAvatarPos(){
return getProfilAvatarPosition();
},
/*** si le profil avatar de l'utilisateur sera affiché sur le drawer */
get showProfilAvatarOnDrawer (){
return getProfilAvatarPosition().toLowerCase() === 'drawer'? true : false;
},
get withStyles (){ return withStyles},
get flattenStyle(){return flattenStyle},
get StyleProp (){return StyleProp},
get StyleProps () {return StyleProp},
get Colors (){return Colors},
get styles (){return styles},
/*** si la couleur du status bar est sombre */
get isStatusBarColorDarken (){ return isStatusBarColorDarken; },
/**** la couleur de surface dynamique fonction du thème dark où non */
get surfaceBackgroundColor (){ return isDark()? theme.colors.background : theme.colors.surface},
//get surfaceTextColor (){ return isDark()? theme.colors.background : theme.colors.surface},
get get (){
return getProperty;
},
get getProperty(){
return getProperty;
},
get isValid(){
return isValid;
}
}
/**** permet de mettre à jour le thème courant :
* @voir : https://callstack.github.io/react-native-paper/theming.html
* @paramètre de l'objet Theme : {
primary - primary color for your app, usually your brand color.
secondary - secondary color for your app which complements the primary color.
background - background color for pages, such as lists.
surface - background color for elements containing content, such as cards.
text - text color for content.
disabled - color for disabled elements.
placeholder - color for placeholder text, such as input placeholder.
backdrop - color for backdrops of various components such as modals.
onSurface - background color for snackbars
notification - background color for badges
* }
*
*/
export const updateTheme = (currentTheme)=>{
currentTheme = updateColors(currentTheme);
if(isValid(currentTheme)){
Object.map(defaultTheme,(v,i)=>{
if(i !=='colors' && !(i in currentTheme)){
currentTheme[i] = v;
}
});
if(typeof appConfig.extendAppTheme ==='function'){
const r = appConfig.extendAppTheme(currentTheme,theme);
if(isValid(r)){
currentTheme = r;
}
}
updateNativeTheme(currentTheme,theme);
if(themeRef && typeof themeRef.setBackgroundColor =='function'){
themeRef.setBackgroundColor(currentTheme.colors.background);
}
setTimeout(()=>{
APP.trigger(APP.EVENTS.UPDATE_THEME,currentTheme);
},100);
Theme.current = currentTheme;
}
return currentTheme;
}
export default theme;
export {theme};
export * from "./styles";
export const StylePropTypes = PropTypes.oneOfType([
PropTypes.object,
PropTypes.array,
PropTypes.number,
]);
export const StylePropsTypes = StylePropTypes;
export const StyleProps = StylePropsTypes;
export const StyleProp = StylePropsTypes;
/** retourne un */
export const getColorFromAlias = (color,defaultColor)=>{
color = defaultStr(color,defaultColor);
if(!color) return undefined;
color = color.trim();
const col = color;
const colors = theme.colors;
if(!colors[color]){
color = color.toLowerCase();
}
if(!colors[color]){
color = color.toCamelCase();
}
return colors[color] || Colors.isValid(col)? col : undefined;
}
const ComponentWrapper = (Component,displayName)=>{
const WrapC = React.forwardRef((props,ref)=>{
return <Component {...props} ref={ref}/>
});
WrapC.displayName = defaultStr(Component.displayName,displayName,"ComponentWrapperName");
WrapC.propTypes = WrapC.propTypes || Component.propTypes;
return WrapC;
};
export const colorsAlias = ['primary','secondary','success','error','danger','warning','normal']
export const colorsKeysAlias = {};
colorsAlias.map((c)=>{
colorsKeysAlias[c] = true;
colorsKeysAlias[c+"Text"] = true;
colorsKeysAlias[c+"Color"] = true;
});
export {styled};
/**** le chmap mode est utilisé pour déterminé comment les propriété primay, secondary, ...et autre seront appliquées
* si mode est différent de contained alors, l'attribut primary ou secondary ou success, donne uniquement
* la couleur primary ou success au composant. sinon, cet attribut donne comme backgroundColor, la couleur primary ou success, ...
* et comme couleur, la valeur du texte lié à ladite couleur
* La props withStyle, permet de spécifier si en dépit de la couleur définie dans le composant, la props style color|background sera calculé en fonction des propriétés de ColorsAlias
*/
export const withStyles = (Component,options,mutator)=>{
Component = isComponent(Component)? Component : defaultStr(Component);
options = typeof options =='object' && options ? options : {};
const {mode,mutator:customMutator,displayName, ...opts} = options;
const customShouldForwardProps = typeof opts.shouldForwardProp =='function'? opts.shouldForwardProp : x=>true;
if(!Component) return React.Fragment;
mutator = typeof customMutator =='function'? customMutator : typeof mutator =='function'? mutator : undefined;
return styled(ComponentWrapper(Component,displayName),{...opts,
shouldForwardProp : (prop,defaultValidatorFn,...rest)=>{
const cP = customShouldForwardProps(prop,defaultValidatorFn,...rest);
if(cP === false) return false;
if(cP === true && typeof opts.shouldForwardProp =='function') return true;
return prop !=="cursorPointer" && prop !=="cursorNotAllowed" && !colorsKeysAlias[prop] && prop !=='surface' && prop !== 'withStyle' ? true : false;
}
})(({color,surface,bold,userSelect,noPadding,noMargin,textBold,cursorNotAllowed:cCursorNotAllowed,cursorPointer:cCursorPointer,loading,withStyle,disabled,backgroundColor,mode:customMode,style,...rest }) =>{
const isContained = (typeof customMode =='string' && customMode ? customMode : mode) == 'contained' ? true : false;
rest = defaultObj(rest);
const colors = theme.colors;
style = flattenStyle(style);
color = getColorFromAlias(color);
backgroundColor = getColorFromAlias(backgroundColor);
if(withStyle || (!style.color && !style.backgroundColor && !Colors.isValid(color) && !Colors.isValid(backgroundColor))){
for(let i in colorsAlias){
let c = colorsAlias[i];
if(c =='normal'){
c = 'text';
}
const colorTextContrast = (c+"Text");
const colorTextColor = (c+"Color");
if(rest[c]){
if(colors[c]){
if(!isContained){
style.color = colors[c];
break;
} else {
style.backgroundColor = colors[c];
if(colors[colorTextContrast]){
style.color = colors[colorTextContrast];
break;
} else {
style.color = Colors.isValid(style.color)? style.color : Colors.getContrast(colors[c]);
break;
}
}
} else if(colors[colorTextColor]){
style.color = colors[colorTextColor];
break;
}
} else {
if(rest[colorTextContrast] && colors[colorTextContrast]){
style.color = colors[colorTextContrast];
} else if(rest[colorTextColor] && colors[colorTextColor]){
style.color = colors[colorTextColor];
}
}
}
}
color = Colors.isValid(style.color)? style.color : Colors.isValid(color)? color : undefined;
backgroundColor = Colors.isValid(style.backgroundColor)? style.backgroundColor : Colors.isValid(backgroundColor)? backgroundColor : undefined;
if(surface){
if(!backgroundColor){
backgroundColor = theme.surface;
color = color || theme.text;
}
}
if(color){
style.color = color;
}
if(backgroundColor){
style.backgroundColor = backgroundColor;
}
if(color && !backgroundColor && disabled && theme.disabled){
style.color = theme.disabled;
}
if(disabled || loading){
style.opacity = style.opacity || 0.3;
if(isWeb() && !style.cursor){
style.cursor = "none";
}
}
if(bold || textBold){
style.fontWeight = "bold";
}
if(_cursorPointer?.cursor){
if(cCursorPointer){
style.cursor = "pointer";
} else if(cCursorNotAllowed){
style.cursor = "not-allowed";
}
}
if(noPadding){
style.padding = style.paddingHorizontal = style.paddingVertical = 0;
}
if(noMargin){
style.margin = style.marginHorizontal = style.marginVertical = 0;
}
if(typeof userSelect ==='boolean'){
style.userSelect = userSelect ? "all" : "none";
}
if(typeof mutator =='function'){
extendObj(style,mutator(style,{...rest,style,disabled,loading,displayName,color,backgroundColor}));
return style;
}
return style;
})
}
const defaultDarkTheme = {
...defaultTheme,
colors : {...darkColors,primary : "#06b6d4",primary:'#212121',secondary : "#fda4af"},
dark : true,
}
updateColors(defaultDarkTheme);
export {defaultTheme as defaultLightTheme,defaultDarkTheme};
if(typeof window !=="undefined" && window && typeof window?.getCurrentAppTheme !=="function"){
Object.defineProperties(window,{
getCurrentAppTheme : {
value : ()=>{
return Theme.current;
}
}
})
}