Flatten transparent product images to white and allow cropper unzoom.
PNG/WebP uploads no longer bake to black on JPEG; product croppers can zoom out with white padding, and SEO tools move into the product overflow menu. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
b743c09e4d
commit
06e2442aaf
@@ -11,6 +11,11 @@ interface ImageCropperProps {
|
||||
/** Crop aspect ratio. Defaults to square (1). */
|
||||
aspect?: number
|
||||
outputFormat?: 'jpeg' | 'png'
|
||||
/** Fill color for JPEG and for crop areas outside the image. */
|
||||
fillColor?: string
|
||||
/** Minimum zoom. Values below 1 shrink the image inside the crop frame. */
|
||||
minZoom?: number
|
||||
maxZoom?: number
|
||||
accept?: string
|
||||
uploadLabel?: string
|
||||
hint?: string
|
||||
@@ -24,6 +29,9 @@ export function ImageCropper({
|
||||
onChange,
|
||||
aspect = 1,
|
||||
outputFormat = 'jpeg',
|
||||
fillColor,
|
||||
minZoom = 1,
|
||||
maxZoom = 3,
|
||||
accept = 'image/*',
|
||||
uploadLabel,
|
||||
hint,
|
||||
@@ -34,6 +42,8 @@ export function ImageCropper({
|
||||
const resolvedUploadLabel = uploadLabel ?? t('products.form.thumbnailUpload')
|
||||
const resolvedHint = hint ?? t('products.form.thumbnailHint')
|
||||
const resolvedChangeLabel = changeLabel ?? t('products.form.thumbnailChange')
|
||||
const resolvedFillColor = fillColor ?? (outputFormat === 'jpeg' ? '#ffffff' : undefined)
|
||||
const cropSurfaceColor = resolvedFillColor ?? '#1e293b'
|
||||
const [imageSrc, setImageSrc] = useState<string | null>(null)
|
||||
const [crop, setCrop] = useState({ x: 0, y: 0 })
|
||||
const [zoom, setZoom] = useState(1)
|
||||
@@ -65,7 +75,12 @@ export function ImageCropper({
|
||||
|
||||
async function applyCrop() {
|
||||
if (!imageSrc || !croppedArea) return
|
||||
const cropped = await getCroppedImage(imageSrc, croppedArea, outputFormat)
|
||||
const cropped = await getCroppedImage(
|
||||
imageSrc,
|
||||
croppedArea,
|
||||
outputFormat,
|
||||
resolvedFillColor,
|
||||
)
|
||||
onChange(cropped)
|
||||
setImageSrc(null)
|
||||
setZoom(1)
|
||||
@@ -128,16 +143,25 @@ export function ImageCropper({
|
||||
|
||||
{imageSrc && (
|
||||
<div className={styles.cropPanel}>
|
||||
<div className={styles.cropArea} style={frameStyle}>
|
||||
<div
|
||||
className={styles.cropArea}
|
||||
style={{ ...frameStyle, background: cropSurfaceColor }}
|
||||
>
|
||||
<Cropper
|
||||
key={String(aspect)}
|
||||
image={imageSrc}
|
||||
crop={crop}
|
||||
zoom={zoom}
|
||||
minZoom={minZoom}
|
||||
maxZoom={maxZoom}
|
||||
aspect={aspect}
|
||||
onCropChange={setCrop}
|
||||
onZoomChange={setZoom}
|
||||
onCropComplete={onCropComplete}
|
||||
restrictPosition={minZoom >= 1}
|
||||
style={{
|
||||
containerStyle: { background: cropSurfaceColor },
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.cropControls}>
|
||||
@@ -145,8 +169,8 @@ export function ImageCropper({
|
||||
{t('products.form.zoom')}
|
||||
<input
|
||||
type="range"
|
||||
min={1}
|
||||
max={3}
|
||||
min={minZoom}
|
||||
max={maxZoom}
|
||||
step={0.05}
|
||||
value={zoom}
|
||||
onChange={(e) => setZoom(Number(e.target.value))}
|
||||
|
||||
Reference in New Issue
Block a user