|
1 | 1 | import { useState, useEffect } from "react";
|
2 | 2 | import { IInputs } from "../generated/ManifestTypes";
|
| 3 | +import { Crop, PixelCrop, convertToPixelCrop } from "react-image-crop"; |
3 | 4 |
|
4 | 5 | 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 | + ) { |
6 | 12 | return str.substring(1, str.length - 1);
|
7 | 13 | }
|
8 | 14 | return str;
|
9 | 15 | }
|
10 | 16 |
|
11 | 17 | /**
|
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 | + * |
13 | 21 | * @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 |
15 | 27 | */
|
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); |
18 | 37 |
|
19 | 38 | 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]); |
22 | 66 |
|
23 | 67 | return imageSrc;
|
24 | 68 | }
|
0 commit comments