Fix module issues, fix styling, add conditions to when the background removal and edit controls are shown
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import FilerobotImageEditor from 'react-filerobot-image-editor';
|
||||
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 [saving, setSaving] = useState(false);
|
||||
const modalContentRef = useRef(null);
|
||||
const previousFocusRef = useRef(null);
|
||||
|
||||
@@ -16,28 +18,78 @@ export function PhotoPreEditor({ imageSrc, onComplete, onClose }) {
|
||||
};
|
||||
}, [onClose]);
|
||||
|
||||
const handleComplete = (editedImageObject) => {
|
||||
setSaving(true);
|
||||
editedImageObject.exportAsync({ quality: 1, mimeType: 'image/png' })
|
||||
.then((blob) => { setSaving(false); onComplete(URL.createObjectURL(blob)); })
|
||||
.catch((error) => { console.error('Export failed:', error); setSaving(false); 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">
|
||||
<FilerobotImageEditor
|
||||
source={imageSrc} onSave={handleComplete} onClose={onClose}
|
||||
annotationsCommon={{ fill: '#ff0000', stroke: '#000000', strokeWidth: 0 }}
|
||||
annotations={['Text', 'Rectangle', 'Ellipse', 'Line', 'Pen', 'Eraser']}
|
||||
tabs={['adjust', 'filters', 'finetune', 'annotate', 'watermark']}
|
||||
defaultTabId="adjust"
|
||||
theme={{ accentColor: '#38bdf8', palettePrimary: '#38bdf8' }}
|
||||
saveButtonProps={{ label: saving ? 'Exporting...' : 'Use Edited Image' }}
|
||||
closeOnSave
|
||||
/>
|
||||
<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" style={{ position: 'absolute', width: 1, height: 1, overflow: 'hidden', clip: 'rect(0,0,0,0)' }}>Photo Editor</h2>
|
||||
<h2 id="photo-editor-title" className="sr-only">Photo Editor</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user