Files
apparel-designer/src/components/editor/PhotoPreEditor.jsx

96 lines
3.7 KiB
JavaScript

import { useEffect, useRef } from 'react';
import FilerobotImageEditor, { TABS } from 'react-filerobot-image-editor';
import { StyleSheetManager } from 'styled-components';
import isPropValid from '@emotion/is-prop-valid';
import '../../styles/PhotoPreEditor.css';
export function PhotoPreEditor({ imageSrc, onComplete, onClose }) {
const modalContentRef = useRef(null);
const previousFocusRef = useRef(null);
useEffect(() => {
previousFocusRef.current = document.activeElement;
const handleKeyDown = (e) => { if (e.key === 'Escape') onClose(); };
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
previousFocusRef.current?.focus();
};
}, [onClose]);
const base64ToBlob = (base64DataUrl) => {
const [header, data] = base64DataUrl.split(',');
const mimeMatch = header?.match(/data:(.*?);base64/);
const mime = mimeMatch?.[1] || 'image/png';
const binary = atob(data || '');
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
return new Blob([bytes], { type: mime });
};
const handleSave = async (savedImageData) => {
try {
// Prefer base64 when available (works without CORS/network).
if (savedImageData?.imageBase64) {
const blob = base64ToBlob(savedImageData.imageBase64);
onComplete(URL.createObjectURL(blob));
return;
}
// Fallback to canvas when provided by the library.
if (savedImageData?.imageCanvas instanceof HTMLCanvasElement) {
const blob = await new Promise((resolve) =>
savedImageData.imageCanvas.toBlob(resolve, savedImageData.mimeType || 'image/png', savedImageData.quality),
);
if (blob) onComplete(URL.createObjectURL(blob));
else throw new Error('Canvas export failed');
return;
}
// Final fallback: cloudimageUrl (fetch then blob).
if (savedImageData?.cloudimageUrl) {
const res = await fetch(savedImageData.cloudimageUrl);
const blob = await res.blob();
onComplete(URL.createObjectURL(blob));
return;
}
throw new Error('No export data returned from image editor');
} catch (error) {
console.error('Export failed:', error);
onClose();
}
};
return (
<div className="filerobot-overlay" role="dialog" aria-modal="true" aria-labelledby="photo-editor-title">
<div className="filerobot-container" ref={modalContentRef} role="document">
<StyleSheetManager
// Filerobot/@scaleflex styled-components pass a bunch of styling props to DOM nodes (e.g. isPhoneScreen).
// Filtering them here prevents noisy React console warnings.
shouldForwardProp={(prop, element) => (typeof element === 'string' ? isPropValid(prop) : true)}
>
<FilerobotImageEditor
source={imageSrc}
onBeforeSave={() => false}
onSave={handleSave}
onClose={() => onClose()}
tabsIds={[TABS.ADJUST, TABS.FILTERS, TABS.FINETUNE]}
defaultTabId={TABS.ADJUST}
Crop={{ autoResize: true, defaultSizePercentage: 1, ratio: 'custom' }}
theme={{ accentColor: '#38bdf8', palettePrimary: '#38bdf8' }}
forceToPngInEllipticalCrop
closeAfterSave
defaultSavedImageName="edited-image"
defaultSavedImageType="png"
defaultSavedImageQuality={1}
savingPixelRatio={4}
previewPixelRatio={4}
/>
</StyleSheetManager>
</div>
<h2 id="photo-editor-title" className="sr-only">Photo Editor</h2>
</div>
);
}