Просмотр исходного кода

Add option to crop logo

https://youtrack.weseek.co.jp/issue/GW-7876
- Add isShouldCrop prop type
- Add method to convert base64 to blob
- Modify crop method based on isCropImage state
- Add conditional rendering of Image crop according to isCropImage value
- Show toggle checkbox of crop option on Image crop Modal
- Update crop/save translation according isCropImage value
- Add onModalCloseHandler  method to clear uploadedLogoSrc and close modal
- Call onModalCloseHandler on crop completed
- Implement onModalCloseHandler
- Adjust props of CustomizeLogoSetting and ProfileImageSettings component
Mudana-Grune 3 лет назад
Родитель
Сommit
7c871dbaa4

+ 1 - 0
packages/app/public/static/locales/en_US/translation.json

@@ -1088,6 +1088,7 @@
   "crop_image_modal": {
     "image_crop": "Image Crop",
     "crop": "Crop",
+    "save": "Save",
     "reset": "Reset",
     "cancel": "Cancel"
   },

+ 1 - 0
packages/app/public/static/locales/ja_JP/translation.json

@@ -1081,6 +1081,7 @@
   "crop_image_modal": {
     "image_crop": "画像の切り抜き",
     "crop": "トリミング",
+    "save": "保存",
     "reset": "リセット",
     "cancel": "キャンセル"
   },

+ 1 - 0
packages/app/public/static/locales/zh_CN/translation.json

@@ -1091,6 +1091,7 @@
   "crop_image_modal": {
     "image_crop": "图像裁剪",
     "crop": "修剪",
+    "save": "节省",
     "reset": "重启",
     "cancel": "取消"
   },

+ 10 - 4
packages/app/src/components/Admin/Customize/CustomizeLogoSetting.tsx

@@ -80,6 +80,12 @@ const CustomizeLogoSetting = (): JSX.Element => {
     }
   }, [t]);
 
+  // Clear uploadedLogoSrc and close modal
+  const onModalCloseHandler = useCallback(() => {
+    setIsImageCropModalShow(false);
+    setUploadLogoSrc(null);
+  }, []);
+
   const onCropCompleted = useCallback(async(croppedImage) => {
     try {
       const formData = new FormData();
@@ -93,9 +99,8 @@ const CustomizeLogoSetting = (): JSX.Element => {
       setRetrieveError(err);
       throw new Error('Failed to upload brand logo');
     }
-    setIsImageCropModalShow(false);
-  }, [t]);
-
+    onModalCloseHandler();
+  }, [t, onModalCloseHandler]);
 
   return (
     <React.Fragment>
@@ -171,9 +176,10 @@ const CustomizeLogoSetting = (): JSX.Element => {
       <ImageCropModal
         isShow={isImageCropModalShow}
         src={uploadLogoSrc}
-        onModalClose={() => setIsImageCropModalShow(false)}
+        onModalClose={onModalCloseHandler}
         onCropCompleted={onCropCompleted}
         isCircular={false}
+        isShouldCrop={false}
       />
     </React.Fragment>
   );

+ 39 - 4
packages/app/src/components/Common/ImageCropModal.tsx

@@ -35,15 +35,17 @@ type Props = {
   onModalClose: () => void,
   onCropCompleted: (res: any) => void,
   isCircular: boolean,
+  isShouldCrop: boolean
 }
 const ImageCropModal: FC<Props> = (props: Props) => {
 
   const {
-    isShow, src, onModalClose, onCropCompleted, isCircular,
+    isShow, src, onModalClose, onCropCompleted, isCircular, isShouldCrop,
   } = props;
 
   const [imageRef, setImageRef] = useState<HTMLImageElement>();
   const [cropOptions, setCropOtions] = useState<CropOptions>(null);
+  const [isCropImage, setIsCropImage] = useState<boolean>(true);
   const { t } = useTranslation();
   const reset = useCallback(() => {
     if (imageRef) {
@@ -93,10 +95,22 @@ const ImageCropModal: FC<Props> = (props: Props) => {
     }
   };
 
+  // Convert base64 Image to blob
+  const convertBase64ToBlob = async(base64Image: string) => {
+    const parts = base64Image.split(';base64,');
+    const imageType = parts[0].split(':')[1];
+    const decodedData = window.atob(parts[1]);
+    const uInt8Array = new Uint8Array(decodedData.length);
+    for (let i = 0; i < decodedData.length; ++i) {
+      uInt8Array[i] = decodedData.charCodeAt(i);
+    }
+    return new Blob([uInt8Array], { type: imageType });
+  };
+
   const crop = async() => {
     // crop immages
     if (imageRef && cropOptions?.width && cropOptions.height) {
-      const result = await getCroppedImg(imageRef, cropOptions);
+      const result = isCropImage ? await getCroppedImg(imageRef, cropOptions) : await convertBase64ToBlob(imageRef.src);
       onCropCompleted(result);
     }
   };
@@ -107,17 +121,38 @@ const ImageCropModal: FC<Props> = (props: Props) => {
         {t('crop_image_modal.image_crop')}
       </ModalHeader>
       <ModalBody className="my-4">
-        <ReactCrop src={src} crop={cropOptions} onImageLoaded={onImageLoaded} onChange={onCropChange} circularCrop={isCircular} />
+        {
+          isCropImage
+            ? (<ReactCrop src={src} crop={cropOptions} onImageLoaded={onImageLoaded} onChange={onCropChange} circularCrop={isCircular} />)
+            : (<img style={{ maxWidth: imageRef?.width }} src={imageRef?.src} />)
+        }
       </ModalBody>
       <ModalFooter>
         <button type="button" className="btn btn-outline-danger rounded-pill mr-auto" onClick={reset}>
           {t('crop_image_modal.reset')}
         </button>
+        { !isShouldCrop && (
+          <div className="mr-auto">
+            <div className="custom-control custom-switch ">
+              <input
+                id="cropImageOption"
+                className="custom-control-input mr-auto"
+                type="checkbox"
+                checked={isCropImage}
+                onChange={() => { setIsCropImage(!isCropImage) }}
+              />
+              <label className="custom-control-label" htmlFor="cropImageOption">
+                { t('crop_image_modal.image_crop') }
+              </label>
+            </div>
+          </div>
+        )
+        }
         <button type="button" className="btn btn-outline-secondary rounded-pill mr-2" onClick={onModalClose}>
           {t('crop_image_modal.cancel')}
         </button>
         <button type="button" className="btn btn-outline-primary rounded-pill" onClick={crop}>
-          {t('crop_image_modal.crop')}
+          { isCropImage ? t('crop_image_modal.crop') : t('crop_image_modal.save') }
         </button>
       </ModalFooter>
     </Modal>

+ 1 - 0
packages/app/src/components/Me/ProfileImageSettings.tsx

@@ -160,6 +160,7 @@ const ProfileImageSettings = (): JSX.Element => {
         onModalClose={() => setShowImageCropModal(false)}
         onCropCompleted={cropCompletedHandler}
         isCircular
+        isShouldCrop
       />
 
       <div className="row my-3">