costco-grocery-list/frontend/src/components/forms/ImageUploadSection.jsx

99 lines
3.0 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useRef, useState, useContext } from "react";
import { ConfigContext } from "../../context/ConfigContext";
import "../../styles/components/ImageUploadSection.css";
/**
* Reusable image upload component with camera and gallery options
* @param {Object} props
* @param {string} props.imagePreview - Base64 preview URL or null
* @param {Function} props.onImageChange - Callback when image is selected (file)
* @param {Function} props.onImageRemove - Callback to remove image
* @param {string} props.title - Section title (optional)
*/
export default function ImageUploadSection({
imagePreview,
onImageChange,
onImageRemove,
title = "Item Image (Optional)"
}) {
const cameraInputRef = useRef(null);
const galleryInputRef = useRef(null);
const [sizeError, setSizeError] = useState(null);
const { config } = useContext(ConfigContext);
const MAX_FILE_SIZE = config ? config.maxFileSizeMB * 1024 * 1024 : 20 * 1024 * 1024;
const MAX_FILE_SIZE_MB = config ? config.maxFileSizeMB : 20;
const handleFileChange = (e) => {
const file = e.target.files[0];
if (file) {
// Check file size
if (file.size > MAX_FILE_SIZE) {
const sizeMB = (file.size / (1024 * 1024)).toFixed(2);
setSizeError(`Image size (${sizeMB}MB) exceeds the ${MAX_FILE_SIZE_MB}MB limit. Please choose a smaller image.`);
// Reset the input
e.target.value = '';
return;
}
// Clear any previous error
setSizeError(null);
onImageChange(file);
}
};
const handleCameraClick = () => {
cameraInputRef.current?.click();
};
const handleGalleryClick = () => {
galleryInputRef.current?.click();
};
return (
<div className="image-upload-section">
<h3 className="image-upload-title">{title}</h3>
{sizeError && (
<div className="image-upload-error">
{sizeError}
</div>
)}
<div className="image-upload-content">
{!imagePreview ? (
<div className="image-upload-options">
<button onClick={handleCameraClick} className="image-upload-btn camera" type="button">
📷 Use Camera
</button>
<button onClick={handleGalleryClick} className="image-upload-btn gallery" type="button">
🖼 Choose from Gallery
</button>
</div>
) : (
<div className="image-upload-preview">
<img src={imagePreview} alt="Preview" />
<button type="button" onClick={onImageRemove} className="image-upload-remove">
× Remove
</button>
</div>
)}
</div>
<input
ref={cameraInputRef}
type="file"
accept="image/*"
capture="environment"
onChange={handleFileChange}
style={{ display: "none" }}
/>
<input
ref={galleryInputRef}
type="file"
accept="image/*"
onChange={handleFileChange}
style={{ display: "none" }}
/>
</div>
);
}