UNPKG

@fto-consult/expo-ui

Version:

Bibliothèque de composants UI Expo,react-native

218 lines (206 loc) 9.86 kB
import {isPlainObj,defaultFunc,defaultStr,isObjOrArray,isObj,defaultObj} from "$cutils"; import {getWindowSizes,isMobileOrTabletMedia,isMobileMedia} from "$cdimensions"; import { StyleSheet } from "react-native"; import Button from "$ecomponents/Button"; import React from "$react"; import theme from "$theme" import Action from "$ecomponents/Form/Action"; import Menu from "$ecomponents/Menu"; import Icon,{ MORE_ICON } from "$ecomponents/Icon"; import breakpoints from "$cdimensions/breakpoints"; import {isPermAllowed} from "$eauth/utils"; export const ACTION_ICON_SIZE = 30; export const TITLE_FONT_SIZE = 16; export const getMaxActions = (windowWidth) => { let iWidth = typeof windowWidth =='number' && windowWidth > 200 ? windowWidth : getWindowSizes().width - 100; return iWidth >= 3000 ? 8 : iWidth >= 2500? 7 : iWidth >= 2000 ? 6 : iWidth >= 1600 ? 5 : iWidth >= 1300 ? 4 : iWidth >= 800 ? 2 : iWidth >= 600 ? 1 : 0 } export const isSplitedActions = (actions)=> isObj(actions) && Array.isArray(actions.actions) && Array.isArray(actions.menus); export const getThemeColors = ()=>{ const isDark = theme.isDark() || theme.colors.dark ,onPrimary = isDark? theme.colors.onSurface : theme.colors.onPrimary; return { onPrimary, color : onPrimary, backgroundColor : isDark? theme.colors.surface : theme.colors.primary } } const renderAction = ({action,isAlert,actionProps,opts,isAppBarAction,isAppBarActionStyle,key,ActionComponent,isMobile}) => { let {Component,isFormAction,...rest} = action; isFormAction = typeof isFormAction === 'boolean' ? isFormAction : isNonNullString(action.formName); actionProps = defaultObj(actionProps); rest = Object.assign({},rest); rest["aria-label"] = defaultStr(rest["aria-label"],rest.title,rest.text,rest.label,rest.children); const {color} = getThemeColors(); rest.style = {...defaultObj(StyleSheet.flatten(actionProps.style)),elevation:0,...defaultObj(StyleSheet.flatten(rest.style))}; if(isAppBarActionStyle !== false && (isAppBarAction || opts.isAppBarAction)){ rest.color = defaultStr(color); rest.style.color = defaultVal(rest.style.color,color); } if(isAppBarAction && isMobile){ rest.tooltip = defaultVal(rest.title,rest.label,rest.text); delete rest.title; delete rest.label; delete rest.text; ActionComponent = Icon; } else { ActionComponent = Component || ActionComponent; if(!isAlert && rest.style.marginRight === undefined){ rest.style.marginRight = 10; } if(opts.isFullScreenDialog ===false){ rest.mode = rest.mode !== undefined ? rest.mode : actionProps.mode !==undefined ? actionProps.mode : 'contained'; } rest.children = defaultVal(rest.children,rest.label,rest.text,rest["aria-label"]); } if(isFormAction){ actionProps.componentProps = defaultObj(actionProps.componentProps) actionProps.componentProps.Component = ActionComponent; ActionComponent = Action; } key = key || action.key; return <ActionComponent {...actionProps} isAppBarAction={isAppBarAction} key = {key} {...rest}/> } /*** cette fonction a pour but de prendre les actions qui irons sur la barre de navigation * puis les subdivisenst en fonction de la taille, en une parties des actions directement visible dans le resultat actions * et une autre partie qui sera rendu visible par un menu, accessible via un menu moreVert * ///alwaysSplitOnMobile : PropoTypes.bool * @param alwaysSplitOnMobile {boolean}, si les actions serton toujours éclatées en un menu d'action en environnement mobile, quel qu'en soit le nombre d'actions trouvées * */ export const splitActions = (args)=>{ let {actions,actionProps,alwaysSplitOnMobile,actionMutator,isAlert,onAlertRequestClose,maxActions,isAppBarActionStyle,cancelButton,...opts} = defaultObj(args) if(isSplitedActions(actions)) { return actions; } if(!isObjOrArray(actions)){ return {actions:[],menus:[]}; } actionMutator = defaultFunc(actionMutator,({action,actions})=> action); if(isAlert){ onAlertRequestClose = defaultFunc(onAlertRequestClose); } opts = defaultObj(opts); const isMobile = isMobileOrTabletMedia() || typeof opts.windowWidth =="number" && opts.windowWidth < breakpoints.all.md; const isAppBarAction = opts.isAppBarAction && isMobile ? true : false; const ActionComponent = isAppBarAction ? Icon : Button; let menus = [] let _actions = []; let countActions = 0; if(maxActions !== 0 && typeof maxActions =='number'){ maxActions = maxActions > 0 ? Math.trunc(maxActions): getMaxActions(opts.windowWidth); } else if(maxActions !=0){ maxActions = getMaxActions(opts.windowWidth); } for(let i in actions){ let act = actions[i]; let cEl = null; if(!React.isValidElement(act)&& isPlainObj(act)){ let {label,perm,text,...action} = act; if(!isPermAllowed(perm,args)) { continue; } action = {...defaultObj(action)}; if(isAppBarAction && !action.icon) { console.log("not icon found for appbar action ",action," you must specity icon for this action"); continue; } action.key = defaultVal(action.key,i) action.text = defaultVal(text,label) action.isAlert = isAlert; const _action = actionMutator({action,key:action.key,actions}); if(isObj(_action)){ if(!_action.divider && !_action.text && !_action.label && !_action.title && !_action.icon && !_action.Component) continue; const {onPress} = _action; _action.onPress = (args)=>{ args = {...opts,...React.getOnPressArgs(args),isAlert}; if(isAlert){ args.closeAlert = args.closeDialog = onAlertRequestClose; } if(onPress){ return onPress(args); } return false; } cEl = _action; countActions++; } } if(cEl){ if(countActions >= maxActions+1) countActions = maxActions+1; if((countActions <= maxActions && maxActions >1)){ _actions.push(renderAction({actionProps,isAlert,isAppBarAction,isAppBarActionStyle,opts,action:cEl,isMenuItem:false,isMobile,ActionComponent})); } else { menus.push({...cEl,isMenuItem:true}); } } } if(isPlainObj(cancelButton) && !React.isValidElement(cancelButton)){ let {label,perm,text,...action} = cancelButton; const canAddCancelBtn = isPermAllowed(perm,{...args,cancelButton,action:cancelButton}); if(canAddCancelBtn && (text||label)){ action = {...defaultObj(action)}; action.label = defaultVal(label,text) action.key = defaultStr(action.key,'cancel-btn-action'); action.isAction = false; action.isAlert = isAlert; action = actionMutator({action,isAlert,isCancelButton:true,key:action.key}); if(isObj(action)){ const {onPress} = action; action.onPress = (args)=>{ args = {...opts,...React.getOnPressArgs(args),isAlert}; if(isAlert){ args.closeAlert = args.closeDialog = onAlertRequestClose; } if(onPress){ return onPress(args); } return false; } if(menus.length && countActions > 1){ menus.push({...action,isCancelButton:true,isMenuItem:true}); } else { _actions.push(<Button isCancelButton testID={'RN_AppBarCancelButton'} {...action} key={action.key}/>) } } } } alwaysSplitOnMobile = isMobileMedia()? alwaysSplitOnMobile : false; if(menus.length === 1 && (alwaysSplitOnMobile !== true)){ menus[0].isMenuItem = false; _actions.push(renderAction({actionProps,isAlert,action:menus[0],isAppBarAction,isAppBarActionStyle,opts,isMobile,itemsLength:1,key:"menu-action-"+menus[0].key,ActionComponent})); menus = []; } return { actions : _actions, menus }; } export const renderSplitedActions = (splitedActions,menuProps)=>{ menuProps = defaultObj(menuProps); const {withBottomSheet,BottomSheetComponent,...rest} = menuProps; const MenuComponent = withBottomSheet && React.isComponent(BottomSheetComponent) ? BottomSheetComponent : Menu; const rest2 = {},anchorProps = { ...defaultObj(menuProps.anchorProps), icon : MORE_ICON, }; if(withBottomSheet){ rest2.anchor = (props)=>{ return <Icon {...anchorProps} {...props} /> } } return isSplitedActions(splitedActions) && (splitedActions.actions.length || splitedActions.menus.length) ? <> {splitedActions.actions} {splitedActions.menus.length ? <MenuComponent testID = {"RN_AppBarMenuAnchor"} {...defaultObj(rest)} {...rest2} anchorProps = {anchorProps} items = {splitedActions.menus} /> : null} </> : null; }