Skip to content

Commit 13d851d

Browse files
author
ricwilson
committed
feat: enhance useImageSrc hook to apply default crop on image load and streamline image source tracking
1 parent b552dff commit 13d851d

File tree

2 files changed

+54
-24
lines changed

2 files changed

+54
-24
lines changed

ImageCrop/ImageCrop/components/imageCropControl.tsx

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,31 +52,17 @@ const ImageCropControl: React.FC<IImageCropControlProps> = (props) => {
5252
const [aspect] = useAspect(pcfContext.context, imgRef, setCrop);
5353
// Get the keepSelection property from PCF context
5454
const keepSelection = useKeepSelection(pcfContext.context);
55+
// Get the default crop object (not a hook)
56+
const defaultCrop = useDefaultCrop(pcfContext.context);
5557
// Get the image from the PCF context property (should be base64)
56-
const imageSrc = useImageSrc(pcfContext.context);
58+
const imageSrc = useImageSrc(pcfContext.context, imgRef, defaultCrop, setCrop, setCompletedCrop);
5759
// Get the rotation property from PCF context
5860
const rotation = useRotation(pcfContext.context);
5961
// Get the scaling property from PCF context
6062
const scaling = useScaling(pcfContext.context);
61-
// Get the default crop object (not a hook)
62-
const defaultCrop = useDefaultCrop(pcfContext.context);
6363
// Use custom hook to handle crop-to-base64 conversion and callback
6464
useCropToBase64(imgRef, completedCrop, props.onCropComplete, rotation, scaling, circularCrop);
6565

66-
// Reset imageLoaded state and crop when imageSrc changes
67-
React.useEffect(() => {
68-
handleImageSrcChange(imageSrc);
69-
setCrop(undefined);
70-
}, [imageSrc]);
71-
72-
// Set crop to default when image is loaded and crop is undefined
73-
React.useEffect(() => {
74-
if (imageLoaded && !crop && defaultCrop) {
75-
setCrop(defaultCrop);
76-
setCompletedCrop(convertToPixelCrop(defaultCrop, imgRef.current.width, imgRef.current.height));
77-
}
78-
}, [imageLoaded]);
79-
8066
return (
8167
<CropWrapper
8268
crop={crop}
Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,68 @@
11
import { useState, useEffect } from "react";
22
import { IInputs } from "../generated/ManifestTypes";
3+
import { Crop, PixelCrop, convertToPixelCrop } from "react-image-crop";
34

45
function stripQuotes(str: string): string {
5-
if (str && str.length > 1 && ((str.startsWith('"') && str.endsWith('"')) || (str.startsWith("'") && str.endsWith("'")))) {
6+
if (
7+
str &&
8+
str.length > 1 &&
9+
((str.startsWith('"') && str.endsWith('"')) ||
10+
(str.startsWith("'") && str.endsWith("'")))
11+
) {
612
return str.substring(1, str.length - 1);
713
}
814
return str;
915
}
1016

1117
/**
12-
* Custom hook to track the image source from PCF context and update when imageInput changes.
18+
* Custom hook to track the image source and automatically apply default crop
19+
* when the image is loaded.
20+
*
1321
* @param context The PCF context object
14-
* @returns The current image source (base64 or URL)
22+
* @param imgRef Ref to the HTMLImageElement
23+
* @param defaultCrop The default crop to apply
24+
* @param setCrop Setter to apply crop
25+
* @param setCompletedCrop Setter to apply completed pixel crop
26+
* @returns The current image source string
1527
*/
16-
export function useImageSrc(context: ComponentFramework.Context<IInputs>): string {
17-
const [imageSrc, setImageSrc] = useState<string>(stripQuotes(context.parameters.imageInput?.raw || ""));
28+
export function useImageSrc(
29+
context: ComponentFramework.Context<IInputs>,
30+
imgRef: React.RefObject<HTMLImageElement>,
31+
defaultCrop: Crop | undefined,
32+
setCrop: (crop: Crop) => void,
33+
setCompletedCrop: (crop: PixelCrop) => void
34+
): string {
35+
const rawSrc = stripQuotes(context.parameters.imageInput?.raw || "");
36+
const [imageSrc, setImageSrc] = useState<string>(rawSrc);
1837

1938
useEffect(() => {
20-
setImageSrc(stripQuotes(context.parameters.imageInput?.raw || ""));
21-
}, [context.parameters.imageInput?.raw]);
39+
const cleanSrc = stripQuotes(context.parameters.imageInput?.raw || "");
40+
setImageSrc(cleanSrc);
41+
42+
const img = imgRef.current;
43+
44+
if (!img || !defaultCrop || !cleanSrc) return;
45+
46+
const applyCropIfReady = () => {
47+
if (img.complete && img.naturalWidth > 0) {
48+
setCrop(defaultCrop);
49+
setCompletedCrop(convertToPixelCrop(defaultCrop, img.width, img.height));
50+
}
51+
};
52+
53+
// Try to apply immediately
54+
applyCropIfReady();
55+
56+
// Fallback: wait for image load if needed
57+
const onLoad = () => {
58+
applyCropIfReady();
59+
};
60+
61+
img.addEventListener("load", onLoad);
62+
return () => {
63+
img.removeEventListener("load", onLoad);
64+
};
65+
}, [context.parameters.imageInput?.raw, defaultCrop]);
2266

2367
return imageSrc;
2468
}

0 commit comments

Comments
 (0)