import React, { useEffect, useRef, useState } from 'react'; import { __, _n, sprintf } from '@wordpress/i18n'; import { ArrowRight, Check, ChevronsLeftRight, FlaskConical } from 'lucide-react'; import type { CompressionCheckResult, CompressionCheckSample } from '../../api'; import { formatBytes, numberFormat, percentFormat } from '../../lib/i18n'; /** The extension of a file name, upper-cased, for the size line under the picture. */ function formatOf( name: string ): string { const dot = name.lastIndexOf( '.' ); return dot > -1 ? name.slice( dot + 1 ).toUpperCase() : ''; } /** * The user's own photos, before and after compression, side by side under one slider. * * Artefacts hide at fit-to-screen, so "View at 100%" shows a 1:1 crop: people who check the pixels * trust the number more. */ export default function BeforeAfterModal( { samples, result, applied, pro = false, onClose }: { samples: CompressionCheckSample[]; result: CompressionCheckResult; /** How many free settings were just switched on. */ applied: number; /** Pro is active: no upsell link. */ pro?: boolean; onClose: () => void; } ) { const [ index, setIndex ] = useState( 0 ); const [ position, setPosition ] = useState( 50 ); const [ actual, setActual ] = useState( false ); const stage = useRef< HTMLDivElement >( null ); const dialog = useRef< HTMLDivElement >( null ); const dragging = useRef( false ); const sample = samples[ index ] ?? samples[ 0 ]; useEffect( () => { dialog.current?.focus(); const onKey = ( event: KeyboardEvent ) => { if ( event.key === 'Escape' ) { onClose(); } }; document.addEventListener( 'keydown', onKey ); return () => document.removeEventListener( 'keydown', onKey ); }, [] ); const moveTo = ( clientX: number ) => { const box = stage.current?.getBoundingClientRect(); if ( box && box.width > 0 ) { setPosition( Math.max( 0, Math.min( 100, ( ( clientX - box.left ) / box.width ) * 100 ) ) ); } }; if ( ! sample ) { return null; } const fit = actual ? 'none' : 'contain'; return (
{ applied > 0 ? <>{ __( 'Free fixes applied.', 'image-sizes' ) } { sprintf( /* translators: %s: number of settings switched on. */ _n( '%s setting is on.', '%s settings are on.', applied, 'image-sizes' ), numberFormat( applied ), ) } : { __( 'Your free settings were already on.', 'image-sizes' ) } }

{ result.saved_bytes >= 1048576 ? sprintf( /* translators: %s: estimated size Pro would save, e.g. "2.1 GB". */ __( 'Here’s the ~%s, on your own photos', 'image-sizes' ), formatBytes( result.saved_bytes, true ), ) : __( 'Here’s the difference, on your own photos', 'image-sizes' ) }

{ sprintf( /* translators: %s: number of photos compressed. */ __( 'We compressed %s of your photos on copies. Nothing on your site changed. Drag the handle, or zoom to 100%% and look closely.', 'image-sizes' ), numberFormat( samples.length ), ) }
{ samples.map( ( item, i ) => ( ) ) }
{ dragging.current = true; ( event.currentTarget as HTMLElement ).setPointerCapture( event.pointerId ); moveTo( event.clientX ); } } onPointerMove={ ( event ) => { if ( dragging.current ) moveTo( event.clientX ); } } onPointerUp={ () => { dragging.current = false; } } > { { { __( 'Before', 'image-sizes' ) } { __( 'After', 'image-sizes' ) } { sample.name }
{ __( 'Before', 'image-sizes' ) } { formatBytes( sample.before ) } { formatOf( sample.name ) }
{ __( 'After', 'image-sizes' ) } ~{ formatBytes( sample.after, true ) } { formatOf( sample.name ) } −{ percentFormat( sample.saved_pct ) }
{ sprintf( /* translators: 1: number of photos tested, 2: percentage saved on average. */ __( 'Average across %1$s of your photos: −%2$s', 'image-sizes' ), numberFormat( result.samples_tested ), percentFormat( result.saved_pct ), ) } { ! pro && { __( 'How Pro works', 'image-sizes' ) } }
); }