@fto-consult/expo-ui
Version:
Bibliothèque de composants UI Expo,react-native
308 lines (297 loc) • 11.8 kB
JavaScript
import {Image,View} from "react-native";
import Menu from "$ecomponents/Menu";
import Avatar from "$ecomponents/Avatar";
import {isDecimal,setQueryParams,isValidURL,defaultDecimal,defaultStr as defaultString,isDataURL,isPromise,defaultBool,isObj,isNonNullString} from "$cutils";
import notify from "$enotify";
import {StyleSheet} from "react-native";
import React from "$react";
import PropTypes from "prop-types";
import {isMobileNative} from "$cplatform";
import {getUri} from "./utils";
import {uniqid} from "$cutils";
//import Signature from "$ecomponents/Signature";
import Label from "$ecomponents/Label";
//import Cropper from "./Cropper";
import {pickImage,nonZeroMin,canTakePhoto,takePhoto} from "$emedia";
import addPhoto from "$eassets/add_photo.png";
let maxWidthDiff = 100, maxHeightDiff = 100;
export * from "./utils";
export default function ImageComponent(props){
const [src,setSrc] = React.useState(defaultVal(props.src));
const [cropWindowProp,setCropWindowProp] = React.useState(null);
const prevSrc = React.usePrevious(src);
const [isDrawing,setIsDrawing] = React.useState(false);
let {disabled,onMount,defaultSource,editable,onUnmount,label,text,labelProps,readOnly,beforeRemove,
onChange,draw,round,drawText,drawLabel,rounded,defaultSrc,
createSignatureOnly,pickImageProps,width,height,cropProps,size,resizeProps,containerProps,
menuProps,pickUri,drawProps,imageProps,length,testID,withLabel,...rest} = props;
const pickedImageRef = React.useRef(null);
pickImageProps = defaultObj(pickImageProps);
cropProps = defaultObj(cropProps);
draw = defaultBool(draw,true);
label = defaultVal(label,text);
labelProps = defaultObj(labelProps);
disabled = defaultVal(disabled,false);
readOnly = defaultBool(readOnly,false);
menuProps = defaultObj(menuProps);
rounded = defaultBool(rounded,round,true);
containerProps = defaultObj(containerProps);
drawProps = defaultObj(drawProps);
const flattenStyle = StyleSheet.flatten(props.style) || {};
defaultSrc = defaultVal(defaultSrc);
if(disabled){
readOnly = true;
}
if(editable ===false){
readOnly = true;
}
React.useEffect(()=>{
if(src == props.src) return;
setSrc(props.src);
},[props.src])
if(!isDecimal(width) && isDecimal(flattenStyle.width)){
width = flattenStyle.width;
}
if(!isDecimal(height) && isDecimal(flattenStyle.height)){
height = flattenStyle.height;
}
if(isDecimal(width) && width > 0){
rest.width = width;
}
if(isDecimal(height) && height > 0){
rest.height = height;
}
if(isDecimal(size) && size > 10){
rest.size = Math.trunc(size);
} else {
const sZize = Math.min(defaultDecimal(width),defaultDecimal(height));
rest.size = sZize >= 10 ? sZize : 50;
}
let imageWidth, imageHeight;
if(isDecimal(width) && width){
imageWidth = width
}
if(isDecimal(height) && height){
imageHeight = height;
}
if(!imageWidth && !imageHeight){
imageWidth = imageHeight = rest.size;
}
let cropWidth = nonZeroMin(cropProps.width,imageWidth,width,size)
let cropHeight = nonZeroMin(cropProps.height,imageHeight,height,size);
if(!cropWidth) cropWidth = undefined;
if(!cropHeight) cropHeight = undefined;
const getCropProps = (opts)=>{
opts = defaultObj(opts);
let canCrop = defaultBool(opts.allowsEditing,true);
if(cropWidth || cropHeight){
canCrop = true;
if(cropWidth) opts.width = cropWidth;
if(cropHeight) opts.height = cropHeight;
}
opts.allowsEditing = canCrop;
return {...cropProps,...opts};
}
const handlePickedImage = (image,opts)=>{
opts = defaultObj(opts);
if(!isDataURL(image.dataURL)){
return notify.error(`Le fichier sélectionné est une image non valide`);
}
const imageSrc = pickUri ? image.uri : image.dataURL;
if(imageSrc){
const diffWidth = image.width - cropWidth - maxWidthDiff,diffHeight = image.height - cropHeight - maxHeightDiff;
const canCrop = isMobileNative()? false : ((diffWidth > 0) || (diffHeight > 0)? true : false);
if(canCrop){
const cProps = getCropProps(opts);
return context.cropImage({...cProps,source:image,uri:image.dataURL,src:imageSrc}).then((props)=>{
setSrc(imageSrc)
});
}
}
pickedImageRef.current = image;
setSrc(imageSrc);
return image;
}
const context = {
setSrc,
deleteImage : React.useCallback(()=>{
if(typeof beforeRemove =='function'){
const r = beforeRemove({context});
if(r === false) return;
const cb = (r)=>{
if(isNonNullString(r)){
notify.error(r);
return false;
}
setSrc(null);
}
if(isPromise(r)){
return r.then(cb);
} else cb();
} else {
setSrc(null);
}
},[src]),
cropImage : (props)=>{
return Promise.resolve(props);
if(!isMobileNative()){
return new Promise((resolve,reject)=>{
setCropWindowProp(props);
});
}
return new Promise((resolve,reject)=>{
console.log({...editorProps,visible:true,...props},"is editor props");
setEditorProps({...editorProps,visible:true,...props})
})
},
pickImage : ()=>{
const opts = getCropProps(defaultObj(pickImageProps));
opts.base64 = true;
return pickImage(opts).then((image)=>handlePickedImage(image,opts));
},
draw : ()=>{
setIsDrawing(true);
},
}
React.useEffect(()=>{
if(src === prevSrc) {
pickedImageRef.current = null;
return;
}
if(typeof onChange =='function'){
onChange({context,...defaultObj(pickedImageRef.current),src,deleted:src == null?true:false,dataURL:src,dataUrl:src})
}
pickedImageRef.current = null;
},[src]);
React.useEffect(()=>{
if(typeof onMount =='function'){
onMount({context});
}
return ()=>{
if(typeof onUnmount =='function'){
onUnmount({context});
}
}
},[])
let defaultURI = getUri(defaultVal(defaultSource,defaultSrc));
if(!defaultURI){
defaultURI = getUri(addPhoto);
}
let uri = getUri(src) || defaultURI
let canUpdate = uri !== defaultURI;
if(isValidURL(uri)){
uri = setQueryParams(uri,'cache',(new Date()).getTime());
}
let source = isNumber(uri)? uri : isObj(uri)? uri : {uri};
if(isObj(src)){
source = {...src,...source};
}
imageProps = defaultObj(imageProps);
testID = defaultStr(testID,"RN_ImageComponent");
let menuItems = []
if(!readOnly){
menuItems.push({
label : 'Sélect Image',
icon :'image-search',
onPress : (a)=>{
context.pickImage();
}
})
}
if(isMobileNative() && !readOnly){
menuItems.push({
label : 'Enregistrer une photo',
icon : 'camera',
onPress : (a)=>{
const opts = getCropProps(defaultObj(pickImageProps));
opts.base64 = true;
takePhoto(opts).then(handlePickedImage);
}
})
}
if(canUpdate && !readOnly){
menuItems.push({
key : 'has-photo',
label : 'Retirer la photo',
icon : "image-off",
onPress : x=> context.deleteImage()
})
}
if(false && defaultBool(draw ,true) && !readOnly){
menuItems.push({
key : "drawImageCustom",
label : defaultString(drawText,drawLabel,'Faire un dessin'),
icon : "signature-image",
onPress : (c)=>{
context.draw(c);
console.log(c," pressed ")
}
})
}
const _label = withLabel !== false ? defaultString(label) : "";
const isDisabled = menuItems.length > 0 ? true : false;
return <View testID={testID+"_FagmentContainer"}>
{false && src && !isMobileNative() && isObj(cropWindowProp) && Object.size(cropWindowProp,true) ? <Cropper
src={src}
{...cropWindowProp}
key = {uniqid("crop-image")}
/> : null}
{!createSignatureOnly ? (<Menu
{...menuProps}
disabled = {isDisabled}
anchor = {(props)=>{
return <View aria-label={_label} testID={testID+"_Container"} {...containerProps} style={[label?styles.align:null,containerProps.style,{pointerEvents:disabled|| readOnly? "none":"auto"},label?styles.container:null]}>
{withLabel !== false ? <Label testID={testID+"_Label"} {...labelProps} disabled={disabled} style={[styles.label,labelProps.style]}>{label}</Label>:null}
{<Avatar
resizeMethod = {"auto"}
resizeMode = {"contain"}
{...rest}
testID = {testID}
width = {imageWidth}
height = {imageHeight}
imageProps = {imageProps}
{...props}
style = {[rest.style]}
rounded = {rounded}
image = {true}
source = {source}
/>}
</View>
}}
items = {menuItems}
/>) : null}
{false && <Signature testID={testID+"_Signature"} visible = {isDrawing} dialogProps = {{
onDismiss :()=>{
setIsDrawing(false);
return false;
}
}} />}
</View>
}
const styles = StyleSheet.create ({
align : {
alignItems : 'center',
justifyContent : 'flex-start',
},
container : {
flexDirection:'row',
justifyContent : 'flex-start',
alignItems : 'center',
width : '100%'
//flex:1,
},
label: {
marginRight : 5,
},
})
ImageComponent.propTypes = {
containerProps : PropTypes.object,//les props du container entre le lable et l'image rendu
menuProps : PropTypes.object, ///les props du menu d'édition du composant,
readOnly : PropTypes.bool,
disabled: PropTypes.bool,
editable : PropTypes.bool,//si la source de l'image peut être modifiée, via le menu Sélectionner une image ou prendre une photo en fonction de la plateforme
pickUri : PropTypes.bool,////si l'uri sera retournée lorsqu'on pick l'image en lieu et place du dataURL
imageProps : PropTypes.object, ///les props supplémentaires du composant Image
draw : PropTypes.bool, //si l'on peut déssiner une image
pickImageProps : PropTypes.object, ///les options à passer à la fonction pickImage, pour récupérer une image enregistrée en machine
}