UNPKG

@fto-consult/expo-ui

Version:

Bibliothèque de composants UI Expo,react-native

284 lines (270 loc) 9.63 kB
import React from '$react' import {useInputFormat,compareTwoDates,locale as defaultLocale,toDateObj} from "../utils"; import TextInputWithMask from './TextInputMask' import {useTheme } from 'react-native-paper' import i18n from "$i18n"; import {defaultStr} from "$cutils"; import DateLib from "$lib/date"; import PropTypes from "prop-types"; import TextField,{inputModes} from "$ecomponents/TextField"; import Icon from "$ecomponents/Icon"; import {StyleSheet,View} from "react-native"; import DatePickerModal from '../DatePickerModal' import PeriodActionComponent from "../PeriodAction"; const validMinDate = (date,minDate)=>{ if(!minDate || !date) return true; return date.withoutTime() >= minDate.withoutTime(); } const validMaxDate = (date,maxDate)=>{ if(!maxDate || !date) return true; return maxDate.withoutTime() >= date.withoutTime(); } const DatePickerInput = React.forwardRef(({ label, defaultValue, onChange, locale, inputMode, errorText, minimumDate, minDate, maxDate, maximumDate, withModal = true, withDateFormatInLabel = true, right:customRight, left : customLeft, helperText, format, displayFormat, disabled,readOnly ,text, withLabel, style, anchorProps, render_filter, isPeriodAction, isFilter, calendarIconBefore = false, //si l'icone calendar sera en position left où non ...rest },ref)=>{ let right = customRight,left = customLeft, isIconLeft = calendarIconBefore; if(!isPeriodAction){ isPeriodAction = isNonNullString(defaultValue) && defaultValue.contains("=>"); } if(isPeriodAction){ return <PeriodActionComponent {...rest} style = {style} left = {left} defaultValue = {defaultValue} right = {right} /> } inputMode = defaultStr(inputMode,"start"); locale = defaultStr(locale,defaultLocale); const theme = useTheme() const [state,setState] = React.useState({ errorText : null, inputDate : toDateObj(defaultValue,format), visible : false }) const inputFormat = useInputFormat(locale) const inputFormatLabel = i18n.lang(inputFormat); const prevInputDate = React.usePrevious(state.inputDate,compareTwoDates) const formattedValue = !state.inputDate ? undefined : DateLib.format(state.inputDate,inputFormat.toLowerCase()); const onDismiss = () => { setState({...state,visible:false}); } minDate = defaultVal(minDate,minimumDate); maxDate = defaultVal(maxDate,maximumDate); if(maxDate){ maxDate = toDateObj(maxDate); maxDate = maxDate && DateLib.isDateObj(maxDate)? maxDate : undefined; } if(minDate){ minDate = toDateObj(minDate); minDate = minDate && DateLib.isDateObj(minDate)? minDate : undefined; } const isEditable = disabled !== true && readOnly !== true ?true : false; withModal = defaultBool(withModal,true); if(!isEditable){ withModal = false; } const validateDate = (date)=>{ let errorText = null,error = false; let lowerInput = inputFormat.toLowerCase(); const dFormat = DateLib.format(date,lowerInput); if(!validMinDate(date,minDate)){ errorText = "la date ["+dFormat+"] doit être plus récente que la date minimale ["+DateLib.format(minDate,lowerInput)+"]"; error = true; } if(!validMaxDate(date,maxDate)){ errorText = "la date ["+dFormat+"] doit être moins ancienne que la date maximale ["+DateLib.format(maxDate,lowerInput)+"]"; error = true; } return {error,errorText}; } React.useEffect(()=>{ const inputDate = toDateObj(defaultValue,format); if(compareTwoDates(inputDate,prevInputDate)) return; setState({...state,inputDate}); },[defaultValue,format]); const setEmptyValue = ()=>{ setState({...state,inputDate:undefined}); } rest = defaultObj(rest); anchorProps = defaultObj(anchorProps); label = defaultStr(label,text); customRight = React.isValidElement(customRight) || typeof customRight =='function'? customRight : null; if(withModal){ const customLOrR = isIconLeft ? customLeft : customRight; const leftOrRight = (props)=>{ let c = typeof customLOrR =='function'? customLOrR(props): customLOrR; c = React.isValidElement(c)? c : null; return <> {isIconLeft ? c : null} <Icon {...anchorProps} {...props} icon="calendar" style = {[anchorProps.style,right?styles.noPadding:null,props.style]} onPress={() => { setState({...state,visible:true}); }} hasTVPreferredFocus={undefined} tvParallaxProperties={undefined} /> {!isIconLeft ? c : null} </> } if(isIconLeft){ left = leftOrRight; } else { right = leftOrRight; } } const onConfirm = (date,updateVisibility) => { const vDate = validateDate(date); const inputDate = !vDate.error ? date : state.inputDate; let visible = state.visible; if(updateVisibility !== false){ visible = false; } setState(({...state,visible,inputDate,errorText:vDate.errorText})); } const hasError = errorText || state.errorText ? true : false; React.useEffect(()=>{ if(compareTwoDates(state.inputDate,prevInputDate) || state.errorText) return; if(onChange){ const date = state.inputDate ? DateLib.toSQLDate(state.inputDate): undefined; onChange({dateObject:state.inputDate,date:state.inputDate,sqlDate:date,value:date}) } },[state]) const labelText = withLabel === false ? null: (render_filter ? label : getLabel({ label, inputFormat:inputFormatLabel, withDateFormatInLabel })); return ( <> <TextField affix = {false} {...rest} style = {[styles.input,style]} readOnly = {!isEditable} disabled = {disabled} left = {left} right = {right} pointerEvents = {isEditable?"auto":"none"} ref={ref} label={labelText} defaultValue={formattedValue} placeholder={inputFormatLabel} inputMode={inputModes.number} mask={inputFormat} keyboardAppearance={theme.dark ? 'dark' : 'default'} error={hasError} helperText = {state.errorText || errorText} render = {(inputProps)=>{ return <TextInputWithMask {...inputProps} locale = {locale} placeholder={inputFormatLabel} value = {formattedValue} style = {[inputProps.style,styles.input,style]} onChangeText={(date) => { if(!date){ return setEmptyValue(); } const dayIndex = inputFormat.indexOf('DD') const monthIndex = inputFormat.indexOf('MM') const yearIndex = inputFormat.indexOf('YYYY') const day = Number(date.slice(dayIndex, dayIndex + 2)) const year = Number(date.slice(yearIndex, yearIndex + 4)) const month = Number(date.slice(monthIndex, monthIndex + 2)) if (Number.isNaN(day) || Number.isNaN(year) || Number.isNaN(month)) { setState({...state,errorText:i18n.lang('notAccordingToDateFormat',undefined,locale)(inputFormat)}) return } const inputDate = inputMode === 'end' ? new Date(year, month - 1, day, 23, 59, 59) : new Date(year, month - 1, day) onConfirm(inputDate,false) }} /> }} /> {withModal ? ( <DatePickerModal date={state.inputDate} visible={state.visible} mode="single" startDate = {minDate} endDate = {maxDate} validRange={{ startDate : minDate, // optional endDate : maxDate, // optional // disabledDates: [new Date()] // optional }} onDismiss={onDismiss} onDateChange = {(date)=>{ onConfirm(date); }} onConfirm={({ date }) => { onConfirm(date); }} onCancel = {onDismiss} locale={locale} dateMode={inputMode} /> ) : null} </> ) }); function getLabel({ withDateFormatInLabel,inputFormat,label}) { inputFormat = i18n.lang(inputFormat); if (withDateFormatInLabel) { return label ? `${label} (${inputFormat})` : inputFormat } return label || '' } export default DatePickerInput; DatePickerInput.displayName = "DatePickerInput"; const styles = StyleSheet.create({ container : { position: 'relative', //flexGrow: 1, //flex:1, }, noPadding : { paddingHorizontal : 0, marginHorizontal:0, }, input : { paddingVertical : 0, width : '100%' } }) DatePickerInput.propTypes = { onChange : PropTypes.func, anchorProps : PropTypes.object,///les props à appliquer à l'icone date permettant de sélectionner la date calendarIconBefore : PropTypes.bool,///la position de l'icone calendrier pour la sélection de la date }